pyvesync 3.0.0__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 (50) hide show
  1. pyvesync/__init__.py +5 -0
  2. pyvesync/base_devices/__init__.py +35 -0
  3. pyvesync/base_devices/bulb_base.py +330 -0
  4. pyvesync/base_devices/fan_base.py +392 -0
  5. pyvesync/base_devices/fryer_base.py +68 -0
  6. pyvesync/base_devices/humidifier_base.py +462 -0
  7. pyvesync/base_devices/outlet_base.py +327 -0
  8. pyvesync/base_devices/purifier_base.py +525 -0
  9. pyvesync/base_devices/switch_base.py +271 -0
  10. pyvesync/base_devices/thermostat_base.py +283 -0
  11. pyvesync/base_devices/vesyncbasedevice.py +572 -0
  12. pyvesync/const.py +825 -0
  13. pyvesync/device_container.py +373 -0
  14. pyvesync/device_map.py +1172 -0
  15. pyvesync/devices/__init__.py +1 -0
  16. pyvesync/devices/vesyncbulb.py +1126 -0
  17. pyvesync/devices/vesyncfan.py +233 -0
  18. pyvesync/devices/vesynchumidifier.py +808 -0
  19. pyvesync/devices/vesynckitchen.py +651 -0
  20. pyvesync/devices/vesyncoutlet.py +954 -0
  21. pyvesync/devices/vesyncpurifier.py +990 -0
  22. pyvesync/devices/vesyncswitch.py +415 -0
  23. pyvesync/devices/vesyncthermostat.py +171 -0
  24. pyvesync/models/__init__.py +11 -0
  25. pyvesync/models/base_models.py +129 -0
  26. pyvesync/models/bulb_models.py +197 -0
  27. pyvesync/models/bypass_models.py +289 -0
  28. pyvesync/models/fan_models.py +47 -0
  29. pyvesync/models/fryer_models.py +34 -0
  30. pyvesync/models/home_models.py +169 -0
  31. pyvesync/models/humidifier_models.py +202 -0
  32. pyvesync/models/outlet_models.py +220 -0
  33. pyvesync/models/purifier_models.py +261 -0
  34. pyvesync/models/switch_models.py +101 -0
  35. pyvesync/models/thermostat_models.py +134 -0
  36. pyvesync/models/vesync_models.py +439 -0
  37. pyvesync/utils/__init__.py +1 -0
  38. pyvesync/utils/colors.py +250 -0
  39. pyvesync/utils/device_mixins.py +362 -0
  40. pyvesync/utils/enum_utils.py +26 -0
  41. pyvesync/utils/errors.py +867 -0
  42. pyvesync/utils/helpers.py +700 -0
  43. pyvesync/utils/logs.py +525 -0
  44. pyvesync/vesync.py +638 -0
  45. pyvesync/vesynchome.py +152 -0
  46. pyvesync-3.0.0.dist-info/METADATA +567 -0
  47. pyvesync-3.0.0.dist-info/RECORD +50 -0
  48. pyvesync-3.0.0.dist-info/WHEEL +5 -0
  49. pyvesync-3.0.0.dist-info/licenses/LICENSE +21 -0
  50. pyvesync-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,233 @@
1
+ """Module for VeSync Fans (Not Purifiers or Humidifiers)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ from pyvesync.base_devices import VeSyncFanBase
9
+ from pyvesync.const import ConnectionStatus, DeviceStatus
10
+ from pyvesync.models.bypass_models import TimerModels
11
+ from pyvesync.models.fan_models import TowerFanResult
12
+ from pyvesync.utils.device_mixins import BypassV2Mixin, process_bypassv2_result
13
+ from pyvesync.utils.helpers import Helpers, Timer
14
+
15
+ if TYPE_CHECKING:
16
+ from pyvesync import VeSync
17
+ from pyvesync.device_map import FanMap
18
+ from pyvesync.models.vesync_models import ResponseDeviceDetailsModel
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class VeSyncTowerFan(BypassV2Mixin, VeSyncFanBase):
24
+ """Levoit Tower Fan Device Class."""
25
+
26
+ __slots__ = ()
27
+
28
+ def __init__(
29
+ self,
30
+ details: ResponseDeviceDetailsModel,
31
+ manager: VeSync,
32
+ feature_map: FanMap,
33
+ ) -> None:
34
+ """Initialize the VeSync Base API V2 Fan Class."""
35
+ super().__init__(details, manager, feature_map)
36
+
37
+ def _set_fan_state(self, res: TowerFanResult) -> None:
38
+ """Set fan state attributes from API response."""
39
+ self.state.connection_status = ConnectionStatus.ONLINE
40
+ self.state.device_status = DeviceStatus.from_int(res.powerSwitch)
41
+ self.state.mode = res.workMode
42
+ self.state.fan_level = res.fanSpeedLevel
43
+ self.state.fan_set_level = res.manualSpeedLevel
44
+ self.state.temperature = res.temperature
45
+
46
+ self.state.mute_status = DeviceStatus.from_int(res.muteState)
47
+ self.state.mute_set_status = DeviceStatus.from_int(res.muteSwitch)
48
+ self.state.oscillation_status = DeviceStatus.from_int(res.oscillationState)
49
+ self.state.oscillation_set_status = DeviceStatus.from_int(res.oscillationSwitch)
50
+ self.state.display_status = DeviceStatus.from_int(res.screenState)
51
+ self.state.display_set_status = DeviceStatus.from_int(res.screenSwitch)
52
+ self.state.displaying_type = DeviceStatus.from_int(res.displayingType)
53
+
54
+ if res.timerRemain is not None and res.timerRemain > 0:
55
+ if self.state.device_status == DeviceStatus.ON:
56
+ self.state.timer = Timer(res.timerRemain, action='off')
57
+ else:
58
+ self.state.timer = Timer(res.timerRemain, action='on')
59
+ if res.sleepPreference is None:
60
+ return
61
+ self.state.sleep_preference_type = res.sleepPreference.sleepPreferenceType
62
+ self.state.sleep_fallasleep_remain = DeviceStatus.from_int(
63
+ res.sleepPreference.fallAsleepRemain
64
+ )
65
+ self.state.sleep_oscillation_switch = DeviceStatus.from_int(
66
+ res.sleepPreference.oscillationSwitch
67
+ )
68
+ self.state.sleep_change_fan_level = DeviceStatus.from_int(
69
+ res.sleepPreference.autoChangeFanLevelSwitch
70
+ )
71
+
72
+ async def get_details(self) -> None:
73
+ r_dict = await self.call_bypassv2_api('getTowerFanStatus')
74
+ result = process_bypassv2_result(
75
+ self, logger, 'get_details', r_dict, TowerFanResult
76
+ )
77
+ if result is None:
78
+ return
79
+ self._set_fan_state(result)
80
+
81
+ async def set_fan_speed(self, speed: int | None = None) -> bool:
82
+ if speed is None:
83
+ new_speed = Helpers.bump_level(self.state.fan_level, self.fan_levels)
84
+ else:
85
+ new_speed = speed
86
+
87
+ if new_speed not in self.fan_levels:
88
+ logger.debug('Invalid fan speed level used - %s', speed)
89
+ return False
90
+
91
+ payload_data = {'manualSpeedLevel': speed, 'levelType': 'wind', 'levelIdx': 0}
92
+ r_dict = await self.call_bypassv2_api('setLevel', payload_data)
93
+ r = Helpers.process_dev_response(logger, 'set_fan_speed', self, r_dict)
94
+ if r is None:
95
+ return False
96
+
97
+ self.state.fan_level = speed
98
+ self.state.connection_status = ConnectionStatus.ONLINE
99
+ return True
100
+
101
+ async def set_mode(self, mode: str) -> bool:
102
+ if mode.lower() == DeviceStatus.OFF:
103
+ logger.warning('Deprecated - Use `turn_off` method to turn off device')
104
+ return await self.turn_off()
105
+
106
+ if mode.lower() not in self.modes:
107
+ logger.debug('Invalid purifier mode used - %s', mode)
108
+ return False
109
+
110
+ payload_data = {'workMode': mode}
111
+ r_dict = await self.call_bypassv2_api('setTowerFanMode', payload_data)
112
+ r = Helpers.process_dev_response(logger, 'set_mode', self, r_dict)
113
+ if r is None:
114
+ return False
115
+
116
+ self.state.mode = mode
117
+ self.state.connection_status = ConnectionStatus.ONLINE
118
+ return True
119
+
120
+ async def toggle_switch(self, toggle: bool | None = None) -> bool:
121
+ if toggle is None:
122
+ toggle = self.state.device_status == DeviceStatus.OFF
123
+ payload_data = {
124
+ 'powerSwitch': int(toggle),
125
+ 'switchIdx': 0,
126
+ }
127
+ r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
128
+ r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
129
+ if r is None:
130
+ return False
131
+
132
+ self.state.device_status = DeviceStatus.from_bool(toggle)
133
+ self.state.connection_status = ConnectionStatus.ONLINE
134
+ return True
135
+
136
+ async def toggle_oscillation(self, toggle: bool) -> bool:
137
+ payload_data = {'oscillationSwitch': int(toggle)}
138
+ r_dict = await self.call_bypassv2_api('setOscillationSwitch', payload_data)
139
+ r = Helpers.process_dev_response(logger, 'toggle_oscillation', self, r_dict)
140
+ if r is None:
141
+ return False
142
+
143
+ self.state.oscillation_status = DeviceStatus.from_bool(toggle)
144
+ self.state.connection_status = ConnectionStatus.ONLINE
145
+ return True
146
+
147
+ async def toggle_mute(self, toggle: bool) -> bool:
148
+ payload_data = {'muteSwitch': int(toggle)}
149
+ r_dict = await self.call_bypassv2_api('setMuteSwitch', payload_data)
150
+ r = Helpers.process_dev_response(logger, 'toggle_mute', self, r_dict)
151
+ if r is None:
152
+ return False
153
+
154
+ self.state.mute_status = DeviceStatus.from_bool(toggle)
155
+ self.state.connection_status = ConnectionStatus.ONLINE
156
+ return True
157
+
158
+ async def toggle_display(self, toggle: bool) -> bool:
159
+ payload_data = {'screenSwitch': int(toggle)}
160
+ r_dict = await self.call_bypassv2_api('setDisplay', payload_data)
161
+ r = Helpers.process_dev_response(logger, 'toggle_display', self, r_dict)
162
+ if r is None:
163
+ return False
164
+
165
+ self.state.display_status = DeviceStatus.from_bool(toggle)
166
+ self.state.connection_status = ConnectionStatus.ONLINE
167
+ return True
168
+
169
+ async def toggle_displaying_type(self, toggle: bool) -> bool:
170
+ """Set displaying type on/off - Unknown functionality."""
171
+ payload_data = {'displayingType': int(toggle)}
172
+ r_dict = await self.call_bypassv2_api('setDisplayingType', payload_data)
173
+ r = Helpers.process_dev_response(logger, 'toggle_displaying_type', self, r_dict)
174
+ if r is None:
175
+ return False
176
+
177
+ self.state.displaying_type = DeviceStatus.from_bool(toggle)
178
+ self.state.connection_status = ConnectionStatus.ONLINE
179
+ return True
180
+
181
+ async def get_timer(self) -> None:
182
+ r_dict = await self.call_bypassv2_api('getTimer', {})
183
+ r_model = process_bypassv2_result(
184
+ self, logger, 'get_timer', r_dict, TimerModels.ResultV2GetTimer
185
+ )
186
+ if not r_model:
187
+ logger.debug('No timer found - run get_timer()')
188
+ return
189
+ if r_model.timers is None or len(r_model.timers) == 0:
190
+ logger.debug('No timer found - run get_timer()')
191
+ return
192
+ timer = r_model.timers[0]
193
+ if not isinstance(timer, TimerModels.TimerItemV2):
194
+ logger.debug('Invalid timer found for %s', self.device_name)
195
+ return
196
+ self.state.timer = Timer(timer.remain, timer.action, timer.id)
197
+
198
+ async def set_timer(self, duration: int, action: str | None = None) -> bool:
199
+ if action is None:
200
+ action = (
201
+ DeviceStatus.OFF
202
+ if self.state.device_status == DeviceStatus.ON
203
+ else DeviceStatus.ON
204
+ )
205
+ if action not in [DeviceStatus.ON, DeviceStatus.OFF]:
206
+ logger.debug('Invalid action used - %s', action)
207
+ return False
208
+ payload_data = {
209
+ 'action': action,
210
+ 'total': duration,
211
+ }
212
+ r_dict = await self.call_bypassv2_api('setTimer', payload_data)
213
+ r_model = process_bypassv2_result(
214
+ self, logger, 'set_timer', r_dict, TimerModels.ResultV2SetTimer
215
+ )
216
+ if r_model is None:
217
+ return False
218
+ self.state.timer = Timer(duration, action, r_model.id)
219
+ return True
220
+
221
+ async def clear_timer(self) -> bool:
222
+ if self.state.timer is None:
223
+ logger.debug('No timer found for - run get_timer()')
224
+ return False
225
+ payload_data = {
226
+ 'id': self.state.timer.id,
227
+ }
228
+ r_dict = await self.call_bypassv2_api('clearTimer', payload_data)
229
+ result = Helpers.process_dev_response(logger, 'clear_timer', self, r_dict)
230
+ if result is None:
231
+ return False
232
+ self.state.timer = None
233
+ return True