pyvesync 3.3.3__tar.gz → 3.4.0__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.
Files changed (60) hide show
  1. {pyvesync-3.3.3 → pyvesync-3.4.0}/MANIFEST.in +2 -1
  2. {pyvesync-3.3.3/src/pyvesync.egg-info → pyvesync-3.4.0}/PKG-INFO +3 -3
  3. {pyvesync-3.3.3 → pyvesync-3.4.0}/README.md +2 -2
  4. {pyvesync-3.3.3 → pyvesync-3.4.0}/pyproject.toml +4 -1
  5. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/fan_base.py +12 -8
  6. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/humidifier_base.py +92 -5
  7. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/outlet_base.py +6 -6
  8. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/const.py +26 -2
  9. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/device_container.py +26 -36
  10. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/device_map.py +61 -21
  11. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/vesyncbulb.py +9 -8
  12. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/vesyncfan.py +5 -9
  13. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/vesynchumidifier.py +577 -32
  14. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/vesynckitchen.py +12 -11
  15. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/vesyncoutlet.py +29 -13
  16. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/bypass_models.py +26 -0
  17. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/humidifier_models.py +104 -2
  18. pyvesync-3.4.0/src/pyvesync/py.typed +1 -0
  19. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/utils/device_mixins.py +12 -26
  20. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/utils/errors.py +9 -3
  21. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/utils/helpers.py +69 -26
  22. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/utils/logs.py +24 -13
  23. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/vesync.py +32 -30
  24. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/vesynchome.py +6 -7
  25. {pyvesync-3.3.3 → pyvesync-3.4.0/src/pyvesync.egg-info}/PKG-INFO +3 -3
  26. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync.egg-info/SOURCES.txt +1 -0
  27. {pyvesync-3.3.3 → pyvesync-3.4.0}/LICENSE +0 -0
  28. {pyvesync-3.3.3 → pyvesync-3.4.0}/requirements.txt +0 -0
  29. {pyvesync-3.3.3 → pyvesync-3.4.0}/setup.cfg +0 -0
  30. {pyvesync-3.3.3 → pyvesync-3.4.0}/setup.py +0 -0
  31. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/__init__.py +0 -0
  32. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/auth.py +0 -0
  33. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/__init__.py +0 -0
  34. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/bulb_base.py +0 -0
  35. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/fryer_base.py +0 -0
  36. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/purifier_base.py +0 -0
  37. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/switch_base.py +0 -0
  38. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/thermostat_base.py +0 -0
  39. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/base_devices/vesyncbasedevice.py +0 -0
  40. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/__init__.py +0 -0
  41. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/vesyncpurifier.py +0 -0
  42. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/vesyncswitch.py +0 -0
  43. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/devices/vesyncthermostat.py +0 -0
  44. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/__init__.py +0 -0
  45. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/base_models.py +0 -0
  46. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/bulb_models.py +0 -0
  47. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/fan_models.py +0 -0
  48. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/fryer_models.py +0 -0
  49. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/home_models.py +0 -0
  50. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/outlet_models.py +0 -0
  51. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/purifier_models.py +0 -0
  52. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/switch_models.py +0 -0
  53. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/thermostat_models.py +0 -0
  54. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/models/vesync_models.py +0 -0
  55. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/utils/__init__.py +0 -0
  56. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/utils/colors.py +0 -0
  57. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync/utils/enum_utils.py +0 -0
  58. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync.egg-info/dependency_links.txt +0 -0
  59. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync.egg-info/requires.txt +0 -0
  60. {pyvesync-3.3.3 → pyvesync-3.4.0}/src/pyvesync.egg-info/top_level.txt +0 -0
@@ -1,3 +1,4 @@
1
1
  include README.md LICENSE requirements.txt
2
2
  exclude test
3
- prune test
3
+ prune test
4
+ include src/pyvesync/py.typed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyvesync
3
- Version: 3.3.3
3
+ Version: 3.4.0
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>
@@ -216,9 +216,9 @@ See [Supported Devices](#supported-devices) for a complete list of supported dev
216
216
 
217
217
  Exceptions are no longer caught by the library and must be handled by the user. Exceptions are raised by server errors and aiohttp connection errors.
218
218
 
219
- Errors that occur at the aiohttp level are raised automatically and propogated to the user. That means exceptions raised by aiohttp that inherit from `aiohttp.ClientError` are propogated.
219
+ Errors that occur at the aiohttp level are raised automatically and propagated to the user. That means exceptions raised by aiohttp that inherit from `aiohttp.ClientError` are propagated.
220
220
 
221
- When the connection to the VeSync API succeeds but returns an error code that prevents the library from functioning a custom exception inherrited from `pyvesync.logs.VeSyncError` is raised.
221
+ When the connection to the VeSync API succeeds but returns an error code that prevents the library from functioning a custom exception inherited from `pyvesync.logs.VeSyncError` is raised.
222
222
 
223
223
  Custom Exceptions raised by all API calls:
224
224
 
@@ -168,9 +168,9 @@ See [Supported Devices](#supported-devices) for a complete list of supported dev
168
168
 
169
169
  Exceptions are no longer caught by the library and must be handled by the user. Exceptions are raised by server errors and aiohttp connection errors.
170
170
 
171
- Errors that occur at the aiohttp level are raised automatically and propogated to the user. That means exceptions raised by aiohttp that inherit from `aiohttp.ClientError` are propogated.
171
+ Errors that occur at the aiohttp level are raised automatically and propagated to the user. That means exceptions raised by aiohttp that inherit from `aiohttp.ClientError` are propagated.
172
172
 
173
- When the connection to the VeSync API succeeds but returns an error code that prevents the library from functioning a custom exception inherrited from `pyvesync.logs.VeSyncError` is raised.
173
+ When the connection to the VeSync API succeeds but returns an error code that prevents the library from functioning a custom exception inherited from `pyvesync.logs.VeSyncError` is raised.
174
174
 
175
175
  Custom Exceptions raised by all API calls:
176
176
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyvesync"
7
- version = "3.3.3"
7
+ version = "3.4.0"
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"
@@ -64,6 +64,9 @@ docs = [
64
64
  where = ["src"]
65
65
  exclude = ["tests", "test*"]
66
66
 
67
+ [tool.setuptools.package-data]
68
+ pyvesync = ["py.typed"]
69
+
67
70
  [tool.pytest.ini_options]
68
71
  addopts = "--tb=short --color=yes"
69
72
  testpaths = ["src/tests"]
@@ -87,7 +87,7 @@ class FanState(DeviceState):
87
87
  feature_map (FanMap): Feature map.
88
88
  """
89
89
  super().__init__(device, details, feature_map)
90
- self.mode: str = FanModes.UNKNOWN
90
+ self.mode: str | None = None
91
91
  self.fan_level: int | None = None
92
92
  self.fan_set_level: int | None = None
93
93
  self.child_lock: str | None = None
@@ -145,6 +145,7 @@ class VeSyncFanBase(VeSyncBaseToggleDevice):
145
145
  """
146
146
 
147
147
  __slots__ = (
148
+ '_reverse_modes',
148
149
  'fan_levels',
149
150
  'modes',
150
151
  'sleep_preferences',
@@ -169,7 +170,10 @@ class VeSyncFanBase(VeSyncBaseToggleDevice):
169
170
  super().__init__(details, manager, feature_map)
170
171
  self.features: list[str] = feature_map.features
171
172
  self.state: FanState = FanState(self, details, feature_map)
172
- self.modes: list[str] = feature_map.modes
173
+ self.modes: dict[str, str] = feature_map.modes
174
+ self._reverse_modes: dict[str, str] = {}
175
+ for k, v in self.modes.items():
176
+ self._reverse_modes[v] = k
173
177
  self.fan_levels: list[int] = feature_map.fan_levels
174
178
  self.sleep_preferences: list[str] = feature_map.sleep_preferences
175
179
 
@@ -278,9 +282,9 @@ class VeSyncFanBase(VeSyncBaseToggleDevice):
278
282
  This method is not supported by all devices, will return
279
283
  false with warning debug message if not supported.
280
284
  """
281
- if FanModes.ADVANCED_SLEEP in self.modes:
282
- return await self.set_mode(FanModes.ADVANCED_SLEEP)
283
- logger.warning('Advanced Sleep mode not supported for this device.')
285
+ if FanModes.SLEEP in self.modes:
286
+ return await self.set_mode(FanModes.SLEEP)
287
+ logger.warning('Sleep mode not supported for this device.')
284
288
  return False
285
289
 
286
290
  async def set_sleep_mode(self) -> bool:
@@ -295,8 +299,8 @@ class VeSyncFanBase(VeSyncBaseToggleDevice):
295
299
  This method is not supported by all devices, will return
296
300
  false with warning debug message if not supported.
297
301
  """
298
- if FanModes.ADVANCED_SLEEP in self.modes:
299
- return await self.set_mode(FanModes.ADVANCED_SLEEP)
302
+ if FanModes.SLEEP in self.modes:
303
+ return await self.set_mode(FanModes.SLEEP)
300
304
  logger.warning('Sleep mode not supported for this device.')
301
305
  return False
302
306
 
@@ -510,7 +514,7 @@ class VeSyncFanBase(VeSyncBaseToggleDevice):
510
514
  @deprecated('Use `set_advanced_sleep_mode` method instead')
511
515
  async def advanced_sleep_mode(self) -> bool:
512
516
  """Set advanced sleep mode."""
513
- return await self.set_mode('advancedSleep')
517
+ return await self.set_mode(FanModes.SLEEP)
514
518
 
515
519
  @deprecated('Use `set_sleep_mode` method instead')
516
520
  async def sleep_mode(self) -> bool:
@@ -4,14 +4,17 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
  from abc import abstractmethod
7
+ from dataclasses import dataclass
7
8
  from typing import TYPE_CHECKING
8
9
 
9
10
  from pyvesync.base_devices.vesyncbasedevice import DeviceState, VeSyncBaseToggleDevice
10
- from pyvesync.const import DeviceStatus, HumidifierFeatures, HumidifierModes
11
+ from pyvesync.const import DeviceStatus, DryingModes, HumidifierFeatures, HumidifierModes
12
+ from pyvesync.models.bypass_models import ResponseBaseModel
11
13
 
12
14
  if TYPE_CHECKING:
13
15
  from pyvesync import VeSync
14
16
  from pyvesync.device_map import HumidifierMap
17
+ from pyvesync.models.humidifier_models import SproutBreathinglamp
15
18
  from pyvesync.models.vesync_models import ResponseDeviceDetailsModel
16
19
 
17
20
 
@@ -55,6 +58,7 @@ class HumidifierState(DeviceState):
55
58
  'auto_stop_target_reached',
56
59
  'auto_target_humidity',
57
60
  'automatic_stop_config',
61
+ 'breathing_lamp',
58
62
  'child_lock',
59
63
  'display_set_status',
60
64
  'display_status',
@@ -63,17 +67,20 @@ class HumidifierState(DeviceState):
63
67
  'drying_mode_status',
64
68
  'drying_mode_time_remain',
65
69
  'filter_life',
70
+ 'hepa_filter_life',
66
71
  'humidity',
67
72
  'humidity_high',
68
73
  'mist_level',
69
74
  'mist_virtual_level',
70
75
  'mode',
71
76
  'nightlight_brightness',
77
+ 'nightlight_color_temp',
72
78
  'nightlight_status',
73
79
  'temperature',
74
80
  'warm_mist_enabled',
75
81
  'warm_mist_level',
76
82
  'water_lacks',
83
+ 'water_lacks_drying_switch',
77
84
  'water_tank_lifted',
78
85
  )
79
86
 
@@ -105,19 +112,23 @@ class HumidifierState(DeviceState):
105
112
  self.mode: str | None = None
106
113
  self.nightlight_brightness: int | None = None
107
114
  self.nightlight_status: str | None = None
115
+ self.nightlight_color_temp: int | None = None
108
116
  self.warm_mist_enabled: bool | None = None
109
117
  self.warm_mist_level: int | None = None
110
118
  self.water_lacks: bool = False
111
119
  self.water_tank_lifted: bool = False
112
120
  self.child_lock: bool | None = None
113
121
  self.temperature: float | None = None # Fahrenheit
114
- # Superior 6000S States
122
+ # Superior 6000S States / Sprout
115
123
  self.auto_preference: int | None = None
116
124
  self.filter_life: int | None = None
117
125
  self.drying_mode_level: int | None = None
118
126
  self.drying_mode_auto_switch: str | None = None
119
- self.drying_mode_status: str | None = None
127
+ self.drying_mode_status: DryingModes | None = None
120
128
  self.drying_mode_time_remain: int | None = None
129
+ self.water_lacks_drying_switch: str | None = None
130
+ self.hepa_filter_life: int | None = None
131
+ self.breathing_lamp: BreathingLampState | None = None
121
132
 
122
133
  @property
123
134
  def automatic_stop(self) -> bool:
@@ -170,13 +181,24 @@ class HumidifierState(DeviceState):
170
181
  return self.drying_mode_time_remain
171
182
 
172
183
  @property
173
- def drying_mode_enabled(self) -> bool:
184
+ def drying_mode_running(self) -> bool:
174
185
  """Return True if drying mode is enabled.
175
186
 
176
187
  Returns:
177
188
  bool | None: True if drying mode is enabled, False otherwise.
178
189
  """
179
- return self.drying_mode_status == DeviceStatus.ON
190
+ return self.drying_mode_status == DryingModes.RUNNING
191
+
192
+ @property
193
+ def breathing_lamp_status(self) -> str | None:
194
+ """Return the breathing lamp status.
195
+
196
+ Returns:
197
+ str | None: Breathing lamp status.
198
+ """
199
+ if self.breathing_lamp is not None:
200
+ return self.breathing_lamp.breathing_lamp_switch
201
+ return None
180
202
 
181
203
 
182
204
  class VeSyncHumidifier(VeSyncBaseToggleDevice):
@@ -213,6 +235,7 @@ class VeSyncHumidifier(VeSyncBaseToggleDevice):
213
235
  """
214
236
 
215
237
  __slots__ = (
238
+ '_reverse_mist_modes',
216
239
  'mist_levels',
217
240
  'mist_modes',
218
241
  'target_minmax',
@@ -235,6 +258,9 @@ class VeSyncHumidifier(VeSyncBaseToggleDevice):
235
258
  super().__init__(details, manager, feature_map)
236
259
  self.state: HumidifierState = HumidifierState(self, details, feature_map)
237
260
  self.mist_modes: dict[str, str] = feature_map.mist_modes
261
+ self._reverse_mist_modes: dict[str, str] = {}
262
+ for k, v in self.mist_modes.items():
263
+ self._reverse_mist_modes[v] = k
238
264
  self.mist_levels: list[str | int] = feature_map.mist_levels
239
265
  self.features: list[str] = feature_map.features
240
266
  self.warm_mist_levels: list[int | str] = feature_map.warm_mist_levels
@@ -278,6 +304,9 @@ class VeSyncHumidifier(VeSyncBaseToggleDevice):
278
304
  bool: Success of request.
279
305
  """
280
306
  del toggle
307
+ if HumidifierFeatures.AUTO_STOP in self.features:
308
+ logger.warning('Automatic stop has not been configured for this device.')
309
+ return False
281
310
  logger.warning('Automatic stop is not supported or configured for this device.')
282
311
  return False
283
312
 
@@ -480,3 +509,61 @@ class VeSyncHumidifier(VeSyncBaseToggleDevice):
480
509
  async def turn_off_drying_mode(self) -> bool:
481
510
  """Turn off drying mode."""
482
511
  return await self.toggle_drying_mode(False)
512
+
513
+ async def toggle_child_lock(self, toggle: bool | None = None) -> bool:
514
+ """Toggle child lock on/off.
515
+
516
+ Args:
517
+ toggle (bool): True to turn on child lock, False to turn off.
518
+
519
+ Returns:
520
+ bool: Success of request.
521
+ """
522
+ del toggle
523
+ logger.error('Child lock is not supported or configured for this device.')
524
+ return False
525
+
526
+ async def turn_on_child_lock(self) -> bool:
527
+ """Turn on child lock.
528
+
529
+ Returns:
530
+ bool: Success of request.
531
+ """
532
+ return await self.toggle_child_lock(True)
533
+
534
+ async def turn_off_child_lock(self) -> bool:
535
+ """Turn off child lock.
536
+
537
+ Returns:
538
+ bool: Success of request.
539
+ """
540
+ return await self.toggle_child_lock(False)
541
+
542
+
543
+ @dataclass
544
+ class BreathingLampState(ResponseBaseModel):
545
+ """Breathing Lamp State Object."""
546
+
547
+ breathing_lamp_switch: str
548
+ color_temperature: int
549
+ time_interval: int
550
+ brightness_start: int
551
+ brightness_end: int
552
+
553
+ @classmethod
554
+ def from_model(cls, model: SproutBreathinglamp) -> BreathingLampState:
555
+ """Create BreathingLampState from SproutBreathinglamp model.
556
+
557
+ Args:
558
+ model (SproutBreathinglamp): The SproutBreathinglamp model.
559
+
560
+ Returns:
561
+ BreathingLampState: The created BreathingLampState object.
562
+ """
563
+ return cls(
564
+ breathing_lamp_switch=DeviceStatus.from_int(model.breathingLampSwitch),
565
+ color_temperature=model.colorTemperature,
566
+ time_interval=model.timeInterval,
567
+ brightness_start=model.brightnessStart,
568
+ brightness_end=model.brightnessEnd,
569
+ )
@@ -12,7 +12,6 @@ from pyvesync.const import (
12
12
  NightlightModes,
13
13
  OutletFeatures,
14
14
  )
15
- from pyvesync.models.base_models import DefaultValues
16
15
  from pyvesync.models.outlet_models import RequestEnergyHistory, ResponseEnergyHistory
17
16
  from pyvesync.utils.helpers import Helpers
18
17
 
@@ -192,7 +191,7 @@ class VeSyncOutlet(VeSyncBaseToggleDevice):
192
191
 
193
192
  def _build_energy_request(self, method: str) -> RequestEnergyHistory:
194
193
  """Build energy request post."""
195
- request_keys = [
194
+ request_keys = (
196
195
  'acceptLanguage',
197
196
  'accountID',
198
197
  'appVersion',
@@ -205,10 +204,11 @@ class VeSyncOutlet(VeSyncBaseToggleDevice):
205
204
  'debugMode',
206
205
  'homeTimeZone',
207
206
  'uuid',
208
- ]
209
- body = Helpers.get_class_attributes(DefaultValues, request_keys)
210
- body.update(Helpers.get_class_attributes(self.manager, request_keys))
211
- body.update(Helpers.get_class_attributes(self, request_keys))
207
+ )
208
+
209
+ body = Helpers.get_defaultvalues_attributes(request_keys)
210
+ body.update(Helpers.get_manager_attributes(self.manager, request_keys))
211
+ body.update(Helpers.get_device_attributes(self, request_keys))
212
212
  body['method'] = method
213
213
  return RequestEnergyHistory.from_dict(body)
214
214
 
@@ -673,12 +673,11 @@ class FanModes(StrEnum):
673
673
  AUTO = 'auto'
674
674
  NORMAL = 'normal'
675
675
  MANUAL = 'normal'
676
- SLEEP = 'advancedSleep'
676
+ SLEEP = 'sleep'
677
677
  TURBO = 'turbo'
678
678
  PET = 'pet'
679
679
  ECO = 'eco'
680
680
  UNKNOWN = 'unknown'
681
- ADVANCED_SLEEP = 'advancedSleep'
682
681
 
683
682
 
684
683
  # Air Fryer Constants
@@ -887,6 +886,31 @@ class DryingModes(StrEnum):
887
886
  RUNNING = 'on'
888
887
  PAUSE = 'pause'
889
888
 
889
+ @classmethod
890
+ def from_int(cls, value: int) -> DryingModes | None:
891
+ """Convert integer value to corresponding string.
892
+
893
+ If value is 1, return ON and if 0, return OFF.
894
+ If value is -999, return NOT_SUPPORTED.
895
+ """
896
+ if value == 0:
897
+ return cls.DONE
898
+ if value == 1:
899
+ return cls.RUNNING
900
+ if value == 2: # noqa: PLR2004
901
+ return cls.PAUSE
902
+ return None
903
+
904
+ def __int__(self) -> int:
905
+ """Return integer representation of the enum."""
906
+ match self:
907
+ case DryingModes.DONE:
908
+ return 0
909
+ case DryingModes.RUNNING:
910
+ return 1
911
+ case DryingModes.PAUSE:
912
+ return 2
913
+
890
914
 
891
915
  DRYING_MODES: dict[str, int] = {
892
916
  DryingModes.DONE: 0,
@@ -17,22 +17,22 @@ from __future__ import annotations
17
17
  import logging
18
18
  import re
19
19
  from collections.abc import Iterator, MutableSet, Sequence
20
- from typing import TYPE_CHECKING, TypeVar
21
-
22
- from pyvesync.base_devices.bulb_base import VeSyncBulb
23
- from pyvesync.base_devices.fan_base import VeSyncFanBase
24
- from pyvesync.base_devices.fryer_base import VeSyncFryer
25
- from pyvesync.base_devices.humidifier_base import VeSyncHumidifier
26
- from pyvesync.base_devices.outlet_base import VeSyncOutlet
27
- from pyvesync.base_devices.purifier_base import VeSyncPurifier
28
- from pyvesync.base_devices.switch_base import VeSyncSwitch
29
- from pyvesync.base_devices.thermostat_base import VeSyncThermostat
20
+ from typing import TYPE_CHECKING, TypeVar, cast
21
+
30
22
  from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseDevice
31
23
  from pyvesync.const import ProductTypes
32
24
  from pyvesync.device_map import get_device_config
33
25
 
34
26
  if TYPE_CHECKING:
35
27
  from pyvesync import VeSync
28
+ from pyvesync.base_devices.bulb_base import VeSyncBulb
29
+ from pyvesync.base_devices.fan_base import VeSyncFanBase
30
+ from pyvesync.base_devices.fryer_base import VeSyncFryer
31
+ from pyvesync.base_devices.humidifier_base import VeSyncHumidifier
32
+ from pyvesync.base_devices.outlet_base import VeSyncOutlet
33
+ from pyvesync.base_devices.purifier_base import VeSyncPurifier
34
+ from pyvesync.base_devices.switch_base import VeSyncSwitch
35
+ from pyvesync.base_devices.thermostat_base import VeSyncThermostat
36
36
  from pyvesync.models.vesync_models import (
37
37
  ResponseDeviceDetailsModel,
38
38
  ResponseDeviceListModel,
@@ -291,82 +291,72 @@ class DeviceContainer(_DeviceContainerBase):
291
291
  def outlets(self) -> list[VeSyncOutlet]:
292
292
  """Return a list of devices that are outlets."""
293
293
  return [
294
- device
294
+ cast('VeSyncOutlet', device)
295
295
  for device in self
296
- if isinstance(device, VeSyncOutlet)
297
- and device.product_type == ProductTypes.OUTLET
296
+ if device.product_type == ProductTypes.OUTLET
298
297
  ]
299
298
 
300
299
  @property
301
300
  def switches(self) -> list[VeSyncSwitch]:
302
301
  """Return a list of devices that are switches."""
303
302
  return [
304
- device
303
+ cast('VeSyncSwitch', device)
305
304
  for device in self
306
- if (
307
- isinstance(device, VeSyncSwitch)
308
- and device.product_type == ProductTypes.SWITCH
309
- )
305
+ if device.product_type == ProductTypes.SWITCH
310
306
  ]
311
307
 
312
308
  @property
313
309
  def bulbs(self) -> list[VeSyncBulb]:
314
310
  """Return a list of devices that are lights."""
315
311
  return [
316
- device
312
+ cast('VeSyncBulb', device)
317
313
  for device in self
318
- if isinstance(device, VeSyncBulb)
319
- and (device.product_type == ProductTypes.BULB)
314
+ if device.product_type == ProductTypes.BULB
320
315
  ]
321
316
 
322
317
  @property
323
318
  def air_purifiers(self) -> list[VeSyncPurifier]:
324
319
  """Return a list of devices that are air purifiers."""
325
320
  return [
326
- device
321
+ cast('VeSyncPurifier', device)
327
322
  for device in self
328
- if isinstance(device, VeSyncPurifier)
329
- and device.product_type == ProductTypes.PURIFIER
323
+ if device.product_type == ProductTypes.PURIFIER
330
324
  ]
331
325
 
332
326
  @property
333
327
  def fans(self) -> list[VeSyncFanBase]:
334
328
  """Return a list of devices that are fans."""
335
329
  return [
336
- device
330
+ cast('VeSyncFanBase', device)
337
331
  for device in self
338
- if isinstance(device, VeSyncFanBase)
339
- and device.product_type == ProductTypes.FAN
332
+ if device.product_type == ProductTypes.FAN
340
333
  ]
341
334
 
342
335
  @property
343
336
  def humidifiers(self) -> list[VeSyncHumidifier]:
344
337
  """Return a list of devices that are humidifiers."""
345
338
  return [
346
- device
339
+ cast('VeSyncHumidifier', device)
347
340
  for device in self
348
- if isinstance(device, VeSyncHumidifier)
349
- and device.product_type == ProductTypes.HUMIDIFIER
341
+ if device.product_type == ProductTypes.HUMIDIFIER
350
342
  ]
351
343
 
352
344
  @property
353
345
  def air_fryers(self) -> list[VeSyncFryer]:
354
346
  """Return a list of devices that are air fryers."""
355
347
  return [
356
- device
348
+ cast('VeSyncFryer', device)
357
349
  for device in self
358
- if isinstance(device, VeSyncFryer)
359
- and device.product_type == ProductTypes.AIR_FRYER
350
+ if device.product_type == ProductTypes.AIR_FRYER
360
351
  ]
361
352
 
362
353
  @property
363
354
  def thermostats(self) -> list[VeSyncThermostat]:
364
355
  """Return a list of devices that are thermostats."""
365
356
  return [
366
- device
357
+ cast('VeSyncThermostat', device)
367
358
  for device in self
368
- if isinstance(device, VeSyncThermostat)
369
- and device.product_type == ProductTypes.THERMOSTAT
359
+ if device.product_type == ProductTypes.THERMOSTAT
370
360
  ]
371
361
 
372
362