pyvesync 3.0.0b1__tar.gz → 3.0.0b3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyvesync-3.0.0b1/src/pyvesync.egg-info → pyvesync-3.0.0b3}/PKG-INFO +1 -1
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/pyproject.toml +1 -1
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/fan_base.py +13 -22
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/humidifier_base.py +19 -22
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/outlet_base.py +7 -7
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/purifier_base.py +17 -22
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/switch_base.py +5 -5
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/vesyncbasedevice.py +5 -4
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/const.py +18 -9
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/vesynchumidifier.py +4 -4
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/vesyncpurifier.py +2 -4
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/base_models.py +4 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/fan_models.py +1 -1
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/home_models.py +8 -8
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/humidifier_models.py +2 -2
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/purifier_models.py +7 -8
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/vesync_models.py +146 -12
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/utils/errors.py +6 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/utils/logs.py +23 -9
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/vesync.py +201 -24
- pyvesync-3.0.0b3/src/pyvesync/vesynchome.py +162 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3/src/pyvesync.egg-info}/PKG-INFO +1 -1
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync.egg-info/SOURCES.txt +1 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/LICENSE +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/MANIFEST.in +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/README.md +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/requirements.txt +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/setup.cfg +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/setup.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/__init__.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/__init__.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/bulb_base.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/fryer_base.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/base_devices/thermostat_base.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/device_container.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/device_map.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/__init__.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/vesyncbulb.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/vesyncfan.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/vesynckitchen.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/vesyncoutlet.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/vesyncswitch.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/devices/vesyncthermostat.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/__init__.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/bulb_models.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/bypass_models.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/fryer_models.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/outlet_models.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/switch_models.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/models/thermostat_models.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/utils/__init__.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/utils/colors.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/utils/device_mixins.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/utils/enum_utils.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync/utils/helpers.py +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync.egg-info/dependency_links.txt +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync.egg-info/requires.txt +0 -0
- {pyvesync-3.0.0b1 → pyvesync-3.0.0b3}/src/pyvesync.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyvesync
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0b3
|
|
4
4
|
Summary: pyvesync is a library to manage Etekcity Devices, Cosori Air Fryers, and Levoit Air Purifiers run on the VeSync app.
|
|
5
5
|
Author: Mark Perdue
|
|
6
6
|
Author-email: Joe Trabulsy <webdjoe@gmail.com>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyvesync"
|
|
7
|
-
version = "3.0.
|
|
7
|
+
version = "3.0.0b3"
|
|
8
8
|
description = "pyvesync is a library to manage Etekcity Devices, Cosori Air Fryers, and Levoit Air Purifiers run on the VeSync app."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -6,13 +6,7 @@ from abc import abstractmethod
|
|
|
6
6
|
from typing_extensions import deprecated
|
|
7
7
|
|
|
8
8
|
from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseToggleDevice, DeviceState
|
|
9
|
-
from pyvesync.const import
|
|
10
|
-
FanModes,
|
|
11
|
-
FanFeatures,
|
|
12
|
-
DeviceStatus,
|
|
13
|
-
FanSleepPreference,
|
|
14
|
-
StrFlag,
|
|
15
|
-
)
|
|
9
|
+
from pyvesync.const import FanModes, FanFeatures
|
|
16
10
|
|
|
17
11
|
if TYPE_CHECKING:
|
|
18
12
|
from pyvesync import VeSync
|
|
@@ -26,10 +20,7 @@ logger = logging.getLogger(__name__)
|
|
|
26
20
|
class FanState(DeviceState):
|
|
27
21
|
"""Base state class for Purifiers.
|
|
28
22
|
|
|
29
|
-
Not all attributes are supported by all devices.
|
|
30
|
-
UNKNOWN by default and attributes that are not supported by all devices are
|
|
31
|
-
set to [StrFlag.NOT_SUPPORTED][pyvesync.const.StrFlag] or
|
|
32
|
-
[IntFlag.NOT_SUPPORTED][pyvesync.const.IntFlag].
|
|
23
|
+
Not all attributes are supported by all devices.
|
|
33
24
|
|
|
34
25
|
|
|
35
26
|
Attributes:
|
|
@@ -93,17 +84,17 @@ class FanState(DeviceState):
|
|
|
93
84
|
self.humidity: int | None = None
|
|
94
85
|
self.temperature: int | None = None
|
|
95
86
|
self.thermal_comfort: int | None = None
|
|
96
|
-
self.sleep_preference_type: str | None =
|
|
97
|
-
self.sleep_fallasleep_remain: str | None =
|
|
98
|
-
self.sleep_oscillation_switch: str | None =
|
|
99
|
-
self.sleep_change_fan_level: str | None =
|
|
100
|
-
self.mute_status: str =
|
|
101
|
-
self.mute_set_state: str =
|
|
102
|
-
self.oscillation_status: str =
|
|
103
|
-
self.oscillation_set_state: str =
|
|
104
|
-
self.display_status: str =
|
|
105
|
-
self.display_set_state: str =
|
|
106
|
-
self.displaying_type: str =
|
|
87
|
+
self.sleep_preference_type: str | None = None
|
|
88
|
+
self.sleep_fallasleep_remain: str | None = None
|
|
89
|
+
self.sleep_oscillation_switch: str | None = None
|
|
90
|
+
self.sleep_change_fan_level: str | None = None
|
|
91
|
+
self.mute_status: str | None = None
|
|
92
|
+
self.mute_set_state: str | None = None
|
|
93
|
+
self.oscillation_status: str | None = None
|
|
94
|
+
self.oscillation_set_state: str | None = None
|
|
95
|
+
self.display_status: str | None = None
|
|
96
|
+
self.display_set_state: str | None = None
|
|
97
|
+
self.displaying_type: str | None = None
|
|
107
98
|
|
|
108
99
|
|
|
109
100
|
class VeSyncFanBase(VeSyncBaseToggleDevice):
|
|
@@ -9,9 +9,7 @@ from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseToggleDevice, Devic
|
|
|
9
9
|
from pyvesync.const import (
|
|
10
10
|
HumidifierFeatures,
|
|
11
11
|
HumidifierModes,
|
|
12
|
-
DeviceStatus
|
|
13
|
-
IntFlag,
|
|
14
|
-
StrFlag
|
|
12
|
+
DeviceStatus
|
|
15
13
|
)
|
|
16
14
|
|
|
17
15
|
if TYPE_CHECKING:
|
|
@@ -27,8 +25,7 @@ class HumidifierState(DeviceState):
|
|
|
27
25
|
"""State Class for VeSync Humidifiers.
|
|
28
26
|
|
|
29
27
|
This is the state class for all vesync humidifiers. If there are
|
|
30
|
-
new features or state attributes
|
|
31
|
-
this class and be set to the default `NOT_SUPPORTED` flag.
|
|
28
|
+
new features or state attributes.
|
|
32
29
|
|
|
33
30
|
Attributes:
|
|
34
31
|
auto_preference (int): Auto preference level.
|
|
@@ -97,29 +94,29 @@ class HumidifierState(DeviceState):
|
|
|
97
94
|
"""
|
|
98
95
|
super().__init__(device, details, feature_map)
|
|
99
96
|
self.auto_stop_target_reached: bool = False
|
|
100
|
-
self.auto_target_humidity: int =
|
|
97
|
+
self.auto_target_humidity: int | None = None
|
|
101
98
|
self.automatic_stop_config: bool = False
|
|
102
99
|
self.display_set_status: str = DeviceStatus.UNKNOWN
|
|
103
100
|
self.display_status: str = DeviceStatus.UNKNOWN
|
|
104
|
-
self.humidity: int =
|
|
101
|
+
self.humidity: int | None = None
|
|
105
102
|
self.humidity_high: bool = False
|
|
106
103
|
self.mist_level: int | None = None
|
|
107
104
|
self.mist_virtual_level: int | None = None
|
|
108
|
-
self.mode: str =
|
|
109
|
-
self.nightlight_brightness: int =
|
|
110
|
-
self.nightlight_status: str =
|
|
105
|
+
self.mode: str | None = None
|
|
106
|
+
self.nightlight_brightness: int | None = None
|
|
107
|
+
self.nightlight_status: str | None = None
|
|
111
108
|
self.warm_mist_enabled: bool | None = None
|
|
112
|
-
self.warm_mist_level: int =
|
|
109
|
+
self.warm_mist_level: int | None = None
|
|
113
110
|
self.water_lacks: bool = False
|
|
114
111
|
self.water_tank_lifted: bool = False
|
|
115
|
-
self.temperature: int =
|
|
112
|
+
self.temperature: int | None = None
|
|
116
113
|
# Superior 6000S States
|
|
117
|
-
self.auto_preference: int =
|
|
118
|
-
self.filter_life_percent: int =
|
|
119
|
-
self.drying_mode_level: int =
|
|
120
|
-
self.drying_mode_auto_switch: str =
|
|
121
|
-
self.drying_mode_status: str =
|
|
122
|
-
self.drying_mode_time_remain: int =
|
|
114
|
+
self.auto_preference: int | None = None
|
|
115
|
+
self.filter_life_percent: int | None = None
|
|
116
|
+
self.drying_mode_level: int | None = None
|
|
117
|
+
self.drying_mode_auto_switch: str | None = None
|
|
118
|
+
self.drying_mode_status: str | None = None
|
|
119
|
+
self.drying_mode_time_remain: int | None = None
|
|
123
120
|
|
|
124
121
|
@property
|
|
125
122
|
def automatic_stop(self) -> bool:
|
|
@@ -141,7 +138,7 @@ class HumidifierState(DeviceState):
|
|
|
141
138
|
return self.auto_stop_target_reached
|
|
142
139
|
|
|
143
140
|
@property
|
|
144
|
-
def target_humidity(self) -> int:
|
|
141
|
+
def target_humidity(self) -> int | None:
|
|
145
142
|
"""Return the target humidity level.
|
|
146
143
|
|
|
147
144
|
Returns:
|
|
@@ -150,7 +147,7 @@ class HumidifierState(DeviceState):
|
|
|
150
147
|
return self.auto_target_humidity
|
|
151
148
|
|
|
152
149
|
@property
|
|
153
|
-
def auto_humidity(self) -> int:
|
|
150
|
+
def auto_humidity(self) -> int | None:
|
|
154
151
|
"""Return the auto humidity level.
|
|
155
152
|
|
|
156
153
|
Returns:
|
|
@@ -174,7 +171,7 @@ class HumidifierState(DeviceState):
|
|
|
174
171
|
return self.humidity
|
|
175
172
|
|
|
176
173
|
@property
|
|
177
|
-
def drying_mode_state(self) -> str:
|
|
174
|
+
def drying_mode_state(self) -> str | None:
|
|
178
175
|
"""Return the drying mode state.
|
|
179
176
|
|
|
180
177
|
Returns:
|
|
@@ -183,7 +180,7 @@ class HumidifierState(DeviceState):
|
|
|
183
180
|
return self.drying_mode_status
|
|
184
181
|
|
|
185
182
|
@property
|
|
186
|
-
def drying_mode_seconds_remaining(self) -> int:
|
|
183
|
+
def drying_mode_seconds_remaining(self) -> int | None:
|
|
187
184
|
"""Return the drying mode seconds remaining.
|
|
188
185
|
|
|
189
186
|
Return:
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
|
-
from pyvesync.const import OutletFeatures,
|
|
6
|
+
from pyvesync.const import OutletFeatures, NightlightModes
|
|
7
7
|
from pyvesync.utils.helpers import Helpers
|
|
8
8
|
from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseToggleDevice, DeviceState
|
|
9
9
|
from pyvesync.models.outlet_models import RequestEnergyHistory, ResponseEnergyHistory
|
|
@@ -89,12 +89,12 @@ class OutletState(DeviceState):
|
|
|
89
89
|
self.device: VeSyncOutlet = device
|
|
90
90
|
self.features: list[str] = feature_map.features
|
|
91
91
|
self.active_time: int | None = 0
|
|
92
|
-
self.power: float =
|
|
93
|
-
self.energy: float =
|
|
94
|
-
self.voltage: float =
|
|
95
|
-
self.nightlight_status: str | None =
|
|
96
|
-
self.nightlight_brightness: int | None =
|
|
97
|
-
self.nightlight_automode: str | None =
|
|
92
|
+
self.power: float | None = None
|
|
93
|
+
self.energy: float | None = None
|
|
94
|
+
self.voltage: float | None = None
|
|
95
|
+
self.nightlight_status: str | None = None
|
|
96
|
+
self.nightlight_brightness: int | None = None
|
|
97
|
+
self.nightlight_automode: str | None = None
|
|
98
98
|
self.weekly_history: ResponseEnergyResult | None = None
|
|
99
99
|
self.monthly_history: ResponseEnergyResult | None = None
|
|
100
100
|
self.yearly_history: ResponseEnergyResult | None = None
|
|
@@ -12,10 +12,7 @@ from pyvesync.const import (
|
|
|
12
12
|
PurifierFeatures,
|
|
13
13
|
PurifierModes,
|
|
14
14
|
DeviceStatus,
|
|
15
|
-
NightlightModes
|
|
16
|
-
PurifierAutoPreference,
|
|
17
|
-
IntFlag,
|
|
18
|
-
StrFlag,
|
|
15
|
+
NightlightModes
|
|
19
16
|
)
|
|
20
17
|
|
|
21
18
|
if TYPE_CHECKING:
|
|
@@ -97,25 +94,23 @@ class PurifierState(DeviceState):
|
|
|
97
94
|
self.fan_level: int | None = None
|
|
98
95
|
self.fan_set_level: int | None = None
|
|
99
96
|
self.filter_life: int | None = None
|
|
100
|
-
self.auto_preference_type: str | None =
|
|
97
|
+
self.auto_preference_type: str | None = None
|
|
101
98
|
self.auto_room_size: int | None = None
|
|
102
|
-
self._air_quality_level: AirQualityLevel =
|
|
99
|
+
self._air_quality_level: AirQualityLevel | None = None
|
|
103
100
|
self.child_lock: bool = False
|
|
104
101
|
self.filter_open_state: bool = False
|
|
105
|
-
self.display_status: str =
|
|
106
|
-
self.display_set_state: str =
|
|
102
|
+
self.display_status: str | None = None
|
|
103
|
+
self.display_set_state: str | None = None
|
|
107
104
|
self.display_forever: bool = False
|
|
108
105
|
# Attributes not supported by all purifiers
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
self.
|
|
112
|
-
self.
|
|
113
|
-
self.
|
|
114
|
-
self.
|
|
115
|
-
self.
|
|
116
|
-
self.
|
|
117
|
-
self.nightlight_status: str = StrFlag.NOT_SUPPORTED
|
|
118
|
-
self.fan_rotate_angle: int | None = IntFlag.NOT_SUPPORTED
|
|
106
|
+
self.pm25: int | None = None
|
|
107
|
+
self.pm1: int | None = None
|
|
108
|
+
self.pm10: int | None = None
|
|
109
|
+
self.aq_percent: int | None = None
|
|
110
|
+
self.light_detection_switch: str | None = None
|
|
111
|
+
self.light_detection_status: str | None = None
|
|
112
|
+
self.nightlight_status: str | None = None
|
|
113
|
+
self.fan_rotate_angle: int | None = None
|
|
119
114
|
|
|
120
115
|
@property
|
|
121
116
|
def air_quality_level(self) -> int:
|
|
@@ -171,19 +166,19 @@ class PurifierState(DeviceState):
|
|
|
171
166
|
|
|
172
167
|
@property
|
|
173
168
|
@deprecated("Use PurifierState.nightlight_status instead.")
|
|
174
|
-
def night_light(self) -> str:
|
|
169
|
+
def night_light(self) -> str | None:
|
|
175
170
|
"""Return night light status."""
|
|
176
171
|
return self.nightlight_status
|
|
177
172
|
|
|
178
173
|
@property
|
|
179
174
|
@deprecated("Use display_status instead.")
|
|
180
|
-
def display_state(self) -> str:
|
|
175
|
+
def display_state(self) -> str | None:
|
|
181
176
|
"""Return display status."""
|
|
182
177
|
return self.display_status
|
|
183
178
|
|
|
184
179
|
@property
|
|
185
180
|
@deprecated("Use display_set_state instead.")
|
|
186
|
-
def display_switch(self) -> str:
|
|
181
|
+
def display_switch(self) -> str | None:
|
|
187
182
|
"""Return display switch status."""
|
|
188
183
|
return self.display_set_state
|
|
189
184
|
|
|
@@ -499,7 +494,7 @@ class VeSyncPurifier(VeSyncBaseToggleDevice):
|
|
|
499
494
|
|
|
500
495
|
@property
|
|
501
496
|
@deprecated("Use self.state.nightlight_status instead.")
|
|
502
|
-
def night_light(self) -> str:
|
|
497
|
+
def night_light(self) -> str | None:
|
|
503
498
|
"""Get night light state.
|
|
504
499
|
|
|
505
500
|
Returns:
|
|
@@ -7,7 +7,7 @@ from typing_extensions import deprecated
|
|
|
7
7
|
from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseToggleDevice, DeviceState
|
|
8
8
|
|
|
9
9
|
from pyvesync.utils.colors import Color
|
|
10
|
-
from pyvesync.const import SwitchFeatures
|
|
10
|
+
from pyvesync.const import SwitchFeatures
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from pyvesync import VeSync
|
|
@@ -57,11 +57,11 @@ class SwitchState(DeviceState):
|
|
|
57
57
|
"""Initialize VeSync Switch State."""
|
|
58
58
|
super().__init__(device, details, feature_map)
|
|
59
59
|
self.device: VeSyncSwitch = device
|
|
60
|
-
self._backlight_color: Color |
|
|
61
|
-
self.brightness: int =
|
|
60
|
+
self._backlight_color: Color | None = None
|
|
61
|
+
self.brightness: int | None = None
|
|
62
62
|
self.active_time: int | None = 0
|
|
63
|
-
self.backlight_status: str =
|
|
64
|
-
self.indicator_status: str =
|
|
63
|
+
self.backlight_status: str | None = None
|
|
64
|
+
self.indicator_status: str | None = None
|
|
65
65
|
|
|
66
66
|
@property
|
|
67
67
|
def backlight_rgb(self) -> RGB | None:
|
|
@@ -10,7 +10,7 @@ from typing_extensions import deprecated
|
|
|
10
10
|
# import warnings
|
|
11
11
|
import orjson
|
|
12
12
|
|
|
13
|
-
from pyvesync.const import DeviceStatus, ConnectionStatus
|
|
13
|
+
from pyvesync.const import DeviceStatus, ConnectionStatus
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
@@ -53,6 +53,7 @@ class VeSyncBaseDevice(ABC, Generic[VS_STATE_T]):
|
|
|
53
53
|
config_module (str): Configuration module of device.
|
|
54
54
|
mac_id (str): MAC ID of device.
|
|
55
55
|
current_firm_version (str): Current firmware version of device.
|
|
56
|
+
latest_firm_version (str | None): Latest firmware version of device.
|
|
56
57
|
device_region (str): Region of device. (US, EU, etc.)
|
|
57
58
|
pid (str): Product ID of device, pulled by some devices on update.
|
|
58
59
|
sub_device_no (int): Sub-device number of device.
|
|
@@ -96,6 +97,7 @@ class VeSyncBaseDevice(ABC, Generic[VS_STATE_T]):
|
|
|
96
97
|
"enabled",
|
|
97
98
|
"features",
|
|
98
99
|
"last_response",
|
|
100
|
+
"latest_firm_version",
|
|
99
101
|
"mac_id",
|
|
100
102
|
"manager",
|
|
101
103
|
"pid",
|
|
@@ -129,6 +131,7 @@ class VeSyncBaseDevice(ABC, Generic[VS_STATE_T]):
|
|
|
129
131
|
self.config_module: str = details.configModule
|
|
130
132
|
self.mac_id: str | None = details.macID
|
|
131
133
|
self.current_firm_version = details.currentFirmVersion
|
|
134
|
+
self.latest_firm_version: str | None = None
|
|
132
135
|
self.device_region: str | None = details.deviceRegion
|
|
133
136
|
self.pid: str | None = None
|
|
134
137
|
self.sub_device_no: int | None = details.subDeviceNo
|
|
@@ -192,7 +195,7 @@ class VeSyncBaseDevice(ABC, Generic[VS_STATE_T]):
|
|
|
192
195
|
|
|
193
196
|
This is going to be updated.
|
|
194
197
|
"""
|
|
195
|
-
return
|
|
198
|
+
return self.latest_firm_version != self.current_firm_version
|
|
196
199
|
|
|
197
200
|
async def set_timer(self, duration: int, action: str | None = None) -> bool:
|
|
198
201
|
"""Set timer for device.
|
|
@@ -542,8 +545,6 @@ class DeviceState:
|
|
|
542
545
|
or attr_name in [*self.__base_exclusions, *self._exclude_serialization]
|
|
543
546
|
):
|
|
544
547
|
continue
|
|
545
|
-
if attr_value in [IntFlag.NOT_SUPPORTED, StrFlag.NOT_SUPPORTED]:
|
|
546
|
-
continue
|
|
547
548
|
state_dict[attr_name] = attr_value
|
|
548
549
|
return state_dict
|
|
549
550
|
|
|
@@ -4,7 +4,8 @@ All device states and information are defined by Enums in this module.
|
|
|
4
4
|
|
|
5
5
|
Attributes:
|
|
6
6
|
DEFAULT_LANGUAGE (str): Default language for the VeSync app.
|
|
7
|
-
API_BASE_URL (str): Base URL for the VeSync API.
|
|
7
|
+
API_BASE_URL (str): Base URL for the VeSync API. If not specified,
|
|
8
|
+
a region-specific API URL is automatically selected.
|
|
8
9
|
API_TIMEOUT (int): Timeout for API requests.
|
|
9
10
|
USER_AGENT (str): User agent for API requests.
|
|
10
11
|
DEFAULT_TZ (str): Default timezone for VeSync devices, updated by API after
|
|
@@ -12,6 +13,8 @@ Attributes:
|
|
|
12
13
|
DEFAULT_REGION (str): Default region for VeSync devices,
|
|
13
14
|
updated by API when retrieving devices.
|
|
14
15
|
APP_VERSION (str): Version of the VeSync app.
|
|
16
|
+
APP_ID (str): ID of the app. VeSync uses a random 8-letter string, but any non-empty
|
|
17
|
+
string works.
|
|
15
18
|
PHONE_BRAND (str): Brand of the phone used to login to the VeSync app.
|
|
16
19
|
PHONE_OS (str): Operating system of the phone used to login to the VeSync app.
|
|
17
20
|
MOBILE_ID (str): Unique identifier for the phone used to login to the VeSync app.
|
|
@@ -20,31 +23,37 @@ Attributes:
|
|
|
20
23
|
BYPASS_HEADER_UA (str): Bypass header user agent
|
|
21
24
|
TERMINAL_ID (str): Unique identifier for new API calls
|
|
22
25
|
"""
|
|
23
|
-
from random import randint
|
|
24
|
-
from uuid import
|
|
26
|
+
from random import randint, choices
|
|
27
|
+
from uuid import uuid4
|
|
28
|
+
import string
|
|
25
29
|
from enum import StrEnum, IntEnum, Enum
|
|
26
30
|
from types import MappingProxyType
|
|
27
31
|
from pyvesync.utils.enum_utils import IntEnumMixin
|
|
28
32
|
|
|
29
33
|
DEFAULT_LANGUAGE = "en"
|
|
30
|
-
API_BASE_URL = "https://smartapi.vesync.com"
|
|
34
|
+
API_BASE_URL = None # Global URL (non-EU regions): "https://smartapi.vesync.com"
|
|
31
35
|
# If device is out of reach, the cloud api sends a timeout response after 7 seconds,
|
|
32
36
|
# using 8 here so there is time enough to catch that message
|
|
37
|
+
API_BASE_URL_US = "https://smartapi.vesync.com"
|
|
38
|
+
API_BASE_URL_EU = "https://smartapi.vesync.eu"
|
|
39
|
+
NON_EU_REGIONS = ['US', 'CA', 'MX', 'JP']
|
|
33
40
|
API_TIMEOUT = 8
|
|
34
41
|
USER_AGENT = (
|
|
35
42
|
"VeSync/3.2.39 (com.etekcity.vesyncPlatform; build:5; iOS 15.5.0) Alamofire/5.2.1"
|
|
36
43
|
)
|
|
37
44
|
DEFAULT_TZ = "America/New_York"
|
|
38
45
|
DEFAULT_REGION = "US"
|
|
39
|
-
APP_VERSION = "
|
|
46
|
+
APP_VERSION = "5.6.60"
|
|
47
|
+
APP_ID = ''.join(choices(string.ascii_lowercase + string.digits, k=8)) # noqa: S311
|
|
40
48
|
PHONE_BRAND = "SM N9005"
|
|
41
49
|
PHONE_OS = "Android"
|
|
42
50
|
# MOBILE_ID = "1234567890123456"
|
|
43
51
|
MOBILE_ID = str(randint(1000000000000000, 9999999999999999)) # noqa: S311
|
|
44
52
|
USER_TYPE = "1"
|
|
45
|
-
BYPASS_APP_V = "VeSync
|
|
53
|
+
BYPASS_APP_V = f"VeSync {APP_VERSION}"
|
|
46
54
|
BYPASS_HEADER_UA = "okhttp/3.12.1"
|
|
47
|
-
TERMINAL_ID = '2' +
|
|
55
|
+
TERMINAL_ID = '2' + str(uuid4()).replace('-', '')
|
|
56
|
+
CLIENT_TYPE = "vesyncApp"
|
|
48
57
|
|
|
49
58
|
|
|
50
59
|
# Generic Constants
|
|
@@ -64,7 +73,7 @@ class ProductTypes(StrEnum):
|
|
|
64
73
|
|
|
65
74
|
|
|
66
75
|
class IntFlag(IntEnum):
|
|
67
|
-
"""Integer flag to indicate if a device is not supported.
|
|
76
|
+
"""DEPRECATED. Integer flag to indicate if a device is not supported.
|
|
68
77
|
|
|
69
78
|
This is used by data models as a default value for feature attributes
|
|
70
79
|
that are not supported by all devices.
|
|
@@ -82,7 +91,7 @@ class IntFlag(IntEnum):
|
|
|
82
91
|
|
|
83
92
|
|
|
84
93
|
class StrFlag(StrEnum):
|
|
85
|
-
"""String flag to indicate if a device is not supported.
|
|
94
|
+
"""DEPRECATED. String flag to indicate if a device is not supported.
|
|
86
95
|
|
|
87
96
|
This is used by data models as a default value for feature attributes
|
|
88
97
|
that are not supported by all devices.
|
|
@@ -8,7 +8,7 @@ import orjson
|
|
|
8
8
|
from pyvesync.base_devices import VeSyncHumidifier
|
|
9
9
|
from pyvesync.utils.helpers import Helpers, Validators, Timer
|
|
10
10
|
from pyvesync.utils.device_mixins import BypassV2Mixin, process_bypassv2_result
|
|
11
|
-
from pyvesync.const import DeviceStatus,
|
|
11
|
+
from pyvesync.const import DeviceStatus, ConnectionStatus
|
|
12
12
|
from pyvesync.models.bypass_models import ResultV2GetTimer, ResultV2SetTimer
|
|
13
13
|
from pyvesync.models.humidifier_models import (
|
|
14
14
|
ClassicLVHumidResult,
|
|
@@ -75,7 +75,7 @@ class VeSyncHumid200300S(BypassV2Mixin, VeSyncHumidifier):
|
|
|
75
75
|
self.state.connection_status = ConnectionStatus.ONLINE
|
|
76
76
|
self.state.device_status = DeviceStatus.from_bool(resp_model.enabled)
|
|
77
77
|
self.state.mode = resp_model.mode
|
|
78
|
-
self.state.humidity = resp_model.humidity
|
|
78
|
+
self.state.humidity = resp_model.humidity
|
|
79
79
|
self.state.mist_virtual_level = resp_model.mist_virtual_level or 0
|
|
80
80
|
self.state.mist_level = resp_model.mist_level or 0
|
|
81
81
|
self.state.water_lacks = resp_model.water_lacks
|
|
@@ -84,7 +84,7 @@ class VeSyncHumid200300S(BypassV2Mixin, VeSyncHumidifier):
|
|
|
84
84
|
self.state.auto_stop_target_reached = resp_model.automatic_stop_reach_target
|
|
85
85
|
if (
|
|
86
86
|
self.supports_nightlight
|
|
87
|
-
and resp_model.night_light_brightness
|
|
87
|
+
and resp_model.night_light_brightness is not None
|
|
88
88
|
):
|
|
89
89
|
self.state.nightlight_brightness = resp_model.night_light_brightness
|
|
90
90
|
self.state.nightlight_status = (
|
|
@@ -93,7 +93,7 @@ class VeSyncHumid200300S(BypassV2Mixin, VeSyncHumidifier):
|
|
|
93
93
|
else DeviceStatus.OFF
|
|
94
94
|
)
|
|
95
95
|
self.state.display_status = DeviceStatus.from_bool(resp_model.display)
|
|
96
|
-
if self.supports_warm_mist and resp_model.warm_level
|
|
96
|
+
if self.supports_warm_mist and resp_model.warm_level is not None:
|
|
97
97
|
self.state.warm_mist_level = resp_model.warm_level
|
|
98
98
|
self.state.warm_mist_enabled = resp_model.warm_enabled
|
|
99
99
|
config = resp_model.configuration
|
|
@@ -14,8 +14,6 @@ from pyvesync.utils.device_mixins import (
|
|
|
14
14
|
from pyvesync.utils.helpers import Helpers, Timer
|
|
15
15
|
# from pyvesync.models.base_models import DefaultValues
|
|
16
16
|
from pyvesync.const import (
|
|
17
|
-
IntFlag,
|
|
18
|
-
StrFlag,
|
|
19
17
|
PurifierAutoPreference,
|
|
20
18
|
DeviceStatus,
|
|
21
19
|
ConnectionStatus,
|
|
@@ -135,7 +133,7 @@ class VeSyncAirBypass(BypassV2Mixin, VeSyncPurifier):
|
|
|
135
133
|
if self.supports_air_quality is True:
|
|
136
134
|
self.state.pm25 = result.air_quality_value
|
|
137
135
|
self.state.set_air_quality_level(result.air_quality)
|
|
138
|
-
if result.night_light
|
|
136
|
+
if result.night_light is not None:
|
|
139
137
|
self.state.nightlight_status = DeviceStatus.ON if result.night_light \
|
|
140
138
|
else DeviceStatus.OFF
|
|
141
139
|
|
|
@@ -490,7 +488,7 @@ class VeSyncAirBaseV2(VeSyncAirBypass):
|
|
|
490
488
|
self.state.pm10 = details.PM10
|
|
491
489
|
self.state.aq_percent = details.AQPercent
|
|
492
490
|
self.state.fan_rotate_angle = details.fanRotateAngle
|
|
493
|
-
if details.filterOpenState
|
|
491
|
+
if details.filterOpenState is not None:
|
|
494
492
|
self.state.filter_open_state = bool(details.filterOpenState)
|
|
495
493
|
if details.timerRemain > 0:
|
|
496
494
|
self.state.timer = Timer(details.timerRemain, 'off')
|
|
@@ -27,6 +27,7 @@ from mashumaro.config import BaseConfig
|
|
|
27
27
|
from pyvesync.const import (
|
|
28
28
|
USER_TYPE,
|
|
29
29
|
APP_VERSION,
|
|
30
|
+
APP_ID,
|
|
30
31
|
PHONE_BRAND,
|
|
31
32
|
PHONE_OS,
|
|
32
33
|
MOBILE_ID,
|
|
@@ -35,6 +36,7 @@ from pyvesync.const import (
|
|
|
35
36
|
DEFAULT_TZ,
|
|
36
37
|
BYPASS_HEADER_UA,
|
|
37
38
|
TERMINAL_ID,
|
|
39
|
+
CLIENT_TYPE
|
|
38
40
|
)
|
|
39
41
|
|
|
40
42
|
|
|
@@ -86,6 +88,8 @@ class DefaultValues:
|
|
|
86
88
|
_call_number: ClassVar[int] = 0
|
|
87
89
|
userType: str = USER_TYPE
|
|
88
90
|
appVersion: str = APP_VERSION
|
|
91
|
+
clientType: str = CLIENT_TYPE
|
|
92
|
+
appId: str = APP_ID
|
|
89
93
|
phoneBrand: str = PHONE_BRAND
|
|
90
94
|
phoneOS: str = PHONE_OS
|
|
91
95
|
mobileId: str = MOBILE_ID
|
|
@@ -20,7 +20,6 @@ from dataclasses import dataclass, field
|
|
|
20
20
|
|
|
21
21
|
from pyvesync.models.base_models import (
|
|
22
22
|
ResponseCodeModel,
|
|
23
|
-
ResponseBaseModel,
|
|
24
23
|
RequestBaseModel,
|
|
25
24
|
DefaultValues,
|
|
26
25
|
)
|
|
@@ -36,9 +35,9 @@ class RequestHomeModel(RequestBaseModel):
|
|
|
36
35
|
"""
|
|
37
36
|
# Arguments set by Manager Instance by passing kw_argument manager
|
|
38
37
|
# these fields should be set to init=False
|
|
39
|
-
accountID: str
|
|
40
|
-
token: str
|
|
41
|
-
userCountryCode: str
|
|
38
|
+
accountID: str
|
|
39
|
+
token: str
|
|
40
|
+
userCountryCode: str
|
|
42
41
|
# Non-default constants
|
|
43
42
|
method: str = 'getHomeList'
|
|
44
43
|
# default values
|
|
@@ -52,17 +51,17 @@ class RequestHomeModel(RequestBaseModel):
|
|
|
52
51
|
|
|
53
52
|
|
|
54
53
|
@dataclass
|
|
55
|
-
class ResponseHomeModel(
|
|
54
|
+
class ResponseHomeModel(ResponseCodeModel):
|
|
56
55
|
"""Model for the home data response.
|
|
57
56
|
|
|
58
|
-
Inherits from `
|
|
57
|
+
Inherits from `ResponseCodeModel`. The `ResponseCodeModel` class provides the
|
|
59
58
|
`code` and `msg` fields. The `ResponseHomeModel` class provides the `result`
|
|
60
59
|
field containing the home data.
|
|
61
60
|
|
|
62
61
|
Attributes:
|
|
63
62
|
result: dict: The home data.
|
|
64
63
|
"""
|
|
65
|
-
result: IntResponseHomeResultModel
|
|
64
|
+
result: IntResponseHomeResultModel | IntResponseHomeInfoResultModel | None
|
|
66
65
|
|
|
67
66
|
|
|
68
67
|
@dataclass
|
|
@@ -154,5 +153,6 @@ class IntResponseHomeResultModel:
|
|
|
154
153
|
@dataclass
|
|
155
154
|
class IntResponseHomeListModel:
|
|
156
155
|
"""Internal model for the 'homeList' field in home response result."""
|
|
157
|
-
|
|
156
|
+
homeId: int
|
|
158
157
|
homeName: str
|
|
158
|
+
nickname: str | None = None
|
|
@@ -73,8 +73,8 @@ class ClassicLVHumidResult(InnerHumidifierBaseResult):
|
|
|
73
73
|
automatic_stop_reach_target: bool = False
|
|
74
74
|
water_tank_lifted: bool = False
|
|
75
75
|
warm_enabled: bool = False
|
|
76
|
-
warm_level: int =
|
|
77
|
-
night_light_brightness: int =
|
|
76
|
+
warm_level: int | None = None
|
|
77
|
+
night_light_brightness: int | None = None
|
|
78
78
|
configuration: ClassicConfig | None = None
|
|
79
79
|
|
|
80
80
|
|
|
@@ -12,7 +12,6 @@ from dataclasses import dataclass
|
|
|
12
12
|
# import orjson
|
|
13
13
|
from mashumaro.config import BaseConfig
|
|
14
14
|
from mashumaro.types import Discriminator
|
|
15
|
-
from pyvesync.const import IntFlag, StrFlag
|
|
16
15
|
from pyvesync.models.bypass_models import (
|
|
17
16
|
RequestBypassV1,
|
|
18
17
|
BypassV2InnerResult,
|
|
@@ -53,11 +52,11 @@ class PurifierV2DetailsResult(InnerPurifierBaseResult):
|
|
|
53
52
|
efficientModeTimeRemain: int
|
|
54
53
|
errorCode: int
|
|
55
54
|
autoPreference: VitalAutoPreferences | None = None
|
|
56
|
-
fanRotateAngle: int | None =
|
|
57
|
-
filterOpenState: int | None =
|
|
58
|
-
PM1: int | None =
|
|
59
|
-
PM10: int | None =
|
|
60
|
-
AQPercent: int | None =
|
|
55
|
+
fanRotateAngle: int | None = None
|
|
56
|
+
filterOpenState: int | None = None
|
|
57
|
+
PM1: int | None = None
|
|
58
|
+
PM10: int | None = None
|
|
59
|
+
AQPercent: int | None = None
|
|
61
60
|
|
|
62
61
|
|
|
63
62
|
@dataclass
|
|
@@ -81,8 +80,8 @@ class PurifierCoreDetailsResult(InnerPurifierBaseResult):
|
|
|
81
80
|
configuration: PurifierCoreDetailsConfig | None = None
|
|
82
81
|
extension: dict | None = None
|
|
83
82
|
air_quality_value: int | None = None
|
|
84
|
-
night_light: str | None =
|
|
85
|
-
fan_rotate: str | None =
|
|
83
|
+
night_light: str | None = None
|
|
84
|
+
fan_rotate: str | None = None
|
|
86
85
|
|
|
87
86
|
|
|
88
87
|
# Purifier Timer Models
|