pyvesync 3.0.0b4__tar.gz → 3.0.0b6__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.0b4/src/pyvesync.egg-info → pyvesync-3.0.0b6}/PKG-INFO +7 -7
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/README.md +7 -7
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/pyproject.toml +1 -1
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/fan_base.py +9 -9
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/humidifier_base.py +0 -2
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/purifier_base.py +5 -5
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/vesyncbasedevice.py +1 -3
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/const.py +2 -2
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/device_map.py +0 -2
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/vesyncfan.py +6 -4
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/vesynchumidifier.py +1 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/vesyncpurifier.py +10 -46
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/fan_models.py +1 -2
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/vesync_models.py +2 -1
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/utils/errors.py +2 -2
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/vesync.py +63 -56
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6/src/pyvesync.egg-info}/PKG-INFO +7 -7
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/LICENSE +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/MANIFEST.in +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/requirements.txt +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/setup.cfg +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/setup.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/__init__.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/__init__.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/bulb_base.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/fryer_base.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/outlet_base.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/switch_base.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/base_devices/thermostat_base.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/device_container.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/__init__.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/vesyncbulb.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/vesynckitchen.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/vesyncoutlet.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/vesyncswitch.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/devices/vesyncthermostat.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/__init__.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/base_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/bulb_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/bypass_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/fryer_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/home_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/humidifier_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/outlet_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/purifier_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/switch_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/models/thermostat_models.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/utils/__init__.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/utils/colors.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/utils/device_mixins.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/utils/enum_utils.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/utils/helpers.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/utils/logs.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync/vesynchome.py +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync.egg-info/SOURCES.txt +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync.egg-info/dependency_links.txt +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/src/pyvesync.egg-info/requires.txt +0 -0
- {pyvesync-3.0.0b4 → pyvesync-3.0.0b6}/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.0b6
|
|
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>
|
|
@@ -94,11 +94,11 @@ async def main():
|
|
|
94
94
|
# To print logs to file
|
|
95
95
|
manager.log_to_file("pyvesync.log")
|
|
96
96
|
|
|
97
|
-
await manager.login()
|
|
97
|
+
await manager.login()
|
|
98
98
|
if not manager.enabled:
|
|
99
99
|
print("Not logged in.")
|
|
100
100
|
return
|
|
101
|
-
await manager.get_devices() # Instantiates supported devices in device list
|
|
101
|
+
await manager.get_devices() # Instantiates supported devices in device list, automatically called by login, only needed if you would like updates
|
|
102
102
|
await manager.update() # Updates the state of all devices
|
|
103
103
|
|
|
104
104
|
# manager.devices is a DeviceContainer object
|
|
@@ -218,7 +218,7 @@ The [raise_api_errors()](https://webdjoe.github.io/pyvesync/latest/development/u
|
|
|
218
218
|
- `VeSyncRateLimitError` - The API's rate limit has been exceeded.
|
|
219
219
|
- `VeSyncAPIStatusCodeError` - The API returned a non-200 status code.
|
|
220
220
|
- `VeSyncTokenError` - The API returned a token error and requires `login()` to be called again.
|
|
221
|
-
- `VeSyncLoginError` - The
|
|
221
|
+
- `VeSyncLoginError` - The username or password is incorrect.
|
|
222
222
|
|
|
223
223
|
## Installation
|
|
224
224
|
|
|
@@ -305,7 +305,7 @@ from pyvesync.logs import VeSyncLoginError
|
|
|
305
305
|
|
|
306
306
|
async def main():
|
|
307
307
|
async with VeSync("user", "password") as manager:
|
|
308
|
-
await manager.login()
|
|
308
|
+
await manager.login()
|
|
309
309
|
await manager.update()
|
|
310
310
|
|
|
311
311
|
# Acts as a set of device instances
|
|
@@ -376,7 +376,7 @@ async def main():
|
|
|
376
376
|
async with VeSync("user", "password") as manager:
|
|
377
377
|
manager.debug = True
|
|
378
378
|
manager.redact = True # True by default
|
|
379
|
-
await manager.login()
|
|
379
|
+
await manager.login()
|
|
380
380
|
await manager.update()
|
|
381
381
|
|
|
382
382
|
outlet = manager.outlets[0]
|
|
@@ -436,7 +436,7 @@ DEVICE_NAME = "Device" # Device to test
|
|
|
436
436
|
async def test_device():
|
|
437
437
|
# Instantiate VeSync class and login
|
|
438
438
|
async with VeSync(USERNAME, PASSWORD, debug=True, redact=True) as manager:
|
|
439
|
-
|
|
439
|
+
await manager.login()
|
|
440
440
|
|
|
441
441
|
# Pull and update devices
|
|
442
442
|
await manager.update()
|
|
@@ -63,11 +63,11 @@ async def main():
|
|
|
63
63
|
# To print logs to file
|
|
64
64
|
manager.log_to_file("pyvesync.log")
|
|
65
65
|
|
|
66
|
-
await manager.login()
|
|
66
|
+
await manager.login()
|
|
67
67
|
if not manager.enabled:
|
|
68
68
|
print("Not logged in.")
|
|
69
69
|
return
|
|
70
|
-
await manager.get_devices() # Instantiates supported devices in device list
|
|
70
|
+
await manager.get_devices() # Instantiates supported devices in device list, automatically called by login, only needed if you would like updates
|
|
71
71
|
await manager.update() # Updates the state of all devices
|
|
72
72
|
|
|
73
73
|
# manager.devices is a DeviceContainer object
|
|
@@ -187,7 +187,7 @@ The [raise_api_errors()](https://webdjoe.github.io/pyvesync/latest/development/u
|
|
|
187
187
|
- `VeSyncRateLimitError` - The API's rate limit has been exceeded.
|
|
188
188
|
- `VeSyncAPIStatusCodeError` - The API returned a non-200 status code.
|
|
189
189
|
- `VeSyncTokenError` - The API returned a token error and requires `login()` to be called again.
|
|
190
|
-
- `VeSyncLoginError` - The
|
|
190
|
+
- `VeSyncLoginError` - The username or password is incorrect.
|
|
191
191
|
|
|
192
192
|
## Installation
|
|
193
193
|
|
|
@@ -274,7 +274,7 @@ from pyvesync.logs import VeSyncLoginError
|
|
|
274
274
|
|
|
275
275
|
async def main():
|
|
276
276
|
async with VeSync("user", "password") as manager:
|
|
277
|
-
await manager.login()
|
|
277
|
+
await manager.login()
|
|
278
278
|
await manager.update()
|
|
279
279
|
|
|
280
280
|
# Acts as a set of device instances
|
|
@@ -345,7 +345,7 @@ async def main():
|
|
|
345
345
|
async with VeSync("user", "password") as manager:
|
|
346
346
|
manager.debug = True
|
|
347
347
|
manager.redact = True # True by default
|
|
348
|
-
await manager.login()
|
|
348
|
+
await manager.login()
|
|
349
349
|
await manager.update()
|
|
350
350
|
|
|
351
351
|
outlet = manager.outlets[0]
|
|
@@ -405,7 +405,7 @@ DEVICE_NAME = "Device" # Device to test
|
|
|
405
405
|
async def test_device():
|
|
406
406
|
# Instantiate VeSync class and login
|
|
407
407
|
async with VeSync(USERNAME, PASSWORD, debug=True, redact=True) as manager:
|
|
408
|
-
|
|
408
|
+
await manager.login()
|
|
409
409
|
|
|
410
410
|
# Pull and update devices
|
|
411
411
|
await manager.update()
|
|
@@ -510,4 +510,4 @@ And to all of those that contributed to the project:
|
|
|
510
510
|
<img src="https://contrib.rocks/image?repo=webdjoe/pyvesync" />
|
|
511
511
|
</a>
|
|
512
512
|
|
|
513
|
-
Made with [contrib.rocks](https://contrib.rocks).
|
|
513
|
+
Made with [contrib.rocks](https://contrib.rocks).
|
|
@@ -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.0b6"
|
|
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"
|
|
@@ -24,16 +24,16 @@ class FanState(DeviceState):
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
Attributes:
|
|
27
|
-
|
|
27
|
+
display_set_status (str): Display set status.
|
|
28
28
|
display_status (str): Display status.
|
|
29
29
|
displaying_type (str): Displaying type.
|
|
30
30
|
fan_level (int): Fan level.
|
|
31
31
|
fan_set_level (int): Fan set level.
|
|
32
32
|
humidity (int): Humidity level.
|
|
33
33
|
mode (str): Mode of device.
|
|
34
|
-
|
|
34
|
+
mute_set_status (str): Mute set status.
|
|
35
35
|
mute_status (str): Mute status.
|
|
36
|
-
|
|
36
|
+
oscillation_set_status (str): Oscillation set status.
|
|
37
37
|
oscillation_status (str): Oscillation status.
|
|
38
38
|
sleep_change_fan_level (str): Sleep change fan level.
|
|
39
39
|
sleep_fallasleep_remain (str): Sleep fall asleep remain.
|
|
@@ -45,16 +45,16 @@ class FanState(DeviceState):
|
|
|
45
45
|
"""
|
|
46
46
|
|
|
47
47
|
__slots__ = (
|
|
48
|
-
"
|
|
48
|
+
"display_set_status",
|
|
49
49
|
"display_status",
|
|
50
50
|
"displaying_type",
|
|
51
51
|
"fan_level",
|
|
52
52
|
"fan_set_level",
|
|
53
53
|
"humidity",
|
|
54
54
|
"mode",
|
|
55
|
-
"
|
|
55
|
+
"mute_set_status",
|
|
56
56
|
"mute_status",
|
|
57
|
-
"
|
|
57
|
+
"oscillation_set_status",
|
|
58
58
|
"oscillation_status",
|
|
59
59
|
"sleep_change_fan_level",
|
|
60
60
|
"sleep_fallasleep_remain",
|
|
@@ -89,11 +89,11 @@ class FanState(DeviceState):
|
|
|
89
89
|
self.sleep_oscillation_switch: str | None = None
|
|
90
90
|
self.sleep_change_fan_level: str | None = None
|
|
91
91
|
self.mute_status: str | None = None
|
|
92
|
-
self.
|
|
92
|
+
self.mute_set_status: str | None = None
|
|
93
93
|
self.oscillation_status: str | None = None
|
|
94
|
-
self.
|
|
94
|
+
self.oscillation_set_status: str | None = None
|
|
95
95
|
self.display_status: str | None = None
|
|
96
|
-
self.
|
|
96
|
+
self.display_set_status: str | None = None
|
|
97
97
|
self.displaying_type: str | None = None
|
|
98
98
|
|
|
99
99
|
|
|
@@ -32,7 +32,6 @@ class HumidifierState(DeviceState):
|
|
|
32
32
|
auto_stop_target_reached (bool): Automatic stop target reached.
|
|
33
33
|
auto_target_humidity (int): Target humidity level.
|
|
34
34
|
automatic_stop_config (bool): Automatic stop configuration.
|
|
35
|
-
child_lock_status (bool): Child lock status.
|
|
36
35
|
display_set_status (str): Display set status.
|
|
37
36
|
display_status (str): Display status.
|
|
38
37
|
drying_mode_auto_switch (str): Drying mode auto switch status.
|
|
@@ -59,7 +58,6 @@ class HumidifierState(DeviceState):
|
|
|
59
58
|
"auto_stop_target_reached",
|
|
60
59
|
"auto_target_humidity",
|
|
61
60
|
"automatic_stop_config",
|
|
62
|
-
"child_lock_status",
|
|
63
61
|
"display_set_status",
|
|
64
62
|
"display_status",
|
|
65
63
|
"drying_mode_auto_switch",
|
|
@@ -43,7 +43,7 @@ class PurifierState(DeviceState):
|
|
|
43
43
|
air_quality_level (AirQualityLevel): Air quality level of the purifier.
|
|
44
44
|
child_lock (bool): Child lock status of the purifier.
|
|
45
45
|
display_status (str): Display status of the purifier.
|
|
46
|
-
|
|
46
|
+
display_set_status (str): Display set status of the purifier.
|
|
47
47
|
display_forever (bool): Display forever status of the purifier.
|
|
48
48
|
timer (Timer): Timer object for the purifier.
|
|
49
49
|
pm25 (int): PM2.5 value of the purifier.
|
|
@@ -66,7 +66,7 @@ class PurifierState(DeviceState):
|
|
|
66
66
|
"auto_room_size",
|
|
67
67
|
"child_lock",
|
|
68
68
|
"display_forever",
|
|
69
|
-
"
|
|
69
|
+
"display_set_status",
|
|
70
70
|
"display_status",
|
|
71
71
|
"fan_level",
|
|
72
72
|
"fan_rotate_angle",
|
|
@@ -100,7 +100,7 @@ class PurifierState(DeviceState):
|
|
|
100
100
|
self.child_lock: bool = False
|
|
101
101
|
self.filter_open_state: bool = False
|
|
102
102
|
self.display_status: str | None = None
|
|
103
|
-
self.
|
|
103
|
+
self.display_set_status: str | None = None
|
|
104
104
|
self.display_forever: bool = False
|
|
105
105
|
# Attributes not supported by all purifiers
|
|
106
106
|
self.pm25: int | None = None
|
|
@@ -177,10 +177,10 @@ class PurifierState(DeviceState):
|
|
|
177
177
|
return self.display_status
|
|
178
178
|
|
|
179
179
|
@property
|
|
180
|
-
@deprecated("Use
|
|
180
|
+
@deprecated("Use display_set_status instead.")
|
|
181
181
|
def display_switch(self) -> str | None:
|
|
182
182
|
"""Return display switch status."""
|
|
183
|
-
return self.
|
|
183
|
+
return self.display_set_status
|
|
184
184
|
|
|
185
185
|
|
|
186
186
|
class VeSyncPurifier(VeSyncBaseToggleDevice):
|
|
@@ -148,9 +148,7 @@ class VeSyncBaseDevice(ABC, Generic[VS_STATE_T]):
|
|
|
148
148
|
|
|
149
149
|
def __hash__(self) -> int:
|
|
150
150
|
"""Use CID and sub-device number to make device hash."""
|
|
151
|
-
|
|
152
|
-
return hash(self.cid + str(self.sub_device_no))
|
|
153
|
-
return hash(self.cid)
|
|
151
|
+
return hash(self.cid + str(self.sub_device_no))
|
|
154
152
|
|
|
155
153
|
def __str__(self) -> str:
|
|
156
154
|
"""Use device info for string representation of class."""
|
|
@@ -202,7 +202,7 @@ class DeviceStatus(StrEnum):
|
|
|
202
202
|
return -1
|
|
203
203
|
|
|
204
204
|
@classmethod
|
|
205
|
-
def from_int(cls, value: int) -> str:
|
|
205
|
+
def from_int(cls, value: int | None) -> str:
|
|
206
206
|
"""Convert integer value to corresponding string.
|
|
207
207
|
|
|
208
208
|
If value is 1, return ON and if 0, return OFF.
|
|
@@ -219,7 +219,7 @@ class DeviceStatus(StrEnum):
|
|
|
219
219
|
@classmethod
|
|
220
220
|
def from_bool(cls, value: bool) -> 'DeviceStatus':
|
|
221
221
|
"""Convert boolean value to corresponding string."""
|
|
222
|
-
return cls.ON if value else cls.OFF
|
|
222
|
+
return cls.ON if value is True else cls.OFF
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
class ConnectionStatus(StrEnum):
|
|
@@ -853,9 +853,7 @@ fan_modules: list[FanMap] = [
|
|
|
853
853
|
class_name="VeSyncTowerFan",
|
|
854
854
|
dev_types=["LTF-F422S-KEU", "LTF-F422S-WUSR", "LTF-F422_WJP", "LTF-F422S-WUS"],
|
|
855
855
|
modes=[
|
|
856
|
-
FanModes.SLEEP, # Alias for Advanced sleep mode
|
|
857
856
|
FanModes.NORMAL,
|
|
858
|
-
FanModes.MANUAL,
|
|
859
857
|
FanModes.TURBO,
|
|
860
858
|
FanModes.AUTO,
|
|
861
859
|
FanModes.ADVANCED_SLEEP,
|
|
@@ -43,11 +43,11 @@ class VeSyncTowerFan(BypassV2Mixin, VeSyncFanBase):
|
|
|
43
43
|
self.state.thermal_comfort = res.thermalComfort
|
|
44
44
|
|
|
45
45
|
self.state.mute_status = DeviceStatus.from_int(res.muteState)
|
|
46
|
-
self.state.
|
|
46
|
+
self.state.mute_set_status = DeviceStatus.from_int(res.muteSwitch)
|
|
47
47
|
self.state.oscillation_status = DeviceStatus.from_int(res.oscillationState)
|
|
48
|
-
self.state.
|
|
48
|
+
self.state.oscillation_set_status = DeviceStatus.from_int(res.oscillationSwitch)
|
|
49
49
|
self.state.display_status = DeviceStatus.from_int(res.screenState)
|
|
50
|
-
self.state.
|
|
50
|
+
self.state.display_set_status = DeviceStatus.from_int(res.screenSwitch)
|
|
51
51
|
self.state.displaying_type = DeviceStatus.from_int(res.displayingType)
|
|
52
52
|
|
|
53
53
|
if res.timerRemain is not None and res.timerRemain > 0:
|
|
@@ -116,7 +116,7 @@ class VeSyncTowerFan(BypassV2Mixin, VeSyncFanBase):
|
|
|
116
116
|
payload_data = {
|
|
117
117
|
"workMode": mode
|
|
118
118
|
}
|
|
119
|
-
r_dict = await self.call_bypassv2_api("
|
|
119
|
+
r_dict = await self.call_bypassv2_api("setTowerFanMode", payload_data)
|
|
120
120
|
r = Helpers.process_dev_response(logger, "set_mode", self, r_dict)
|
|
121
121
|
if r is None:
|
|
122
122
|
return False
|
|
@@ -126,6 +126,8 @@ class VeSyncTowerFan(BypassV2Mixin, VeSyncFanBase):
|
|
|
126
126
|
return True
|
|
127
127
|
|
|
128
128
|
async def toggle_switch(self, toggle: bool | None = None) -> bool:
|
|
129
|
+
if toggle is None:
|
|
130
|
+
toggle = self.state.device_status == DeviceStatus.OFF
|
|
129
131
|
payload_data = {
|
|
130
132
|
'powerSwitch': int(toggle),
|
|
131
133
|
'switchIdx': 0,
|
|
@@ -221,6 +221,7 @@ class VeSyncHumid200300S(BypassV2Mixin, VeSyncHumidifier):
|
|
|
221
221
|
if r is None:
|
|
222
222
|
return False
|
|
223
223
|
self.state.display_set_status = DeviceStatus.from_bool(toggle)
|
|
224
|
+
self.state.display_status = DeviceStatus.from_bool(toggle)
|
|
224
225
|
self.state.connection_status = ConnectionStatus.ONLINE
|
|
225
226
|
return True
|
|
226
227
|
|
|
@@ -12,7 +12,6 @@ from pyvesync.utils.device_mixins import (
|
|
|
12
12
|
process_bypassv2_result
|
|
13
13
|
)
|
|
14
14
|
from pyvesync.utils.helpers import Helpers, Timer
|
|
15
|
-
# from pyvesync.models.base_models import DefaultValues
|
|
16
15
|
from pyvesync.const import (
|
|
17
16
|
PurifierAutoPreference,
|
|
18
17
|
DeviceStatus,
|
|
@@ -124,7 +123,7 @@ class VeSyncAirBypass(BypassV2Mixin, VeSyncPurifier):
|
|
|
124
123
|
self.state.child_lock = result.child_lock or False
|
|
125
124
|
config = result.configuration
|
|
126
125
|
if config is not None:
|
|
127
|
-
self.state.
|
|
126
|
+
self.state.display_set_status = DeviceStatus.ON if config.display \
|
|
128
127
|
else DeviceStatus.OFF
|
|
129
128
|
self.state.display_forever = config.display_forever
|
|
130
129
|
if config.auto_preference is not None:
|
|
@@ -134,8 +133,7 @@ class VeSyncAirBypass(BypassV2Mixin, VeSyncPurifier):
|
|
|
134
133
|
self.state.pm25 = result.air_quality_value
|
|
135
134
|
self.state.set_air_quality_level(result.air_quality)
|
|
136
135
|
if result.night_light is not None:
|
|
137
|
-
self.state.nightlight_status = DeviceStatus
|
|
138
|
-
else DeviceStatus.OFF
|
|
136
|
+
self.state.nightlight_status = DeviceStatus(result.night_light)
|
|
139
137
|
|
|
140
138
|
async def get_details(self) -> None:
|
|
141
139
|
r_dict = await self.call_bypassv2_api('getPurifierStatus')
|
|
@@ -251,6 +249,7 @@ class VeSyncAirBypass(BypassV2Mixin, VeSyncPurifier):
|
|
|
251
249
|
return False
|
|
252
250
|
|
|
253
251
|
self.state.fan_level = new_speed
|
|
252
|
+
self.state.fan_set_level = new_speed
|
|
254
253
|
self.state.mode = PurifierModes.MANUAL # Set mode to manual to set fan speed
|
|
255
254
|
self.state.device_status = DeviceStatus.ON
|
|
256
255
|
self.state.connection_status = ConnectionStatus.ONLINE
|
|
@@ -351,7 +350,7 @@ class VeSyncAirBypass(BypassV2Mixin, VeSyncPurifier):
|
|
|
351
350
|
if r is None:
|
|
352
351
|
return False
|
|
353
352
|
self.state.connection_status = ConnectionStatus.ONLINE
|
|
354
|
-
self.state.
|
|
353
|
+
self.state.display_set_status = DeviceStatus.from_bool(mode)
|
|
355
354
|
return True
|
|
356
355
|
|
|
357
356
|
async def set_nightlight_mode(self, mode: str) -> bool:
|
|
@@ -477,7 +476,7 @@ class VeSyncAirBaseV2(VeSyncAirBypass):
|
|
|
477
476
|
self.state.light_detection_status = DeviceStatus.from_int(
|
|
478
477
|
details.environmentLightState
|
|
479
478
|
)
|
|
480
|
-
self.state.
|
|
479
|
+
self.state.display_set_status = DeviceStatus.from_int(details.screenSwitch)
|
|
481
480
|
self.state.display_status = DeviceStatus.from_int(details.screenState)
|
|
482
481
|
auto_pref = details.autoPreference
|
|
483
482
|
if auto_pref is not None:
|
|
@@ -588,7 +587,7 @@ class VeSyncAirBaseV2(VeSyncAirBypass):
|
|
|
588
587
|
return True
|
|
589
588
|
|
|
590
589
|
async def toggle_display(self, mode: bool) -> bool:
|
|
591
|
-
if bool(self.state.
|
|
590
|
+
if bool(self.state.display_set_status) == mode:
|
|
592
591
|
_LOGGER.debug('Display is already %s', mode)
|
|
593
592
|
return True
|
|
594
593
|
|
|
@@ -601,7 +600,7 @@ class VeSyncAirBaseV2(VeSyncAirBypass):
|
|
|
601
600
|
if r is None:
|
|
602
601
|
return False
|
|
603
602
|
|
|
604
|
-
self.state.
|
|
603
|
+
self.state.display_set_status = DeviceStatus.from_bool(mode)
|
|
605
604
|
self.state.connection_status = ConnectionStatus.ONLINE
|
|
606
605
|
return True
|
|
607
606
|
|
|
@@ -707,6 +706,7 @@ class VeSyncAirBaseV2(VeSyncAirBypass):
|
|
|
707
706
|
return False
|
|
708
707
|
|
|
709
708
|
self.state.fan_set_level = new_speed
|
|
709
|
+
self.state.fan_level = new_speed
|
|
710
710
|
self.state.mode = PurifierModes.MANUAL
|
|
711
711
|
self.state.device_status = DeviceStatus.ON
|
|
712
712
|
self.state.connection_status = ConnectionStatus.ONLINE
|
|
@@ -806,7 +806,7 @@ class VeSyncAir131(BypassV1Mixin, VeSyncPurifier):
|
|
|
806
806
|
self.state.active_time = details.activeTime
|
|
807
807
|
self.state.filter_life = details.filterLife.percent
|
|
808
808
|
self.state.display_status = details.screenStatus
|
|
809
|
-
self.state.
|
|
809
|
+
self.state.display_set_status = details.screenStatus
|
|
810
810
|
self.state.child_lock = bool(DeviceStatus(details.childLock))
|
|
811
811
|
self.state.mode = details.mode
|
|
812
812
|
self.state.fan_level = details.level or 0
|
|
@@ -814,15 +814,6 @@ class VeSyncAir131(BypassV1Mixin, VeSyncPurifier):
|
|
|
814
814
|
self.state.set_air_quality_level(details.airQuality)
|
|
815
815
|
|
|
816
816
|
async def get_details(self) -> None:
|
|
817
|
-
# body = self._build_request('deviceDetail')
|
|
818
|
-
# headers = Helpers.req_header_bypass()
|
|
819
|
-
|
|
820
|
-
# r_dict, _ = await self.manager.async_call_api(
|
|
821
|
-
# '/cloud/v1/deviceManaged/deviceDetail',
|
|
822
|
-
# method='post',
|
|
823
|
-
# headers=headers,
|
|
824
|
-
# json_object=body.to_dict(),
|
|
825
|
-
# )
|
|
826
817
|
r_dict = await self.call_bypassv1_api(
|
|
827
818
|
RequestPurifier131, method="deviceDetail", endpoint="deviceDetail"
|
|
828
819
|
)
|
|
@@ -837,13 +828,6 @@ class VeSyncAir131(BypassV1Mixin, VeSyncPurifier):
|
|
|
837
828
|
update_dict = {
|
|
838
829
|
"status": "on" if mode else "off"
|
|
839
830
|
}
|
|
840
|
-
# body = self._build_request('airPurifierScreenCtl', update_dict=update_dict)
|
|
841
|
-
# headers = Helpers.req_header_bypass()
|
|
842
|
-
|
|
843
|
-
# r_dict, _ = await self.manager.async_call_api(
|
|
844
|
-
# '/cloud/v1/deviceManaged/airPurifierScreenCtl', 'post',
|
|
845
|
-
# json_object=body.to_dict(), headers=headers
|
|
846
|
-
# )
|
|
847
831
|
r_dict = await self.call_bypassv1_api(
|
|
848
832
|
RequestPurifier131, method="airPurifierScreenCtl",
|
|
849
833
|
endpoint="airPurifierScreenCtl", update_dict=update_dict
|
|
@@ -852,7 +836,7 @@ class VeSyncAir131(BypassV1Mixin, VeSyncPurifier):
|
|
|
852
836
|
if r is None:
|
|
853
837
|
return False
|
|
854
838
|
|
|
855
|
-
self.state.
|
|
839
|
+
self.state.display_set_status = DeviceStatus.from_bool(mode)
|
|
856
840
|
self.state.display_status = DeviceStatus.from_bool(mode)
|
|
857
841
|
self.state.connection_status = ConnectionStatus.ONLINE
|
|
858
842
|
return True
|
|
@@ -864,12 +848,6 @@ class VeSyncAir131(BypassV1Mixin, VeSyncPurifier):
|
|
|
864
848
|
update_dict = {
|
|
865
849
|
"status": DeviceStatus.from_bool(toggle).value
|
|
866
850
|
}
|
|
867
|
-
# body = self._build_request('airPurifierPowerSwitchCtl', update_dict=update_dict)
|
|
868
|
-
# headers = Helpers.req_header_bypass()
|
|
869
|
-
# r_dict, _ = await self.manager.async_call_api(
|
|
870
|
-
# '/cloud/v1/deviceManaged/airPurifierPowerSwitchCtl', 'post',
|
|
871
|
-
# json_object=body.to_dict(), headers=headers
|
|
872
|
-
# )
|
|
873
851
|
r_dict = await self.call_bypassv1_api(
|
|
874
852
|
RequestPurifier131, method="airPurifierPowerSwitchCtl",
|
|
875
853
|
endpoint="airPurifierPowerSwitchCtl", update_dict=update_dict
|
|
@@ -900,13 +878,6 @@ class VeSyncAir131(BypassV1Mixin, VeSyncPurifier):
|
|
|
900
878
|
update_dict = {
|
|
901
879
|
"level": new_speed
|
|
902
880
|
}
|
|
903
|
-
# body = self._build_request('airPurifierSpeedCtl', update_dict=update_dict)
|
|
904
|
-
# headers = Helpers.req_header_bypass()
|
|
905
|
-
|
|
906
|
-
# r_dict, _ = await self.manager.async_call_api(
|
|
907
|
-
# '/cloud/v1/deviceManaged/airPurifierSpeedCtl', 'post',
|
|
908
|
-
# json_object=body, headers=headers
|
|
909
|
-
# )
|
|
910
881
|
r_dict = await self.call_bypassv1_api(
|
|
911
882
|
RequestPurifier131Level, method="airPurifierSpeedCtl",
|
|
912
883
|
endpoint="airPurifierSpeedCtl", update_dict=update_dict
|
|
@@ -935,13 +906,6 @@ class VeSyncAir131(BypassV1Mixin, VeSyncPurifier):
|
|
|
935
906
|
update_dict = {
|
|
936
907
|
"mode": mode
|
|
937
908
|
}
|
|
938
|
-
# body = self._build_request('airPurifierModeCtl', update_dict=update_dict)
|
|
939
|
-
# headers = Helpers.req_header_bypass()
|
|
940
|
-
|
|
941
|
-
# r_dict, _ = await self.manager.async_call_api(
|
|
942
|
-
# '/cloud/v1/deviceManaged/airPurifierRunModeCtl', 'post',
|
|
943
|
-
# json_object=body.to_dict(), headers=headers
|
|
944
|
-
# )
|
|
945
909
|
r_dict = await self.call_bypassv1_api(
|
|
946
910
|
RequestPurifier131Mode, method="airPurifierRunModeCtl",
|
|
947
911
|
endpoint="airPurifierRunModeCtl", update_dict=update_dict
|
|
@@ -15,9 +15,8 @@ from pyvesync.models.bypass_models import (
|
|
|
15
15
|
|
|
16
16
|
@dataclass
|
|
17
17
|
class TowerFanResult(BypassV2InnerResult):
|
|
18
|
-
"""
|
|
18
|
+
"""Tower Fan Result Model."""
|
|
19
19
|
powerSwitch: int
|
|
20
|
-
filterLifePercent: int
|
|
21
20
|
workMode: str
|
|
22
21
|
manualSpeedLevel: int
|
|
23
22
|
fanSpeedLevel: int
|
|
@@ -247,7 +247,8 @@ class ResponseDeviceDetailsModel(ResponseBaseModel):
|
|
|
247
247
|
"""Perform device_list pre-deserialization processes.
|
|
248
248
|
|
|
249
249
|
This performs the following:
|
|
250
|
-
- Flattens the `deviceProp` field into `deviceStatus`, `connectionStatus`
|
|
250
|
+
- Flattens the `deviceProp` field into `deviceStatus`, `connectionStatus`
|
|
251
|
+
and `macID`
|
|
251
252
|
- Sets `cid` to `uuid` or `macID` if null
|
|
252
253
|
"""
|
|
253
254
|
super().__pre_deserialize__(d)
|
|
@@ -787,7 +787,7 @@ class VeSyncError(Exception):
|
|
|
787
787
|
"""
|
|
788
788
|
|
|
789
789
|
|
|
790
|
-
class
|
|
790
|
+
class VeSyncLoginError(VeSyncError):
|
|
791
791
|
"""Exception raised for login authentication errors."""
|
|
792
792
|
|
|
793
793
|
def __init__(self, msg: str) -> None:
|
|
@@ -855,7 +855,7 @@ def raise_api_errors(error_info: ResponseInfo) -> None:
|
|
|
855
855
|
case ErrorTypes.RATE_LIMIT:
|
|
856
856
|
raise VeSyncRateLimitError
|
|
857
857
|
case ErrorTypes.AUTHENTICATION:
|
|
858
|
-
raise
|
|
858
|
+
raise VeSyncLoginError(error_info.message)
|
|
859
859
|
case ErrorTypes.TOKEN_ERROR:
|
|
860
860
|
raise VeSyncTokenError
|
|
861
861
|
case ErrorTypes.SERVER_ERROR:
|
|
@@ -39,7 +39,7 @@ from pyvesync.utils.errors import (
|
|
|
39
39
|
VeSyncAPIStatusCodeError,
|
|
40
40
|
VeSyncError,
|
|
41
41
|
VeSyncServerError,
|
|
42
|
-
|
|
42
|
+
VeSyncLoginError,
|
|
43
43
|
raise_api_errors,
|
|
44
44
|
)
|
|
45
45
|
|
|
@@ -74,7 +74,7 @@ class VeSync: # pylint: disable=function-redefined
|
|
|
74
74
|
'username'
|
|
75
75
|
)
|
|
76
76
|
|
|
77
|
-
def __init__(self,
|
|
77
|
+
def __init__(self, # noqa: PLR0913
|
|
78
78
|
username: str,
|
|
79
79
|
password: str,
|
|
80
80
|
country_code: str = DEFAULT_REGION,
|
|
@@ -106,6 +106,8 @@ class VeSync: # pylint: disable=function-redefined
|
|
|
106
106
|
time_zone (str): Time zone for device from IANA database, by default
|
|
107
107
|
DEFAULT_TZ. This is automatically set to the time zone of the
|
|
108
108
|
VeSync account during login.
|
|
109
|
+
debug (bool): Enable debug logging, by default False.
|
|
110
|
+
redact (bool): Enable redaction of sensitive information, by default True.
|
|
109
111
|
|
|
110
112
|
Attributes:
|
|
111
113
|
session (ClientSession): Client session for API calls
|
|
@@ -310,17 +312,16 @@ class VeSync: # pylint: disable=function-redefined
|
|
|
310
312
|
|
|
311
313
|
Username and password are provided when class is instantiated.
|
|
312
314
|
|
|
313
|
-
Returns:
|
|
314
|
-
True if login successful, False if not.
|
|
315
|
-
|
|
316
315
|
Raises:
|
|
317
|
-
VeSyncLoginError: If login fails due to invalid username
|
|
316
|
+
VeSyncLoginError: If login fails, for example due to invalid username
|
|
317
|
+
or password.
|
|
318
318
|
VeSyncAPIResponseError: If API response is invalid.
|
|
319
319
|
VeSyncServerError: If server returns an error.
|
|
320
320
|
"""
|
|
321
321
|
if not isinstance(self.username, str) or len(self.username) == 0 \
|
|
322
322
|
or not isinstance(self.password, str) or len(self.password) == 0:
|
|
323
|
-
raise
|
|
323
|
+
raise VeSyncLoginError('Username and password must be specified')
|
|
324
|
+
|
|
324
325
|
request_auth = RequestAuthModel(
|
|
325
326
|
email=self.username,
|
|
326
327
|
method='authByPWDOrOTM',
|
|
@@ -332,27 +333,32 @@ class VeSync: # pylint: disable=function-redefined
|
|
|
332
333
|
)
|
|
333
334
|
if resp_dict is None:
|
|
334
335
|
raise VeSyncAPIResponseError('Error receiving response to auth request')
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
336
|
+
|
|
337
|
+
if resp_dict.get('code') != 0:
|
|
338
|
+
error_info = ErrorCodes.get_error_info(resp_dict.get("code"))
|
|
339
|
+
resp_message = resp_dict.get('msg')
|
|
340
|
+
if resp_message is not None:
|
|
341
|
+
error_info.message = f'{error_info.message} ({resp_message})'
|
|
342
|
+
raise VeSyncAPIResponseError(
|
|
343
|
+
f"Error receiving response to auth request - {error_info.message}"
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
response_model = ResponseLoginModel.from_dict(resp_dict)
|
|
348
|
+
except Exception as exc:
|
|
349
|
+
logger.debug('Error parsing auth response: %s', exc)
|
|
350
|
+
raise VeSyncAPIResponseError(
|
|
351
|
+
'Error receiving response to auth request'
|
|
352
|
+
) from exc
|
|
353
|
+
|
|
354
|
+
result = response_model.result
|
|
355
|
+
if not isinstance(result, IntRespAuthResultModel):
|
|
356
|
+
raise VeSyncAPIResponseError(
|
|
357
|
+
"Error receiving response to login request -"
|
|
358
|
+
" result is not IntRespAuthResultModel"
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
return await self._login_token(auth_code=result.authorizeCode)
|
|
356
362
|
|
|
357
363
|
async def _login_token(
|
|
358
364
|
self,
|
|
@@ -370,11 +376,9 @@ class VeSync: # pylint: disable=function-redefined
|
|
|
370
376
|
region_change_token (str): "bizToken" to use when calling this endpoint
|
|
371
377
|
for a second time with a different region.
|
|
372
378
|
|
|
373
|
-
Returns:
|
|
374
|
-
True if login successful, False if not.
|
|
375
|
-
|
|
376
379
|
Raises:
|
|
377
|
-
VeSyncLoginError: If login fails due to invalid username
|
|
380
|
+
VeSyncLoginError: If login fails, for example due to invalid username
|
|
381
|
+
or password.
|
|
378
382
|
VeSyncAPIResponseError: If API response is invalid.
|
|
379
383
|
VeSyncServerError: If server returns an error.
|
|
380
384
|
"""
|
|
@@ -398,35 +402,38 @@ class VeSync: # pylint: disable=function-redefined
|
|
|
398
402
|
"Error receiving response to login request -"
|
|
399
403
|
"result is not IntRespLoginResultModel"
|
|
400
404
|
)
|
|
401
|
-
if response_model.code
|
|
402
|
-
|
|
403
|
-
if
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
405
|
+
if response_model.code != 0:
|
|
406
|
+
error_info = ErrorCodes.get_error_info(resp_dict.get("code"))
|
|
407
|
+
if error_info.error_type == ErrorTypes.CROSS_REGION: # cross region error
|
|
408
|
+
result = response_model.result
|
|
409
|
+
self.country_code = result.countryCode
|
|
410
|
+
await self._login_token(region_change_token=result.bizToken)
|
|
411
|
+
resp_message = resp_dict.get('msg')
|
|
412
|
+
if resp_message is not None:
|
|
413
|
+
error_info.message = f'{error_info.message} ({resp_message})'
|
|
414
|
+
raise VeSyncLoginError(
|
|
415
|
+
f"Error receiving response to login request - {error_info.message}"
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
result = response_model.result
|
|
419
|
+
if not isinstance(result, IntRespLoginResultModel):
|
|
420
|
+
raise VeSyncAPIResponseError(
|
|
421
|
+
"Error receiving response to login request -"
|
|
422
|
+
" result is not IntRespLoginResultModel"
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
self._token = result.token
|
|
426
|
+
self._account_id = result.accountID
|
|
427
|
+
self.country_code = result.countryCode
|
|
428
|
+
self.enabled = True
|
|
429
|
+
logger.debug('Login successful')
|
|
430
|
+
|
|
425
431
|
except (MissingField, UnserializableDataError) as exc:
|
|
426
432
|
logger.debug('Error parsing login response: %s', exc)
|
|
427
433
|
raise VeSyncAPIResponseError(
|
|
428
434
|
'Error receiving response to login request'
|
|
429
435
|
) from exc
|
|
436
|
+
return True
|
|
430
437
|
|
|
431
438
|
async def update(self) -> None:
|
|
432
439
|
"""Fetch updated information about devices and new device list.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyvesync
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0b6
|
|
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>
|
|
@@ -94,11 +94,11 @@ async def main():
|
|
|
94
94
|
# To print logs to file
|
|
95
95
|
manager.log_to_file("pyvesync.log")
|
|
96
96
|
|
|
97
|
-
await manager.login()
|
|
97
|
+
await manager.login()
|
|
98
98
|
if not manager.enabled:
|
|
99
99
|
print("Not logged in.")
|
|
100
100
|
return
|
|
101
|
-
await manager.get_devices() # Instantiates supported devices in device list
|
|
101
|
+
await manager.get_devices() # Instantiates supported devices in device list, automatically called by login, only needed if you would like updates
|
|
102
102
|
await manager.update() # Updates the state of all devices
|
|
103
103
|
|
|
104
104
|
# manager.devices is a DeviceContainer object
|
|
@@ -218,7 +218,7 @@ The [raise_api_errors()](https://webdjoe.github.io/pyvesync/latest/development/u
|
|
|
218
218
|
- `VeSyncRateLimitError` - The API's rate limit has been exceeded.
|
|
219
219
|
- `VeSyncAPIStatusCodeError` - The API returned a non-200 status code.
|
|
220
220
|
- `VeSyncTokenError` - The API returned a token error and requires `login()` to be called again.
|
|
221
|
-
- `VeSyncLoginError` - The
|
|
221
|
+
- `VeSyncLoginError` - The username or password is incorrect.
|
|
222
222
|
|
|
223
223
|
## Installation
|
|
224
224
|
|
|
@@ -305,7 +305,7 @@ from pyvesync.logs import VeSyncLoginError
|
|
|
305
305
|
|
|
306
306
|
async def main():
|
|
307
307
|
async with VeSync("user", "password") as manager:
|
|
308
|
-
await manager.login()
|
|
308
|
+
await manager.login()
|
|
309
309
|
await manager.update()
|
|
310
310
|
|
|
311
311
|
# Acts as a set of device instances
|
|
@@ -376,7 +376,7 @@ async def main():
|
|
|
376
376
|
async with VeSync("user", "password") as manager:
|
|
377
377
|
manager.debug = True
|
|
378
378
|
manager.redact = True # True by default
|
|
379
|
-
await manager.login()
|
|
379
|
+
await manager.login()
|
|
380
380
|
await manager.update()
|
|
381
381
|
|
|
382
382
|
outlet = manager.outlets[0]
|
|
@@ -436,7 +436,7 @@ DEVICE_NAME = "Device" # Device to test
|
|
|
436
436
|
async def test_device():
|
|
437
437
|
# Instantiate VeSync class and login
|
|
438
438
|
async with VeSync(USERNAME, PASSWORD, debug=True, redact=True) as manager:
|
|
439
|
-
|
|
439
|
+
await manager.login()
|
|
440
440
|
|
|
441
441
|
# Pull and update devices
|
|
442
442
|
await manager.update()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|