diff --git a/src/pyvesync/base_devices/purifier_base.py b/src/pyvesync/base_devices/purifier_base.py index 2e8e5d98..f65a35f2 100644 --- a/src/pyvesync/base_devices/purifier_base.py +++ b/src/pyvesync/base_devices/purifier_base.py @@ -40,6 +40,8 @@ class PurifierState(DeviceState): fan_level (int): Current fan level of the purifier. fan_set_level (int): Set fan level of the purifier. filter_life (int): Filter life percentage of the purifier. + carbon_filter_life (int): Activated carbon filter life percentage of the + purifier, for models with a separately-tracked carbon filter. auto_preference_type (str): Auto preference type of the purifier. auto_room_size (int): Auto room size of the purifier. air_quality_level (AirQualityLevel): Air quality level of the purifier. @@ -71,6 +73,7 @@ class PurifierState(DeviceState): 'aq_percent', 'auto_preference_type', 'auto_room_size', + 'carbon_filter_life', 'child_lock', 'co2', 'display_forever', @@ -106,6 +109,7 @@ def __init__( self.fan_level: int | None = None self.fan_set_level: int | None = None self.filter_life: int | None = None + self.carbon_filter_life: int | None = None self.auto_preference_type: str | None = None self.auto_room_size: int | None = None self._air_quality_level: AirQualityLevel | None = None diff --git a/src/pyvesync/device_map.py b/src/pyvesync/device_map.py index 172b04c3..0acd39c3 100644 --- a/src/pyvesync/device_map.py +++ b/src/pyvesync/device_map.py @@ -996,6 +996,29 @@ class ThermostatMap(DeviceMapTemplate): model_name='Everest Air', setup_entry='EL551S', ), + PurifierMap( + class_name='VeSyncAirBaseV2', + dev_types=['LAP-C341S-WUSZ'], + modes=[ + PurifierModes.SLEEP, + PurifierModes.MANUAL, + PurifierModes.AUTO, + ], + features=[ + PurifierFeatures.AIR_QUALITY, + PurifierFeatures.LIGHT_DETECT, + ], + fan_levels=list(range(1, 5)), + device_alias='Eleva 300X', + auto_preferences=[ + PurifierAutoPreference.DEFAULT, + PurifierAutoPreference.EFFICIENT, + PurifierAutoPreference.QUIET, + ], + model_display='LAP-C341S Series', + model_name='Eleva 300X', + setup_entry='LAP-C341S', + ), PurifierMap( class_name='VeSyncAirSprout', dev_types=[ diff --git a/src/pyvesync/devices/vesyncpurifier.py b/src/pyvesync/devices/vesyncpurifier.py index ba3ba808..c756b9c4 100644 --- a/src/pyvesync/devices/vesyncpurifier.py +++ b/src/pyvesync/devices/vesyncpurifier.py @@ -460,6 +460,7 @@ def _set_state(self, details: InnerPurifierBaseResult) -> None: self.state.device_status = DeviceStatus.from_int(details.powerSwitch) self.state.mode = details.workMode self.state.filter_life = details.filterLifePercent + self.state.carbon_filter_life = details.carbonFilterLifePercent if details.fanSpeedLevel == 255: # noqa: PLR2004 self.state.fan_level = 0 else: diff --git a/src/pyvesync/models/purifier_models.py b/src/pyvesync/models/purifier_models.py index d510ef33..66d95413 100644 --- a/src/pyvesync/models/purifier_models.py +++ b/src/pyvesync/models/purifier_models.py @@ -54,7 +54,6 @@ class PurifierVitalDetailsResult(InnerPurifierBaseResult): environmentLightState: int scheduleCount: int timerRemain: int - efficientModeTimeRemain: int errorCode: int autoPreference: V2AutoPreferences | None = None fanRotateAngle: int | None = None @@ -62,6 +61,8 @@ class PurifierVitalDetailsResult(InnerPurifierBaseResult): PM1: int | None = None PM10: int | None = None AQPercent: int | None = None + carbonFilterLifePercent: int | None = None + efficientModeTimeRemain: int | None = None @dataclass @@ -69,7 +70,7 @@ class V2AutoPreferences: """Vital 100S/200S Auto Preferences.""" autoPreferenceType: str - roomSize: int + roomSize: int | None = None @dataclass diff --git a/src/tests/api/vesyncpurifier/LAP-C341S.yaml b/src/tests/api/vesyncpurifier/LAP-C341S.yaml new file mode 100644 index 00000000..b35085b3 --- /dev/null +++ b/src/tests/api/vesyncpurifier/LAP-C341S.yaml @@ -0,0 +1,248 @@ +set_auto_mode: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: + workMode: auto + method: setPurifierMode + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 +set_fan_speed: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: + levelIdx: 0 + levelType: wind + manualSpeedLevel: 3 + method: setLevel + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 +set_manual_mode: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: + levelIdx: 0 + levelType: wind + manualSpeedLevel: 1 + method: setLevel + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 +set_sleep_mode: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: + workMode: sleep + method: setPurifierMode + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 +turn_off: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: + powerSwitch: 0 + switchIdx: 0 + method: setSwitch + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 +turn_off_display: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: + screenSwitch: 0 + method: setDisplay + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 +turn_on: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: + powerSwitch: 1 + switchIdx: 0 + method: setSwitch + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 +turn_on_display: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: + screenSwitch: 1 + method: setDisplay + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 +update: + headers: + Content-Type: application/json; charset=UTF-8 + User-Agent: okhttp/3.12.1 + json_object: + acceptLanguage: en + accountID: sample_id + appVersion: 5.6.60 + cid: LAP-C341S-CID + configModel: ConfigModule + configModule: ConfigModule + debugMode: false + deviceId: LAP-C341S-CID + method: bypassV2 + payload: + data: {} + method: getPurifierStatus + source: APP + phoneBrand: pyvesync + phoneOS: Android + timeZone: America/New_York + token: sample_tk + traceId: TRACE_ID + userCountryCode: US + method: post + url: /cloud/v2/deviceManaged/bypassV2 diff --git a/src/tests/call_json_purifiers.py b/src/tests/call_json_purifiers.py index 79b02dcd..004a884d 100644 --- a/src/tests/call_json_purifiers.py +++ b/src/tests/call_json_purifiers.py @@ -59,6 +59,7 @@ class PurifierDefaults: fan_level = 1 fan_set_level = 2 filter_life = 80 + carbon_filter_life = 90 humidity = 50 night_light = DeviceStatus.ON display = DeviceStatus.ON @@ -257,6 +258,27 @@ class PurifierDefaults: "ecoModeRunTime": 0, "errorCode": 0, }, + "LAP-C341S": { # Eleva 300X + "powerSwitch": int(DeviceStatus.ON), + "filterLifePercent": PurifierDefaults.filter_life, + "carbonFilterLifePercent": PurifierDefaults.carbon_filter_life, + "workMode": PurifierDefaults.purifier_mode, + "manualSpeedLevel": PurifierDefaults.fan_set_level, + "fanSpeedLevel": PurifierDefaults.fan_level, + "AQLevel": PurifierDefaults.air_quality_enum.value, + "PM25": PurifierDefaults.air_quality_value_pm25, + "PM1": PurifierDefaults.pm1, + "PM10": PurifierDefaults.pm10, + "screenState": int(PurifierDefaults.display), + "childLockSwitch": int(PurifierDefaults.child_lock), + "screenSwitch": int(PurifierDefaults.display_config), + "lightDetectionSwitch": int(PurifierDefaults.light_detection), + "environmentLightState": int(PurifierDefaults.light_detected), + "autoPreference": {"autoPreferenceType": "default"}, + "scheduleCount": 0, + "timerRemain": 0, + "errorCode": 0, + }, "LAP-B851S-WUS": { "powerSwitch": TestDefaults.bin_toggle, "workMode": PurifierDefaults.purifier_mode, @@ -317,6 +339,7 @@ class PurifierDefaults: "LAP-V102S": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["LAP-V102S"]), "LAP-V201S": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["LAP-V201S"]), "EL551S": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["EL551S"]), + "LAP-C341S": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["LAP-C341S"]), "LAP-B851S-WUS": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["LAP-B851S-WUS"]), } @@ -340,6 +363,7 @@ class PurifierDefaults: "LAP-V102S": deepcopy(FunctionResponsesV2), "LAP-V201S": deepcopy(FunctionResponsesV2), "EL551S": deepcopy(FunctionResponsesV2), + "LAP-C341S": deepcopy(FunctionResponsesV2), "LAP-B851S-WUS": deepcopy(FunctionResponsesV2), } diff --git a/src/tests/test_purifiers.py b/src/tests/test_purifiers.py index edbd4ebd..482f988a 100644 --- a/src/tests/test_purifiers.py +++ b/src/tests/test_purifiers.py @@ -107,6 +107,7 @@ class TestAirPurifiers(TestBase): "LAP-V102S": [["set_auto_mode"], ['set_sleep_mode'], ['set_pet_mode'], ["turn_on_display"], ["turn_off_display"]], "LAP-V201S": [["set_auto_mode"], ['set_sleep_mode'], ['set_pet_mode'], ["turn_on_display"], ["turn_off_display"]], "EL551S": [["set_auto_mode"], ["set_sleep_mode"], ["set_turbo_mode"], ["turn_on_display"], ["turn_off_display"]], + "LAP-C341S": [["set_auto_mode"], ["set_sleep_mode"], ["turn_on_display"], ["turn_off_display"]], "LAP-B851S-WUS": [["set_auto_mode"], ['set_sleep_mode'], ["turn_on_display"], ["turn_off_display"]], }