pyvesync 3.0.0b0__py3-none-any.whl

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.
Files changed (49) hide show
  1. pyvesync/__init__.py +11 -0
  2. pyvesync/base_devices/__init__.py +35 -0
  3. pyvesync/base_devices/bulb_base.py +348 -0
  4. pyvesync/base_devices/fan_base.py +398 -0
  5. pyvesync/base_devices/fryer_base.py +69 -0
  6. pyvesync/base_devices/humidifier_base.py +460 -0
  7. pyvesync/base_devices/outlet_base.py +315 -0
  8. pyvesync/base_devices/purifier_base.py +518 -0
  9. pyvesync/base_devices/switch_base.py +270 -0
  10. pyvesync/base_devices/thermostat_base.py +277 -0
  11. pyvesync/base_devices/vesyncbasedevice.py +580 -0
  12. pyvesync/const.py +749 -0
  13. pyvesync/device_container.py +360 -0
  14. pyvesync/device_map.py +1090 -0
  15. pyvesync/devices/__init__.py +1 -0
  16. pyvesync/devices/vesyncbulb.py +1254 -0
  17. pyvesync/devices/vesyncfan.py +238 -0
  18. pyvesync/devices/vesynchumidifier.py +785 -0
  19. pyvesync/devices/vesynckitchen.py +664 -0
  20. pyvesync/devices/vesyncoutlet.py +962 -0
  21. pyvesync/devices/vesyncpurifier.py +951 -0
  22. pyvesync/devices/vesyncswitch.py +449 -0
  23. pyvesync/devices/vesyncthermostat.py +175 -0
  24. pyvesync/models/__init__.py +11 -0
  25. pyvesync/models/base_models.py +117 -0
  26. pyvesync/models/bulb_models.py +173 -0
  27. pyvesync/models/bypass_models.py +264 -0
  28. pyvesync/models/fan_models.py +46 -0
  29. pyvesync/models/fryer_models.py +28 -0
  30. pyvesync/models/home_models.py +158 -0
  31. pyvesync/models/humidifier_models.py +187 -0
  32. pyvesync/models/outlet_models.py +194 -0
  33. pyvesync/models/purifier_models.py +204 -0
  34. pyvesync/models/switch_models.py +89 -0
  35. pyvesync/models/thermostat_models.py +122 -0
  36. pyvesync/models/vesync_models.py +252 -0
  37. pyvesync/utils/__init__.py +1 -0
  38. pyvesync/utils/colors.py +248 -0
  39. pyvesync/utils/device_mixins.py +366 -0
  40. pyvesync/utils/enum_utils.py +25 -0
  41. pyvesync/utils/errors.py +859 -0
  42. pyvesync/utils/helpers.py +696 -0
  43. pyvesync/utils/logs.py +500 -0
  44. pyvesync/vesync.py +445 -0
  45. pyvesync-3.0.0b0.dist-info/METADATA +527 -0
  46. pyvesync-3.0.0b0.dist-info/RECORD +49 -0
  47. pyvesync-3.0.0b0.dist-info/WHEEL +5 -0
  48. pyvesync-3.0.0b0.dist-info/licenses/LICENSE +21 -0
  49. pyvesync-3.0.0b0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,518 @@
1
+ """Air Purifier Base Class."""
2
+ from __future__ import annotations
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+ from abc import abstractmethod
6
+
7
+ from deprecated import deprecated
8
+
9
+ from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseToggleDevice, DeviceState
10
+ from pyvesync.const import (
11
+ AirQualityLevel,
12
+ PurifierFeatures,
13
+ PurifierModes,
14
+ DeviceStatus,
15
+ NightlightModes,
16
+ PurifierAutoPreference,
17
+ IntFlag,
18
+ StrFlag,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ from pyvesync import VeSync
23
+ from pyvesync.models.vesync_models import ResponseDeviceDetailsModel
24
+ from pyvesync.device_map import PurifierMap
25
+
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class PurifierState(DeviceState):
31
+ """Base state class for Purifiers.
32
+
33
+ Attributes:
34
+ active_time (int): Active time of device, defaults to None.
35
+ connection_status (str): Connection status of device.
36
+ device (VeSyncOutlet): Device object.
37
+ device_status (str): Device status.
38
+ features (dict): Features of device.
39
+ last_update_ts (int): Last update timestamp of device, defaults to None.
40
+ mode (str): Current mode of the purifier.
41
+ fan_level (int): Current fan level of the purifier.
42
+ fan_set_level (int): Set fan level of the purifier.
43
+ filter_life (int): Filter life percentage of the purifier.
44
+ auto_preference_type (str): Auto preference type of the purifier.
45
+ auto_room_size (int): Auto room size of the purifier.
46
+ air_quality_level (AirQualityLevel): Air quality level of the purifier.
47
+ child_lock (bool): Child lock status of the purifier.
48
+ display_status (str): Display status of the purifier.
49
+ display_set_state (str): Display set state of the purifier.
50
+ display_forever (bool): Display forever status of the purifier.
51
+ timer (Timer): Timer object for the purifier.
52
+ pm25 (int): PM2.5 value of the purifier.
53
+ pm1 (int): PM1 value of the purifier.
54
+ pm10 (int): PM10 value of the purifier.
55
+ aq_percent (int): Air quality percentage of the purifier.
56
+ light_detection_switch (str): Light detection switch status of the purifier.
57
+ light_detection_status (str): Light detection status of the purifier.
58
+ nightlight_status (str): Nightlight status of the purifier.
59
+ fan_rotate_angle (int): Fan rotate angle of the purifier.
60
+
61
+ Note:
62
+ Not all attributes are supported by all models.
63
+ """
64
+
65
+ __slots__ = (
66
+ "_air_quality_level",
67
+ "aq_percent",
68
+ "auto_preference_type",
69
+ "auto_room_size",
70
+ "child_lock",
71
+ "display_forever",
72
+ "display_set_state",
73
+ "display_status",
74
+ "fan_level",
75
+ "fan_rotate_angle",
76
+ "fan_set_level",
77
+ "filter_life",
78
+ "filter_open_state",
79
+ "light_detection_status",
80
+ "light_detection_switch",
81
+ "mode",
82
+ "nightlight_status",
83
+ "pm1",
84
+ "pm10",
85
+ "pm25",
86
+ )
87
+
88
+ def __init__(
89
+ self,
90
+ device: VeSyncPurifier,
91
+ details: ResponseDeviceDetailsModel,
92
+ feature_map: PurifierMap,
93
+ ) -> None:
94
+ """Initialize Purifier State."""
95
+ super().__init__(device, details, feature_map)
96
+ self.mode: str = PurifierModes.UNKNOWN
97
+ self.fan_level: int | None = None
98
+ self.fan_set_level: int | None = None
99
+ self.filter_life: int | None = None
100
+ self.auto_preference_type: str | None = PurifierAutoPreference.UNKNOWN
101
+ self.auto_room_size: int | None = None
102
+ self._air_quality_level: AirQualityLevel = AirQualityLevel.UNKNOWN
103
+ self.child_lock: bool = False
104
+ self.filter_open_state: bool = False
105
+ self.display_status: str = DeviceStatus.UNKNOWN
106
+ self.display_set_state: str = DeviceStatus.UNKNOWN
107
+ self.display_forever: bool = False
108
+ # Attributes not supported by all purifiers
109
+ # When update is first called, devices that support these attributes will
110
+ # update them from NOT_SUPPORTED, whether to None or another value.
111
+ self.pm25: int | None = IntFlag.NOT_SUPPORTED
112
+ self.pm1: int | None = IntFlag.NOT_SUPPORTED
113
+ self.pm10: int | None = IntFlag.NOT_SUPPORTED
114
+ self.aq_percent: int | None = IntFlag.NOT_SUPPORTED
115
+ self.light_detection_switch: str = StrFlag.NOT_SUPPORTED
116
+ self.light_detection_status: str = StrFlag.NOT_SUPPORTED
117
+ self.nightlight_status: str = StrFlag.NOT_SUPPORTED
118
+ self.fan_rotate_angle: int | None = IntFlag.NOT_SUPPORTED
119
+
120
+ @property
121
+ def air_quality_level(self) -> int:
122
+ """Return air quality level in integer from 1-4.
123
+
124
+ Returns -1 if unknown.
125
+ """
126
+ if self._air_quality_level is None:
127
+ return -1
128
+ return int(self._air_quality_level)
129
+
130
+ @air_quality_level.setter
131
+ def air_quality_level(self, value: int | None) -> None:
132
+ """Set air quality level."""
133
+ if isinstance(value, str):
134
+ self._air_quality_level = AirQualityLevel.from_int(value)
135
+
136
+ def set_air_quality_level(self, value: int | str | None) -> None:
137
+ """Set air quality level."""
138
+ if isinstance(value, str):
139
+ self._air_quality_level = AirQualityLevel.from_string(value)
140
+ elif isinstance(value, int):
141
+ self._air_quality_level = AirQualityLevel.from_int(value)
142
+
143
+ @property
144
+ def air_quality_string(self) -> str:
145
+ """Return air quality level as string."""
146
+ return str(self._air_quality_level)
147
+
148
+ @property
149
+ @deprecated("Use state.air_quality_level instead.")
150
+ def air_quality(self) -> int | str | None:
151
+ """Return air quality level."""
152
+ return self.air_quality_level
153
+
154
+ @property
155
+ @deprecated("Use light_detection_switch instead.")
156
+ def light_detection(self) -> bool:
157
+ """Return light detection status."""
158
+ return self.light_detection_switch == DeviceStatus.ON
159
+
160
+ @property
161
+ @deprecated("Use state.pm25 instead.")
162
+ def air_quality_value(self) -> int | None:
163
+ """Return air quality value."""
164
+ return self.pm25
165
+
166
+ @property
167
+ @deprecated("Use PurifierState.fan_level instead.")
168
+ def speed(self) -> int | None:
169
+ """Return fan speed."""
170
+ return self.fan_level
171
+
172
+ @property
173
+ @deprecated("Use PurifierState.nightlight_status instead.")
174
+ def night_light(self) -> str:
175
+ """Return night light status."""
176
+ return self.nightlight_status
177
+
178
+ @property
179
+ @deprecated("Use display_status instead.")
180
+ def display_state(self) -> str:
181
+ """Return display status."""
182
+ return self.display_status
183
+
184
+ @property
185
+ @deprecated("Use display_set_state instead.")
186
+ def display_switch(self) -> str:
187
+ """Return display switch status."""
188
+ return self.display_set_state
189
+
190
+
191
+ class VeSyncPurifier(VeSyncBaseToggleDevice):
192
+ """Base device for vesync air purifiers.
193
+
194
+ Args:
195
+ details (ResponseDeviceDetailsModel): Device details from API.
196
+ manager (VeSync): VeSync manager instance.
197
+ feature_map (PurifierMap): Feature map for the device.
198
+
199
+ Attributes:
200
+ state (PurifierState): State of the device.
201
+ last_response (ResponseInfo): Last response from API call.
202
+ manager (VeSync): Manager object for API calls.
203
+ device_name (str): Name of device.
204
+ device_image (str): URL for device image.
205
+ cid (str): Device ID.
206
+ connection_type (str): Connection type of device.
207
+ device_type (str): Type of device.
208
+ type (str): Type of device.
209
+ uuid (str): UUID of device, not always present.
210
+ config_module (str): Configuration module of device.
211
+ mac_id (str): MAC ID of device.
212
+ current_firm_version (str): Current firmware version of device.
213
+ device_region (str): Region of device. (US, EU, etc.)
214
+ pid (str): Product ID of device, pulled by some devices on update.
215
+ sub_device_no (int): Sub-device number of device.
216
+ product_type (str): Product type of device.
217
+ features (dict): Features of device.
218
+ modes (list[str]): List of modes supported by the device.
219
+ fan_levels (list[int]): List of fan levels supported by the device.
220
+ nightlight_modes (list[str]): List of nightlight modes supported by the device.
221
+ auto_preferences (list[str]): List of auto preferences supported by the device.
222
+ """
223
+
224
+ __slots__ = (
225
+ "auto_preferences",
226
+ "fan_levels",
227
+ "modes",
228
+ "nightlight_modes"
229
+ )
230
+
231
+ def __init__(
232
+ self,
233
+ details: ResponseDeviceDetailsModel,
234
+ manager: VeSync,
235
+ feature_map: PurifierMap,
236
+ ) -> None:
237
+ """Initialize VeSync Purifier Base Class."""
238
+ super().__init__(details, manager, feature_map)
239
+ self.features: list[str] = feature_map.features
240
+ self.state: PurifierState = PurifierState(self, details, feature_map)
241
+ self.modes: list[str] = feature_map.modes
242
+ self.fan_levels: list[int] = feature_map.fan_levels
243
+ self.nightlight_modes: list[str] = feature_map.nightlight_modes
244
+ self.auto_preferences: list[str] = feature_map.auto_preferences
245
+
246
+ @property
247
+ def supports_air_quality(self) -> bool:
248
+ """Return True if device supports air quality."""
249
+ return PurifierFeatures.AIR_QUALITY in self.features
250
+
251
+ @property
252
+ def supports_fan_rotate(self) -> bool:
253
+ """Return True if device supports fan rotation."""
254
+ return PurifierFeatures.VENT_ANGLE in self.features
255
+
256
+ @property
257
+ def supports_nightlight(self) -> bool:
258
+ """Return True if device supports nightlight."""
259
+ return PurifierFeatures.NIGHTLIGHT in self.features
260
+
261
+ @property
262
+ def supports_light_detection(self) -> bool:
263
+ """Returns True if device supports light detection."""
264
+ return PurifierFeatures.LIGHT_DETECT in self.features
265
+
266
+ async def toggle_display(self, mode: bool) -> bool:
267
+ """Set Display Mode."""
268
+ del mode
269
+ raise NotImplementedError
270
+
271
+ async def turn_on_display(self) -> bool:
272
+ """Turn on Display."""
273
+ return await self.toggle_display(True)
274
+
275
+ async def turn_off_display(self) -> bool:
276
+ """Turn off Display."""
277
+ return await self.toggle_display(False)
278
+
279
+ async def set_nightlight_mode(self, mode: str) -> bool:
280
+ """Set Nightlight Mode.
281
+
282
+ Modes are defined in the `device.nightlight_modes` attribute.
283
+
284
+ Args:
285
+ mode (str): Nightlight mode to set.
286
+
287
+ Returns:
288
+ bool: True if successful, False otherwise.
289
+ """
290
+ del mode
291
+ return False
292
+
293
+ async def set_nightlight_dim(self) -> bool:
294
+ """Set Nightlight Dim."""
295
+ return await self.set_nightlight_mode(NightlightModes.DIM)
296
+
297
+ async def turn_on_nightlight(self) -> bool:
298
+ """Turn on Nightlight."""
299
+ return await self.set_nightlight_mode(NightlightModes.ON)
300
+
301
+ async def turn_off_nightlight(self) -> bool:
302
+ """Turn off Nightlight."""
303
+ return await self.set_nightlight_mode(NightlightModes.OFF)
304
+
305
+ async def toggle_child_lock(self, toggle: bool | None = None) -> bool:
306
+ """Toggle Child Lock (Display Lock).
307
+
308
+ Args:
309
+ toggle (bool | None): Toggle child lock. If None, toggle state.
310
+ """
311
+ del toggle
312
+ logger.debug("Child lock not configured for this device.")
313
+ return False
314
+
315
+ async def turn_on_child_lock(self) -> bool:
316
+ """Set child lock (display lock) to ON."""
317
+ return await self.toggle_child_lock(True)
318
+
319
+ async def turn_off_child_lock(self) -> bool:
320
+ """Set child lock (display lock) to OFF."""
321
+ return await self.toggle_child_lock(False)
322
+
323
+ @abstractmethod
324
+ async def set_mode(self, mode: str) -> bool:
325
+ """Set Purifier Mode.
326
+
327
+ Allowed modes are found in the `device.modes` attribute.
328
+
329
+ Args:
330
+ mode (str): Mode to set.
331
+
332
+ Returns:
333
+ bool: True if successful, False otherwise.
334
+ """
335
+
336
+ @abstractmethod
337
+ async def set_fan_speed(self, speed: int | None = None) -> bool:
338
+ """Set Purifier Fan Speed.
339
+
340
+ Args:
341
+ speed (int | None): Fan speed to set. If None, use default speed.
342
+
343
+ Returns:
344
+ bool: True if successful, False otherwise.
345
+ """
346
+
347
+ async def set_auto_mode(self) -> bool:
348
+ """Set Purifier to Auto Mode."""
349
+ if PurifierModes.AUTO in self.modes:
350
+ return await self.set_mode(PurifierModes.AUTO)
351
+ logger.error("Auto mode not supported for this device.")
352
+ return False
353
+
354
+ async def set_sleep_mode(self) -> bool:
355
+ """Set Purifier to Sleep Mode."""
356
+ if PurifierModes.SLEEP in self.modes:
357
+ return await self.set_mode(PurifierModes.SLEEP)
358
+ logger.error("Sleep mode not supported for this device.")
359
+ return False
360
+
361
+ async def set_manual_mode(self) -> bool:
362
+ """Set Purifier to Manual Mode."""
363
+ if PurifierModes.MANUAL in self.modes:
364
+ return await self.set_mode(PurifierModes.MANUAL)
365
+ logger.error("Manual mode not supported for this device.")
366
+ return False
367
+
368
+ async def set_turbo_mode(self) -> bool:
369
+ """Set Purifier to Turbo Mode."""
370
+ if PurifierModes.TURBO in self.modes:
371
+ return await self.set_mode(PurifierModes.TURBO)
372
+ logger.error("Turbo mode not supported for this device.")
373
+ return False
374
+
375
+ async def set_pet_mode(self) -> bool:
376
+ """Set Purifier to Pet Mode."""
377
+ if PurifierModes.PET in self.modes:
378
+ return await self.set_mode(PurifierModes.PET)
379
+ logger.error("Pet mode not supported for this device.")
380
+ return False
381
+
382
+ async def set_auto_preference(self, preference: str, room_size: int = 800) -> bool:
383
+ """Set auto preference.
384
+
385
+ Args:
386
+ preference (str): Auto preference to set, available preference is
387
+ found in `self.auto_preferences`.
388
+ room_size (int): Room size to set, defaults to 800ft2.
389
+
390
+ Returns:
391
+ bool: True if successful, False otherwise.
392
+ """
393
+ del preference, room_size
394
+ logger.debug("Auto preference not configured for this device.")
395
+ return False
396
+
397
+ async def toggle_light_detection(self, toggle: bool | None = None) -> bool:
398
+ """Set Light Detection Mode.
399
+
400
+ Args:
401
+ toggle (bool | None): Toggle light detection. If None, toggle state.
402
+
403
+ Returns:
404
+ bool: True if successful, False otherwise.
405
+ """
406
+ del toggle
407
+ if not self.supports_light_detection:
408
+ logger.debug("Light detection not supported for this device.")
409
+ else:
410
+ logger.debug("Light detection not configured for this device.")
411
+ return False
412
+
413
+ async def turn_on_light_detection(self) -> bool:
414
+ """Turn on Light Detection."""
415
+ return await self.toggle_light_detection(True)
416
+
417
+ async def turn_off_light_detection(self) -> bool:
418
+ """Turn off Light Detection."""
419
+ return await self.toggle_light_detection(False)
420
+
421
+ async def reset_filter(self) -> bool:
422
+ """Reset filter life."""
423
+ logger.debug("Filter life reset not configured for this device.")
424
+ return False
425
+
426
+ @deprecated("Use set_auto_mode instead.")
427
+ async def auto_mode(self) -> bool:
428
+ """Set Purifier to Auto Mode."""
429
+ return await self.set_auto_mode()
430
+
431
+ @deprecated("Use set_sleep_mode instead.")
432
+ async def sleep_mode(self) -> bool:
433
+ """Set Purifier to Sleep Mode."""
434
+ return await self.set_sleep_mode()
435
+
436
+ @deprecated("Use set_manual_mode instead.")
437
+ async def manual_mode(self) -> bool:
438
+ """Set Purifier to Manual Mode."""
439
+ return await self.set_manual_mode()
440
+
441
+ @deprecated("Use set_turbo_mode instead.")
442
+ async def turbo_mode(self) -> bool:
443
+ """Set Purifier to Turbo Mode."""
444
+ return await self.set_turbo_mode()
445
+
446
+ @deprecated("Use set_pet_mode instead.")
447
+ async def pet_mode(self) -> bool:
448
+ """Set Purifier to Pet Mode."""
449
+ return await self.set_pet_mode()
450
+
451
+ @deprecated("Use set_nightlight_mode instead.")
452
+ async def nightlight_mode(self, mode: str) -> bool:
453
+ """Set Nightlight Mode."""
454
+ return await self.set_nightlight_mode(mode)
455
+
456
+ @deprecated("Use `set_fan_speed()` instead.")
457
+ async def change_fan_speed(self, speed: int | None = None) -> bool:
458
+ """Deprecated method for changing fan speed."""
459
+ return await self.set_fan_speed(speed)
460
+
461
+ @deprecated("Use `set_mode(mode: str)` instead.")
462
+ async def change_mode(self, mode: str) -> bool:
463
+ """Deprecated method for changing purifier mode."""
464
+ return await self.set_mode(mode)
465
+
466
+ @deprecated("Use `toggle_child_lock()` instead.")
467
+ async def set_child_lock(self, toggle: bool) -> bool:
468
+ """Set child lock (display lock).
469
+
470
+ This has been deprecated in favor of `toggle_child_lock()`.
471
+ """
472
+ return await self.toggle_child_lock(toggle)
473
+
474
+ @deprecated("Use `turn_on_child_lock()` instead.")
475
+ async def child_lock_on(self) -> bool:
476
+ """Turn on child lock (display lock).
477
+
478
+ This has been deprecated, use `turn_on_child_lock()` instead.
479
+ """
480
+ return await self.toggle_child_lock(True)
481
+
482
+ @deprecated("Use `turn_off_child_lock()` instead.")
483
+ async def child_lock_off(self) -> bool:
484
+ """Turn off child lock (display lock).
485
+
486
+ This has been deprecated, use `turn_off_child_lock()` instead.
487
+ """
488
+ return await self.toggle_child_lock(False)
489
+
490
+ @property
491
+ @deprecated("Use self.state.child_lock instead.")
492
+ def child_lock(self) -> bool:
493
+ """Get child lock state.
494
+
495
+ Returns:
496
+ bool : True if child lock is enabled, False if not.
497
+ """
498
+ return self.state.child_lock
499
+
500
+ @property
501
+ @deprecated("Use self.state.nightlight_status instead.")
502
+ def night_light(self) -> str:
503
+ """Get night light state.
504
+
505
+ Returns:
506
+ str : Night light state (on, dim, off)
507
+ """
508
+ return self.state.nightlight_status
509
+
510
+ @deprecated("Use turn_on_light_detection() instead.")
511
+ async def set_light_detection_on(self) -> bool:
512
+ """Turn on light detection feature."""
513
+ return await self.toggle_light_detection(True)
514
+
515
+ @deprecated("Use turn_off_light_detection() instead.")
516
+ async def set_light_detection_off(self) -> bool:
517
+ """Turn off light detection feature."""
518
+ return await self.toggle_light_detection(False)