Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ pip install pyvesync
7. Vital 100S / 200S
8. Everest Air
9. Sprout Air Purifier
10. Eleva 300X

### Etekcity / Valceno Bulbs

Expand Down
3 changes: 3 additions & 0 deletions src/pyvesync/base_devices/purifier_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ 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): Carbon filter life percentage of the purifier.
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.
Expand Down Expand Up @@ -71,6 +72,7 @@ class PurifierState(DeviceState):
'aq_percent',
'auto_preference_type',
'auto_room_size',
'carbon_filter_life',
'child_lock',
'co2',
'display_forever',
Expand Down Expand Up @@ -106,6 +108,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
Expand Down
20 changes: 20 additions & 0 deletions src/pyvesync/device_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,26 @@ class ThermostatMap(DeviceMapTemplate):
model_name='Sprout Air',
setup_entry='LAP-B851S-WUS',
),
PurifierMap(
class_name='VeSyncAirBaseV2',
dev_types=['LAP-C341S-WEU'],
modes=[
PurifierModes.SLEEP,
PurifierModes.MANUAL,
PurifierModes.AUTO,
],
features=[PurifierFeatures.AIR_QUALITY],
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-WEU',
),
]
"""List of ['PurifierMap'][pyvesync.device_map.PurifierMap] configuration
objects for purifier devices."""
Expand Down
1 change: 1 addition & 0 deletions src/pyvesync/devices/vesyncpurifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
23 changes: 12 additions & 11 deletions src/pyvesync/models/purifier_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,35 +41,36 @@ class PurifierVitalDetailsResult(InnerPurifierBaseResult):
"""Vital 100S/200S and Everest Purifier Result Model."""

powerSwitch: int
filterLifePercent: int
workMode: str
manualSpeedLevel: int
fanSpeedLevel: int
AQLevel: int
PM25: int
screenState: int
childLockSwitch: int
screenSwitch: int
lightDetectionSwitch: int
environmentLightState: int
scheduleCount: int
timerRemain: int
efficientModeTimeRemain: int
errorCode: int
filterLifePercent: int = 100
screenState: int = 0
childLockSwitch: int = 0
screenSwitch: int = 0
lightDetectionSwitch: int = 0
environmentLightState: int = 0
scheduleCount: int = 0
timerRemain: int = 0
efficientModeTimeRemain: int = 0
errorCode: int = 0
autoPreference: V2AutoPreferences | None = None
fanRotateAngle: int | None = None
filterOpenState: int | None = None
PM1: int | None = None
PM10: int | None = None
AQPercent: int | None = None
carbonFilterLifePercent: int | None = None


@dataclass
class V2AutoPreferences:
"""Vital 100S/200S Auto Preferences."""

autoPreferenceType: str
roomSize: int
roomSize: int | None = None


@dataclass
Expand Down
167 changes: 167 additions & 0 deletions src/tests/api/vesyncpurifier/LAP-C341S-WEU.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
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-WEU-CID
configModel: ConfigModule
configModule: ConfigModule
debugMode: false
deviceId: LAP-C341S-WEU-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-WEU-CID
configModel: ConfigModule
configModule: ConfigModule
debugMode: false
deviceId: LAP-C341S-WEU-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-WEU-CID
configModel: ConfigModule
configModule: ConfigModule
debugMode: false
deviceId: LAP-C341S-WEU-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-WEU-CID
configModel: ConfigModule
configModule: ConfigModule
debugMode: false
deviceId: LAP-C341S-WEU-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_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-WEU-CID
configModel: ConfigModule
configModule: ConfigModule
debugMode: false
deviceId: LAP-C341S-WEU-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
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-WEU-CID
configModel: ConfigModule
configModule: ConfigModule
debugMode: false
deviceId: LAP-C341S-WEU-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
21 changes: 21 additions & 0 deletions src/tests/call_json_purifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,25 @@ class PurifierDefaults:
"timerRemain": 0,
"efficientModeTimeRemain": 0,
"errorCode": 0,
},
"LAP-C341S-WEU": { # Eleva 300X
"powerSwitch": int(DeviceStatus.ON),
"filterLifePercent": PurifierDefaults.filter_life,
"workMode": PurifierDefaults.purifier_mode,
"manualSpeedLevel": PurifierDefaults.fan_set_level,
"fanSpeedLevel": PurifierDefaults.fan_level,
"AQLevel": int(PurifierDefaults.air_quality_enum),
"PM25": PurifierDefaults.air_quality_value_pm25,
"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,
"efficientModeTimeRemain": 0,
"errorCode": 0,
},
"LAP-V201S": { # Vital 200S
"powerSwitch": int(DeviceStatus.ON),
Expand Down Expand Up @@ -318,6 +337,7 @@ class PurifierDefaults:
"LAP-V201S": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["LAP-V201S"]),
"EL551S": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["EL551S"]),
"LAP-B851S-WUS": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["LAP-B851S-WUS"]),
"LAP-C341S-WEU": build_bypass_v2_response(inner_result=PURIFIER_DETAILS["LAP-C341S-WEU"]),
}

FunctionResponses.default_factory = lambda: (
Expand All @@ -341,6 +361,7 @@ class PurifierDefaults:
"LAP-V201S": deepcopy(FunctionResponsesV2),
"EL551S": deepcopy(FunctionResponsesV2),
"LAP-B851S-WUS": deepcopy(FunctionResponsesV2),
"LAP-C341S-WEU": deepcopy(FunctionResponsesV2),
}

# Add responses for methods with different responses than the default
Expand Down