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