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.
- pyvesync/__init__.py +5 -0
- pyvesync/base_devices/__init__.py +35 -0
- pyvesync/base_devices/bulb_base.py +330 -0
- pyvesync/base_devices/fan_base.py +392 -0
- pyvesync/base_devices/fryer_base.py +68 -0
- pyvesync/base_devices/humidifier_base.py +462 -0
- pyvesync/base_devices/outlet_base.py +327 -0
- pyvesync/base_devices/purifier_base.py +525 -0
- pyvesync/base_devices/switch_base.py +271 -0
- pyvesync/base_devices/thermostat_base.py +283 -0
- pyvesync/base_devices/vesyncbasedevice.py +572 -0
- pyvesync/const.py +825 -0
- pyvesync/device_container.py +373 -0
- pyvesync/device_map.py +1172 -0
- pyvesync/devices/__init__.py +1 -0
- pyvesync/devices/vesyncbulb.py +1126 -0
- pyvesync/devices/vesyncfan.py +233 -0
- pyvesync/devices/vesynchumidifier.py +808 -0
- pyvesync/devices/vesynckitchen.py +651 -0
- pyvesync/devices/vesyncoutlet.py +954 -0
- pyvesync/devices/vesyncpurifier.py +990 -0
- pyvesync/devices/vesyncswitch.py +415 -0
- pyvesync/devices/vesyncthermostat.py +171 -0
- pyvesync/models/__init__.py +11 -0
- pyvesync/models/base_models.py +129 -0
- pyvesync/models/bulb_models.py +197 -0
- pyvesync/models/bypass_models.py +289 -0
- pyvesync/models/fan_models.py +47 -0
- pyvesync/models/fryer_models.py +34 -0
- pyvesync/models/home_models.py +169 -0
- pyvesync/models/humidifier_models.py +202 -0
- pyvesync/models/outlet_models.py +220 -0
- pyvesync/models/purifier_models.py +261 -0
- pyvesync/models/switch_models.py +101 -0
- pyvesync/models/thermostat_models.py +134 -0
- pyvesync/models/vesync_models.py +439 -0
- pyvesync/utils/__init__.py +1 -0
- pyvesync/utils/colors.py +250 -0
- pyvesync/utils/device_mixins.py +362 -0
- pyvesync/utils/enum_utils.py +26 -0
- pyvesync/utils/errors.py +867 -0
- pyvesync/utils/helpers.py +700 -0
- pyvesync/utils/logs.py +525 -0
- pyvesync/vesync.py +638 -0
- pyvesync/vesynchome.py +152 -0
- pyvesync-3.0.0.dist-info/METADATA +567 -0
- pyvesync-3.0.0.dist-info/RECORD +50 -0
- pyvesync-3.0.0.dist-info/WHEEL +5 -0
- pyvesync-3.0.0.dist-info/licenses/LICENSE +21 -0
- pyvesync-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
"""VeSync Humidifier Devices."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
import orjson
|
|
9
|
+
from typing_extensions import deprecated
|
|
10
|
+
|
|
11
|
+
from pyvesync.base_devices import VeSyncHumidifier
|
|
12
|
+
from pyvesync.const import ConnectionStatus, DeviceStatus
|
|
13
|
+
from pyvesync.models.bypass_models import ResultV2GetTimer, ResultV2SetTimer
|
|
14
|
+
from pyvesync.models.humidifier_models import (
|
|
15
|
+
ClassicLVHumidResult,
|
|
16
|
+
InnerHumidifierBaseResult,
|
|
17
|
+
Levoit1000SResult,
|
|
18
|
+
Superior6000SResult,
|
|
19
|
+
)
|
|
20
|
+
from pyvesync.utils.device_mixins import BypassV2Mixin, process_bypassv2_result
|
|
21
|
+
from pyvesync.utils.helpers import Helpers, Timer, Validators
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from pyvesync import VeSync
|
|
25
|
+
from pyvesync.device_map import HumidifierMap
|
|
26
|
+
from pyvesync.models.vesync_models import ResponseDeviceDetailsModel
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class VeSyncHumid200300S(BypassV2Mixin, VeSyncHumidifier):
|
|
33
|
+
"""300S Humidifier Class.
|
|
34
|
+
|
|
35
|
+
Primary class for VeSync humidifier devices.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
details (ResponseDeviceDetailsModel): The device details.
|
|
39
|
+
manager (VeSync): The manager object for API calls.
|
|
40
|
+
feature_map (HumidifierMap): The feature map for the device.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
state (HumidifierState): The state of the humidifier.
|
|
44
|
+
last_response (ResponseInfo): Last response info from API call.
|
|
45
|
+
manager (VeSync): Manager object for API calls.
|
|
46
|
+
device_name (str): Name of device.
|
|
47
|
+
device_image (str): URL for device image.
|
|
48
|
+
cid (str): Device ID.
|
|
49
|
+
connection_type (str): Connection type of device.
|
|
50
|
+
device_type (str): Type of device.
|
|
51
|
+
type (str): Type of device.
|
|
52
|
+
uuid (str): UUID of device, not always present.
|
|
53
|
+
config_module (str): Configuration module of device.
|
|
54
|
+
mac_id (str): MAC ID of device.
|
|
55
|
+
current_firm_version (str): Current firmware version of device.
|
|
56
|
+
device_region (str): Region of device. (US, EU, etc.)
|
|
57
|
+
pid (str): Product ID of device, pulled by some devices on update.
|
|
58
|
+
sub_device_no (int): Sub-device number of device.
|
|
59
|
+
product_type (str): Product type of device.
|
|
60
|
+
features (dict): Features of device.
|
|
61
|
+
mist_levels (list): List of mist levels.
|
|
62
|
+
mist_modes (list): List of mist modes.
|
|
63
|
+
target_minmax (tuple): Tuple of target min and max values.
|
|
64
|
+
warm_mist_levels (list): List of warm mist levels.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
__slots__ = ()
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
details: ResponseDeviceDetailsModel,
|
|
72
|
+
manager: VeSync,
|
|
73
|
+
feature_map: HumidifierMap,
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Initialize 200S/300S Humidifier class."""
|
|
76
|
+
super().__init__(details, manager, feature_map)
|
|
77
|
+
|
|
78
|
+
def _set_state(self, resp_model: ClassicLVHumidResult) -> None:
|
|
79
|
+
"""Set state from get_details API model."""
|
|
80
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
81
|
+
self.state.device_status = DeviceStatus.from_bool(resp_model.enabled)
|
|
82
|
+
self.state.mode = resp_model.mode
|
|
83
|
+
self.state.humidity = resp_model.humidity
|
|
84
|
+
self.state.mist_virtual_level = resp_model.mist_virtual_level or 0
|
|
85
|
+
self.state.mist_level = resp_model.mist_level or 0
|
|
86
|
+
self.state.water_lacks = resp_model.water_lacks
|
|
87
|
+
self.state.humidity_high = resp_model.humidity_high
|
|
88
|
+
self.state.water_tank_lifted = resp_model.water_tank_lifted
|
|
89
|
+
self.state.auto_stop_target_reached = resp_model.automatic_stop_reach_target
|
|
90
|
+
if self.supports_nightlight and resp_model.night_light_brightness is not None:
|
|
91
|
+
self.state.nightlight_brightness = resp_model.night_light_brightness
|
|
92
|
+
self.state.nightlight_status = (
|
|
93
|
+
DeviceStatus.ON
|
|
94
|
+
if resp_model.night_light_brightness > 0
|
|
95
|
+
else DeviceStatus.OFF
|
|
96
|
+
)
|
|
97
|
+
self.state.display_status = DeviceStatus.from_bool(resp_model.display)
|
|
98
|
+
if self.supports_warm_mist and resp_model.warm_level is not None:
|
|
99
|
+
self.state.warm_mist_level = resp_model.warm_level
|
|
100
|
+
self.state.warm_mist_enabled = resp_model.warm_enabled
|
|
101
|
+
config = resp_model.configuration
|
|
102
|
+
if config is not None:
|
|
103
|
+
self.state.auto_target_humidity = config.auto_target_humidity
|
|
104
|
+
self.state.automatic_stop_config = config.automatic_stop
|
|
105
|
+
self.state.display_set_status = DeviceStatus.from_bool(config.display)
|
|
106
|
+
|
|
107
|
+
async def get_details(self) -> None:
|
|
108
|
+
r_dict = await self.call_bypassv2_api('getHumidifierStatus')
|
|
109
|
+
r_model = process_bypassv2_result(
|
|
110
|
+
self, logger, 'get_details', r_dict, ClassicLVHumidResult
|
|
111
|
+
)
|
|
112
|
+
if r_model is None:
|
|
113
|
+
return
|
|
114
|
+
self._set_state(r_model)
|
|
115
|
+
|
|
116
|
+
async def toggle_switch(self, toggle: bool | None = None) -> bool:
|
|
117
|
+
if toggle is None:
|
|
118
|
+
toggle = self.state.device_status == DeviceStatus.ON
|
|
119
|
+
|
|
120
|
+
payload_data = {'enabled': toggle, 'id': 0}
|
|
121
|
+
r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
|
|
122
|
+
r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
|
|
123
|
+
if r is None:
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
self.state.device_status = DeviceStatus.from_bool(toggle)
|
|
127
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
async def get_timer(self) -> Timer | None:
|
|
131
|
+
r_dict = await self.call_bypassv2_api('getTimer')
|
|
132
|
+
result_model = process_bypassv2_result(
|
|
133
|
+
self, logger, 'get_timer', r_dict, ResultV2GetTimer
|
|
134
|
+
)
|
|
135
|
+
if result_model is None:
|
|
136
|
+
return None
|
|
137
|
+
if not result_model.timers:
|
|
138
|
+
logger.debug('No timers found')
|
|
139
|
+
return None
|
|
140
|
+
timer = result_model.timers[0]
|
|
141
|
+
self.state.timer = Timer(
|
|
142
|
+
timer_duration=timer.total,
|
|
143
|
+
action=timer.action,
|
|
144
|
+
id=timer.id,
|
|
145
|
+
)
|
|
146
|
+
return self.state.timer
|
|
147
|
+
|
|
148
|
+
async def clear_timer(self) -> bool:
|
|
149
|
+
if self.state.timer is None:
|
|
150
|
+
logger.debug('No timer to clear, run get_timer() first.')
|
|
151
|
+
return False
|
|
152
|
+
payload = {
|
|
153
|
+
'id': self.state.timer.id,
|
|
154
|
+
}
|
|
155
|
+
r_dict = await self.call_bypassv2_api('delTimer', payload)
|
|
156
|
+
r = Helpers.process_dev_response(logger, 'clear_timer', self, r_dict)
|
|
157
|
+
if r is None:
|
|
158
|
+
return False
|
|
159
|
+
self.state.timer = None
|
|
160
|
+
return True
|
|
161
|
+
|
|
162
|
+
async def set_timer(self, duration: int, action: str | None = None) -> bool:
|
|
163
|
+
if action is None:
|
|
164
|
+
action = (
|
|
165
|
+
DeviceStatus.OFF
|
|
166
|
+
if self.state.device_status == DeviceStatus.ON
|
|
167
|
+
else DeviceStatus.ON
|
|
168
|
+
)
|
|
169
|
+
payload_data = {
|
|
170
|
+
'action': str(action),
|
|
171
|
+
'total': duration,
|
|
172
|
+
}
|
|
173
|
+
r_dict = await self.call_bypassv2_api('addTimer', payload_data)
|
|
174
|
+
r = process_bypassv2_result(self, logger, 'set_timer', r_dict, ResultV2SetTimer)
|
|
175
|
+
if r is None:
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
self.state.timer = Timer(
|
|
179
|
+
timer_duration=duration, action=action, id=r.id, remaining=0
|
|
180
|
+
)
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
@deprecated('Use turn_on_automatic_stop() instead.')
|
|
184
|
+
async def automatic_stop_on(self) -> bool:
|
|
185
|
+
"""Turn 200S/300S Humidifier automatic stop on."""
|
|
186
|
+
return await self.toggle_automatic_stop(True)
|
|
187
|
+
|
|
188
|
+
@deprecated('Use turn_off_automatic_stop() instead.')
|
|
189
|
+
async def automatic_stop_off(self) -> bool:
|
|
190
|
+
"""Turn 200S/300S Humidifier automatic stop on."""
|
|
191
|
+
return await self.toggle_automatic_stop(False)
|
|
192
|
+
|
|
193
|
+
@deprecated('Use toggle_automatic_stop(toggle: bool) instead.')
|
|
194
|
+
async def set_automatic_stop(self, mode: bool) -> bool:
|
|
195
|
+
"""Set 200S/300S Humidifier to automatic stop."""
|
|
196
|
+
return await self.toggle_automatic_stop(mode)
|
|
197
|
+
|
|
198
|
+
async def toggle_automatic_stop(self, toggle: bool | None = None) -> bool:
|
|
199
|
+
if toggle is None:
|
|
200
|
+
toggle = self.state.automatic_stop_config != DeviceStatus.ON
|
|
201
|
+
|
|
202
|
+
payload_data = {'enabled': toggle}
|
|
203
|
+
r_dict = await self.call_bypassv2_api('setAutomaticStop', payload_data)
|
|
204
|
+
r = Helpers.process_dev_response(logger, 'set_automatic_stop', self, r_dict)
|
|
205
|
+
if r is None:
|
|
206
|
+
return False
|
|
207
|
+
self.state.automatic_stop_config = toggle
|
|
208
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
209
|
+
return True
|
|
210
|
+
|
|
211
|
+
@deprecated('Use toggle_display(toggle: bool) instead.')
|
|
212
|
+
async def set_display(self, toggle: bool) -> bool:
|
|
213
|
+
"""Deprecated method to toggle display on/off.
|
|
214
|
+
|
|
215
|
+
Use toggle_display(toggle: bool) instead.
|
|
216
|
+
"""
|
|
217
|
+
return await self.toggle_display(toggle)
|
|
218
|
+
|
|
219
|
+
async def toggle_display(self, toggle: bool) -> bool:
|
|
220
|
+
payload_data = {'state': toggle}
|
|
221
|
+
r_dict = await self.call_bypassv2_api('setDisplay', payload_data)
|
|
222
|
+
r = Helpers.process_dev_response(logger, 'set_display', self, r_dict)
|
|
223
|
+
if r is None:
|
|
224
|
+
return False
|
|
225
|
+
self.state.display_set_status = DeviceStatus.from_bool(toggle)
|
|
226
|
+
self.state.display_status = DeviceStatus.from_bool(toggle)
|
|
227
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
228
|
+
return True
|
|
229
|
+
|
|
230
|
+
async def set_humidity(self, humidity: int) -> bool:
|
|
231
|
+
if not Validators.validate_range(humidity, *self.target_minmax):
|
|
232
|
+
logger.debug(
|
|
233
|
+
'Invalid humidity, must be between %s and %s', *self.target_minmax
|
|
234
|
+
)
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
payload_data = {'target_humidity': humidity}
|
|
238
|
+
r_dict = await self.call_bypassv2_api('setTargetHumidity', payload_data)
|
|
239
|
+
r = Helpers.process_dev_response(logger, 'set_humidity', self, r_dict)
|
|
240
|
+
if r is None:
|
|
241
|
+
return False
|
|
242
|
+
self.state.auto_target_humidity = humidity
|
|
243
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
244
|
+
return True
|
|
245
|
+
|
|
246
|
+
async def set_nightlight_brightness(self, brightness: int) -> bool:
|
|
247
|
+
if not self.supports_nightlight:
|
|
248
|
+
logger.debug(
|
|
249
|
+
'%s is a %s does not have a nightlight',
|
|
250
|
+
self.device_name,
|
|
251
|
+
self.device_type,
|
|
252
|
+
)
|
|
253
|
+
return False
|
|
254
|
+
|
|
255
|
+
if not Validators.validate_zero_to_hundred(brightness):
|
|
256
|
+
logger.debug('Brightness value must be set between 0 and 100')
|
|
257
|
+
return False
|
|
258
|
+
|
|
259
|
+
payload_data = {'night_light_brightness': brightness}
|
|
260
|
+
r_dict = await self.call_bypassv2_api('setNightLightBrightness', payload_data)
|
|
261
|
+
r = Helpers.process_dev_response(
|
|
262
|
+
logger, 'set_night_light_brightness', self, r_dict
|
|
263
|
+
)
|
|
264
|
+
if r is None:
|
|
265
|
+
return False
|
|
266
|
+
self.state.nightlight_brightness = brightness
|
|
267
|
+
self.state.nightlight_status = (
|
|
268
|
+
DeviceStatus.ON if brightness > 0 else DeviceStatus.OFF
|
|
269
|
+
)
|
|
270
|
+
return True
|
|
271
|
+
|
|
272
|
+
@deprecated('Use set_mode(mode: str) instead.')
|
|
273
|
+
async def set_humidity_mode(self, mode: str) -> bool:
|
|
274
|
+
"""Deprecated - set humidifier mode.
|
|
275
|
+
|
|
276
|
+
Use `set_mode(mode: str)` instead.
|
|
277
|
+
"""
|
|
278
|
+
return await self.set_mode(mode)
|
|
279
|
+
|
|
280
|
+
async def set_mode(self, mode: str) -> bool:
|
|
281
|
+
if mode.lower() not in self.mist_modes:
|
|
282
|
+
logger.debug('Invalid humidity mode used - %s', mode)
|
|
283
|
+
logger.debug(
|
|
284
|
+
'Proper modes for this device are - %s',
|
|
285
|
+
orjson.dumps(
|
|
286
|
+
self.mist_modes, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
|
|
287
|
+
),
|
|
288
|
+
)
|
|
289
|
+
return False
|
|
290
|
+
|
|
291
|
+
payload_data = {'mode': self.mist_modes[mode.lower()]}
|
|
292
|
+
r_dict = await self.call_bypassv2_api('setHumidityMode', payload_data)
|
|
293
|
+
r = Helpers.process_dev_response(logger, 'set_humidity_mode', self, r_dict)
|
|
294
|
+
if r is None:
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
self.state.mode = mode
|
|
298
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
299
|
+
return True
|
|
300
|
+
|
|
301
|
+
async def set_warm_level(self, warm_level: int) -> bool:
|
|
302
|
+
if not self.supports_warm_mist:
|
|
303
|
+
logger.debug(
|
|
304
|
+
'%s is a %s does not have a mist warmer',
|
|
305
|
+
self.device_name,
|
|
306
|
+
self.device_type,
|
|
307
|
+
)
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
if warm_level not in self.warm_mist_levels:
|
|
311
|
+
logger.debug('warm_level value must be - %s', str(self.warm_mist_levels))
|
|
312
|
+
return False
|
|
313
|
+
|
|
314
|
+
payload_data = {'type': 'warm', 'level': warm_level, 'id': 0}
|
|
315
|
+
r_dict = await self.call_bypassv2_api('setVirtualLevel', payload_data)
|
|
316
|
+
r = Helpers.process_dev_response(logger, 'set_warm_level', self, r_dict)
|
|
317
|
+
if r is None:
|
|
318
|
+
return False
|
|
319
|
+
|
|
320
|
+
self.state.warm_mist_level = warm_level
|
|
321
|
+
self.state.warm_mist_enabled = True
|
|
322
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
323
|
+
return True
|
|
324
|
+
|
|
325
|
+
async def set_mist_level(self, level: int) -> bool:
|
|
326
|
+
if level not in self.mist_levels:
|
|
327
|
+
logger.debug(
|
|
328
|
+
'Humidifier mist level must be between %s and %s',
|
|
329
|
+
self.mist_levels[0],
|
|
330
|
+
self.mist_levels[-1],
|
|
331
|
+
)
|
|
332
|
+
return False
|
|
333
|
+
|
|
334
|
+
payload_data = {'id': 0, 'level': level, 'type': 'mist'}
|
|
335
|
+
r_dict = await self.call_bypassv2_api('setVirtualLevel', payload_data)
|
|
336
|
+
r = Helpers.process_dev_response(logger, 'set_mist_level', self, r_dict)
|
|
337
|
+
if r is None:
|
|
338
|
+
return False
|
|
339
|
+
|
|
340
|
+
self.state.mist_virtual_level = level
|
|
341
|
+
self.state.mist_level = level
|
|
342
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
343
|
+
return True
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class VeSyncHumid200S(VeSyncHumid200300S):
|
|
347
|
+
"""Levoit Classic 200S Specific class.
|
|
348
|
+
|
|
349
|
+
Overrides the `toggle_display(toggle: bool)` method of the
|
|
350
|
+
VeSyncHumid200300S class.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
details (ResponseDeviceDetailsModel): The device details.
|
|
354
|
+
manager (VeSync): The manager object for API calls.
|
|
355
|
+
feature_map (HumidifierMap): The feature map for the device.
|
|
356
|
+
|
|
357
|
+
Attributes:
|
|
358
|
+
state (HumidifierState): The state of the humidifier.
|
|
359
|
+
last_response (ResponseInfo): Last response from API call.
|
|
360
|
+
manager (VeSync): Manager object for API calls.
|
|
361
|
+
device_name (str): Name of device.
|
|
362
|
+
device_image (str): URL for device image.
|
|
363
|
+
cid (str): Device ID.
|
|
364
|
+
connection_type (str): Connection type of device.
|
|
365
|
+
device_type (str): Type of device.
|
|
366
|
+
type (str): Type of device.
|
|
367
|
+
uuid (str): UUID of device, not always present.
|
|
368
|
+
config_module (str): Configuration module of device.
|
|
369
|
+
mac_id (str): MAC ID of device.
|
|
370
|
+
current_firm_version (str): Current firmware version of device.
|
|
371
|
+
device_region (str): Region of device. (US, EU, etc.)
|
|
372
|
+
pid (str): Product ID of device, pulled by some devices on update.
|
|
373
|
+
sub_device_no (int): Sub-device number of device.
|
|
374
|
+
product_type (str): Product type of device.
|
|
375
|
+
features (dict): Features of device.
|
|
376
|
+
mist_levels (list): List of mist levels.
|
|
377
|
+
mist_modes (list): List of mist modes.
|
|
378
|
+
target_minmax (tuple): Tuple of target min and max values.
|
|
379
|
+
warm_mist_levels (list): List of warm mist levels.
|
|
380
|
+
"""
|
|
381
|
+
|
|
382
|
+
def __init__(
|
|
383
|
+
self,
|
|
384
|
+
details: ResponseDeviceDetailsModel,
|
|
385
|
+
manager: VeSync,
|
|
386
|
+
feature_map: HumidifierMap,
|
|
387
|
+
) -> None:
|
|
388
|
+
"""Initialize levoit 200S device class.
|
|
389
|
+
|
|
390
|
+
This overrides the `toggle_display(toggle: bool)` method of the
|
|
391
|
+
VeSyncHumid200300S class.
|
|
392
|
+
"""
|
|
393
|
+
super().__init__(details, manager, feature_map)
|
|
394
|
+
|
|
395
|
+
async def toggle_display(self, toggle: bool) -> bool:
|
|
396
|
+
payload_data = {'enabled': toggle, 'id': 0}
|
|
397
|
+
r_dict = await self.call_bypassv2_api('setIndicatorLightSwitch', payload_data)
|
|
398
|
+
r = Helpers.process_dev_response(logger, 'toggle_display', self, r_dict)
|
|
399
|
+
if r is None:
|
|
400
|
+
return False
|
|
401
|
+
|
|
402
|
+
self.state.display_set_status = DeviceStatus.from_bool(toggle)
|
|
403
|
+
self.state.display_status = DeviceStatus.from_bool(toggle)
|
|
404
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
405
|
+
return True
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
class VeSyncSuperior6000S(BypassV2Mixin, VeSyncHumidifier):
|
|
409
|
+
"""Superior 6000S Humidifier.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
details (ResponseDeviceDetailsModel): The device details.
|
|
413
|
+
manager (VeSync): The manager object for API calls.
|
|
414
|
+
feature_map (HumidifierMap): The feature map for the device.
|
|
415
|
+
|
|
416
|
+
Attributes:
|
|
417
|
+
state (HumidifierState): The state of the humidifier.
|
|
418
|
+
last_response (ResponseInfo): Last response from API call.
|
|
419
|
+
manager (VeSync): Manager object for API calls.
|
|
420
|
+
device_name (str): Name of device.
|
|
421
|
+
device_image (str): URL for device image.
|
|
422
|
+
cid (str): Device ID.
|
|
423
|
+
connection_type (str): Connection type of device.
|
|
424
|
+
device_type (str): Type of device.
|
|
425
|
+
type (str): Type of device.
|
|
426
|
+
uuid (str): UUID of device, not always present.
|
|
427
|
+
config_module (str): Configuration module of device.
|
|
428
|
+
mac_id (str): MAC ID of device.
|
|
429
|
+
current_firm_version (str): Current firmware version of device.
|
|
430
|
+
device_region (str): Region of device. (US, EU, etc.)
|
|
431
|
+
pid (str): Product ID of device, pulled by some devices on update.
|
|
432
|
+
sub_device_no (int): Sub-device number of device.
|
|
433
|
+
product_type (str): Product type of device.
|
|
434
|
+
features (dict): Features of device.
|
|
435
|
+
mist_levels (list): List of mist levels.
|
|
436
|
+
mist_modes (list): List of mist modes.
|
|
437
|
+
target_minmax (tuple): Tuple of target min and max values.
|
|
438
|
+
warm_mist_levels (list): List of warm mist levels.
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
def __init__(
|
|
442
|
+
self,
|
|
443
|
+
details: ResponseDeviceDetailsModel,
|
|
444
|
+
manager: VeSync,
|
|
445
|
+
feature_map: HumidifierMap,
|
|
446
|
+
) -> None:
|
|
447
|
+
"""Initialize Superior 6000S Humidifier class."""
|
|
448
|
+
super().__init__(details, manager, feature_map)
|
|
449
|
+
|
|
450
|
+
def _set_state(self, resp_model: Superior6000SResult) -> None:
|
|
451
|
+
"""Set state from Superior 6000S API result model."""
|
|
452
|
+
self.state.device_status = DeviceStatus.from_int(resp_model.powerSwitch)
|
|
453
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
454
|
+
self.state.mode = resp_model.workMode
|
|
455
|
+
self.state.auto_target_humidity = resp_model.targetHumidity
|
|
456
|
+
self.state.mist_level = resp_model.mistLevel
|
|
457
|
+
self.state.mist_virtual_level = resp_model.virtualLevel
|
|
458
|
+
self.state.water_lacks = bool(resp_model.waterLacksState)
|
|
459
|
+
self.state.water_tank_lifted = bool(resp_model.waterTankLifted)
|
|
460
|
+
self.state.automatic_stop_config = bool(resp_model.autoStopSwitch)
|
|
461
|
+
self.state.auto_stop_target_reached = bool(resp_model.autoStopState)
|
|
462
|
+
self.state.display_set_status = DeviceStatus.from_int(resp_model.screenSwitch)
|
|
463
|
+
self.state.display_status = DeviceStatus.from_int(resp_model.screenState)
|
|
464
|
+
self.state.auto_preference = resp_model.autoPreference
|
|
465
|
+
self.state.filter_life_percent = resp_model.filterLifePercent
|
|
466
|
+
self.state.temperature = resp_model.temperature # Unknown units
|
|
467
|
+
|
|
468
|
+
drying_mode = resp_model.dryingMode
|
|
469
|
+
if drying_mode is not None:
|
|
470
|
+
self.state.drying_mode_status = DeviceStatus.from_int(drying_mode.dryingState)
|
|
471
|
+
self.state.drying_mode_level = drying_mode.dryingLevel
|
|
472
|
+
self.state.drying_mode_auto_switch = DeviceStatus.from_int(
|
|
473
|
+
drying_mode.autoDryingSwitch
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
if resp_model.timerRemain > 0:
|
|
477
|
+
self.state.timer = Timer(
|
|
478
|
+
resp_model.timerRemain,
|
|
479
|
+
DeviceStatus.from_bool(self.state.device_status != DeviceStatus.ON),
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
async def get_details(self) -> None:
|
|
483
|
+
r_dict = await self.call_bypassv2_api('getHumidifierStatus')
|
|
484
|
+
r_model = process_bypassv2_result(
|
|
485
|
+
self, logger, 'get_details', r_dict, Superior6000SResult
|
|
486
|
+
)
|
|
487
|
+
if r_model is None:
|
|
488
|
+
return
|
|
489
|
+
|
|
490
|
+
self._set_state(r_model)
|
|
491
|
+
|
|
492
|
+
async def toggle_switch(self, toggle: bool | None = None) -> bool:
|
|
493
|
+
if toggle is None:
|
|
494
|
+
toggle = self.state.device_status != DeviceStatus.ON
|
|
495
|
+
|
|
496
|
+
payload_data = {'powerSwitch': int(toggle), 'switchIdx': 0}
|
|
497
|
+
r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
|
|
498
|
+
r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
|
|
499
|
+
if r is None:
|
|
500
|
+
return False
|
|
501
|
+
|
|
502
|
+
self.state.device_status = DeviceStatus.from_bool(toggle)
|
|
503
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
504
|
+
return True
|
|
505
|
+
|
|
506
|
+
async def toggle_automatic_stop(self, toggle: bool | None = None) -> bool:
|
|
507
|
+
if toggle is None:
|
|
508
|
+
toggle = self.state.automatic_stop_config is not True
|
|
509
|
+
|
|
510
|
+
payload_data = {'autoStopSwitch': int(toggle)}
|
|
511
|
+
r_dict = await self.call_bypassv2_api('setAutoStopSwitch', payload_data)
|
|
512
|
+
r = Helpers.process_dev_response(logger, 'toggle_automatic_stop', self, r_dict)
|
|
513
|
+
if r is None:
|
|
514
|
+
return False
|
|
515
|
+
|
|
516
|
+
self.state.device_status = DeviceStatus.from_bool(toggle)
|
|
517
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
518
|
+
return True
|
|
519
|
+
|
|
520
|
+
@deprecated('Use toggle_drying_mode() instead.')
|
|
521
|
+
async def set_drying_mode_enabled(self, mode: bool) -> bool:
|
|
522
|
+
"""Set drying mode on/off."""
|
|
523
|
+
return await self.toggle_drying_mode(mode)
|
|
524
|
+
|
|
525
|
+
async def toggle_drying_mode(self, toggle: bool | None = None) -> bool:
|
|
526
|
+
if toggle is None:
|
|
527
|
+
toggle = self.state.drying_mode_status != DeviceStatus.ON
|
|
528
|
+
|
|
529
|
+
payload_data = {'autoDryingSwitch': int(toggle)}
|
|
530
|
+
r_dict = await self.call_bypassv2_api('setDryingMode', payload_data)
|
|
531
|
+
r = Helpers.process_dev_response(logger, 'set_drying_mode_enabled', self, r_dict)
|
|
532
|
+
if r is None:
|
|
533
|
+
return False
|
|
534
|
+
|
|
535
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
536
|
+
self.state.drying_mode_auto_switch = DeviceStatus.from_bool(toggle)
|
|
537
|
+
return True
|
|
538
|
+
|
|
539
|
+
@deprecated('Use toggle_display() instead.')
|
|
540
|
+
async def set_display_enabled(self, mode: bool) -> bool:
|
|
541
|
+
"""Set display on/off.
|
|
542
|
+
|
|
543
|
+
Deprecated method, please use toggle_display() instead.
|
|
544
|
+
"""
|
|
545
|
+
return await self.toggle_display(mode)
|
|
546
|
+
|
|
547
|
+
async def toggle_display(self, toggle: bool | None = None) -> bool:
|
|
548
|
+
if toggle is None:
|
|
549
|
+
toggle = self.state.display_set_status != DeviceStatus.ON
|
|
550
|
+
|
|
551
|
+
payload_data = {'screenSwitch': int(toggle)}
|
|
552
|
+
r_dict = await self.call_bypassv2_api('setDisplay', payload_data)
|
|
553
|
+
r = Helpers.process_dev_response(logger, 'set_display', self, r_dict)
|
|
554
|
+
if r is None:
|
|
555
|
+
return False
|
|
556
|
+
|
|
557
|
+
self.state.display_set_status = DeviceStatus.from_bool(toggle)
|
|
558
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
559
|
+
return True
|
|
560
|
+
|
|
561
|
+
async def set_humidity(self, humidity: int) -> bool:
|
|
562
|
+
if not Validators.validate_range(humidity, *self.target_minmax):
|
|
563
|
+
logger.debug('Humidity value must be set between 30 and 80')
|
|
564
|
+
return False
|
|
565
|
+
|
|
566
|
+
payload_data = {'targetHumidity': humidity}
|
|
567
|
+
r_dict = await self.call_bypassv2_api('setTargetHumidity', payload_data)
|
|
568
|
+
r = Helpers.process_dev_response(logger, 'set_humidity', self, r_dict)
|
|
569
|
+
if r is None:
|
|
570
|
+
return False
|
|
571
|
+
|
|
572
|
+
self.state.auto_target_humidity = humidity
|
|
573
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
574
|
+
return True
|
|
575
|
+
|
|
576
|
+
@deprecated('Use set_mode(mode: str) instead.')
|
|
577
|
+
async def set_humidity_mode(self, mode: str) -> bool:
|
|
578
|
+
"""Set humidifier mode."""
|
|
579
|
+
return await self.set_mode(mode)
|
|
580
|
+
|
|
581
|
+
async def set_mode(self, mode: str) -> bool:
|
|
582
|
+
if mode.lower() not in self.mist_modes:
|
|
583
|
+
logger.debug('Invalid humidity mode used - %s', mode)
|
|
584
|
+
logger.debug(
|
|
585
|
+
'Proper modes for this device are - %s',
|
|
586
|
+
orjson.dumps(
|
|
587
|
+
self.mist_modes, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
|
|
588
|
+
),
|
|
589
|
+
)
|
|
590
|
+
return False
|
|
591
|
+
|
|
592
|
+
payload_data = {'workMode': self.mist_modes[mode.lower()]}
|
|
593
|
+
r_dict = await self.call_bypassv2_api('setHumidityMode', payload_data)
|
|
594
|
+
|
|
595
|
+
r = Helpers.process_dev_response(logger, 'set_humidity_mode', self, r_dict)
|
|
596
|
+
if r is None:
|
|
597
|
+
return False
|
|
598
|
+
self.state.mode = mode
|
|
599
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
600
|
+
return True
|
|
601
|
+
|
|
602
|
+
async def set_mist_level(self, level: int) -> bool:
|
|
603
|
+
if level not in self.mist_levels:
|
|
604
|
+
logger.debug('Humidifier mist level must be between 0 and 9')
|
|
605
|
+
return False
|
|
606
|
+
|
|
607
|
+
payload_data = {'levelIdx': 0, 'virtualLevel': level, 'levelType': 'mist'}
|
|
608
|
+
r_dict = await self.call_bypassv2_api('setVirtualLevel', payload_data)
|
|
609
|
+
r = Helpers.process_dev_response(logger, 'set_mist_level', self, r_dict)
|
|
610
|
+
if r is None:
|
|
611
|
+
return False
|
|
612
|
+
|
|
613
|
+
self.state.mist_level = level
|
|
614
|
+
self.state.mist_virtual_level = level
|
|
615
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
616
|
+
return True
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
class VeSyncHumid1000S(VeSyncHumid200300S):
|
|
620
|
+
"""Levoit OasisMist 1000S Specific class.
|
|
621
|
+
|
|
622
|
+
Args:
|
|
623
|
+
details (ResponseDeviceDetailsModel): The device details.
|
|
624
|
+
manager (VeSync): The manager object for API calls.
|
|
625
|
+
feature_map (HumidifierMap): The feature map for the device.
|
|
626
|
+
|
|
627
|
+
Attributes:
|
|
628
|
+
state (HumidifierState): The state of the humidifier.
|
|
629
|
+
last_response (ResponseInfo): Last response from API call.
|
|
630
|
+
manager (VeSync): Manager object for API calls.
|
|
631
|
+
device_name (str): Name of device.
|
|
632
|
+
device_image (str): URL for device image.
|
|
633
|
+
cid (str): Device ID.
|
|
634
|
+
connection_type (str): Connection type of device.
|
|
635
|
+
device_type (str): Type of device.
|
|
636
|
+
type (str): Type of device.
|
|
637
|
+
uuid (str): UUID of device, not always present.
|
|
638
|
+
config_module (str): Configuration module of device.
|
|
639
|
+
mac_id (str): MAC ID of device.
|
|
640
|
+
current_firm_version (str): Current firmware version of device.
|
|
641
|
+
device_region (str): Region of device. (US, EU, etc.)
|
|
642
|
+
pid (str): Product ID of device, pulled by some devices on update.
|
|
643
|
+
sub_device_no (int): Sub-device number of device.
|
|
644
|
+
product_type (str): Product type of device.
|
|
645
|
+
features (dict): Features of device.
|
|
646
|
+
mist_levels (list): List of mist levels.
|
|
647
|
+
mist_modes (list): List of mist modes.
|
|
648
|
+
target_minmax (tuple): Tuple of target min and max values.
|
|
649
|
+
warm_mist_levels (list): List of warm mist levels.
|
|
650
|
+
"""
|
|
651
|
+
|
|
652
|
+
def __init__(
|
|
653
|
+
self,
|
|
654
|
+
details: ResponseDeviceDetailsModel,
|
|
655
|
+
manager: VeSync,
|
|
656
|
+
feature_map: HumidifierMap,
|
|
657
|
+
) -> None:
|
|
658
|
+
"""Initialize levoit 1000S device class."""
|
|
659
|
+
super().__init__(details, manager, feature_map)
|
|
660
|
+
|
|
661
|
+
def _set_state(self, resp_model: InnerHumidifierBaseResult) -> None:
|
|
662
|
+
"""Set state of Levoit 1000S from API result model."""
|
|
663
|
+
if not isinstance(resp_model, Levoit1000SResult):
|
|
664
|
+
return
|
|
665
|
+
self.state.device_status = DeviceStatus.from_int(resp_model.powerSwitch)
|
|
666
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
667
|
+
self.state.mode = resp_model.workMode
|
|
668
|
+
self.state.auto_target_humidity = resp_model.targetHumidity
|
|
669
|
+
self.state.mist_level = resp_model.mistLevel
|
|
670
|
+
self.state.mist_virtual_level = resp_model.virtualLevel
|
|
671
|
+
self.state.water_lacks = bool(resp_model.waterLacksState)
|
|
672
|
+
self.state.water_tank_lifted = bool(resp_model.waterTankLifted)
|
|
673
|
+
self.state.automatic_stop_config = bool(resp_model.autoStopSwitch)
|
|
674
|
+
self.state.auto_stop_target_reached = bool(resp_model.autoStopState)
|
|
675
|
+
self.state.display_set_status = DeviceStatus.from_int(resp_model.screenSwitch)
|
|
676
|
+
self.state.display_status = DeviceStatus.from_int(resp_model.screenState)
|
|
677
|
+
|
|
678
|
+
async def get_details(self) -> None:
|
|
679
|
+
r_dict = await self.call_bypassv2_api('getHumidifierStatus')
|
|
680
|
+
r_model = process_bypassv2_result(
|
|
681
|
+
self, logger, 'get_details', r_dict, Levoit1000SResult
|
|
682
|
+
)
|
|
683
|
+
if r_model is None:
|
|
684
|
+
return
|
|
685
|
+
|
|
686
|
+
self._set_state(r_model)
|
|
687
|
+
|
|
688
|
+
@deprecated('Use toggle_display() instead.')
|
|
689
|
+
async def set_display(self, toggle: bool) -> bool:
|
|
690
|
+
"""Toggle display on/off.
|
|
691
|
+
|
|
692
|
+
This is a deprecated method, please use toggle_display() instead.
|
|
693
|
+
"""
|
|
694
|
+
return await self.toggle_switch(toggle)
|
|
695
|
+
|
|
696
|
+
async def toggle_display(self, toggle: bool) -> bool:
|
|
697
|
+
payload_data = {'screenSwitch': int(toggle)}
|
|
698
|
+
body = self._build_request('setDisplay', payload_data)
|
|
699
|
+
headers = Helpers.req_header_bypass()
|
|
700
|
+
|
|
701
|
+
r_dict, _ = await self.manager.async_call_api(
|
|
702
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
703
|
+
method='post',
|
|
704
|
+
headers=headers,
|
|
705
|
+
json_object=body.to_dict(),
|
|
706
|
+
)
|
|
707
|
+
r = Helpers.process_dev_response(logger, 'set_display', self, r_dict)
|
|
708
|
+
if r is None:
|
|
709
|
+
return False
|
|
710
|
+
self.state.display_set_status = DeviceStatus.from_bool(toggle)
|
|
711
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
712
|
+
return True
|
|
713
|
+
|
|
714
|
+
@deprecated('Use set_mode() instead.')
|
|
715
|
+
async def set_humidity_mode(self, mode: str) -> bool:
|
|
716
|
+
"""Set humidifier mode - sleep, auto or manual.
|
|
717
|
+
|
|
718
|
+
Deprecated, please use set_mode() instead.
|
|
719
|
+
"""
|
|
720
|
+
return await self.set_mode(mode)
|
|
721
|
+
|
|
722
|
+
async def set_mode(self, mode: str) -> bool:
|
|
723
|
+
if mode.lower() not in self.mist_modes:
|
|
724
|
+
logger.debug('Invalid humidity mode used - %s', mode)
|
|
725
|
+
logger.debug(
|
|
726
|
+
'Proper modes for this device are - %s',
|
|
727
|
+
orjson.dumps(
|
|
728
|
+
self.mist_modes, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
|
|
729
|
+
),
|
|
730
|
+
)
|
|
731
|
+
return False
|
|
732
|
+
|
|
733
|
+
payload_data = {'workMode': mode.lower()}
|
|
734
|
+
r_dict = await self.call_bypassv2_api('setHumidityMode', payload_data)
|
|
735
|
+
r = Helpers.process_dev_response(logger, 'set_mode', self, r_dict)
|
|
736
|
+
if r is None:
|
|
737
|
+
return False
|
|
738
|
+
|
|
739
|
+
self.state.mode = mode
|
|
740
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
741
|
+
return True
|
|
742
|
+
|
|
743
|
+
async def set_mist_level(self, level: int) -> bool:
|
|
744
|
+
if level not in self.mist_levels:
|
|
745
|
+
logger.debug('Humidifier mist level out of range')
|
|
746
|
+
return False
|
|
747
|
+
|
|
748
|
+
payload_data = {'levelIdx': 0, 'virtualLevel': level, 'levelType': 'mist'}
|
|
749
|
+
r_dict = await self.call_bypassv2_api('virtualLevel', payload_data)
|
|
750
|
+
r = Helpers.process_dev_response(logger, 'set_mist_level', self, r_dict)
|
|
751
|
+
if r is None:
|
|
752
|
+
return False
|
|
753
|
+
|
|
754
|
+
self.state.mist_level = level
|
|
755
|
+
self.state.mist_virtual_level = level
|
|
756
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
757
|
+
return True
|
|
758
|
+
|
|
759
|
+
async def toggle_switch(self, toggle: bool | None = None) -> bool:
|
|
760
|
+
if toggle is None:
|
|
761
|
+
toggle = self.state.device_status != DeviceStatus.ON
|
|
762
|
+
|
|
763
|
+
payload_data = {'powerSwitch': int(toggle), 'switchIdx': 0}
|
|
764
|
+
r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
|
|
765
|
+
r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
|
|
766
|
+
if r is None:
|
|
767
|
+
return False
|
|
768
|
+
|
|
769
|
+
self.state.device_status = DeviceStatus.from_bool(toggle)
|
|
770
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
771
|
+
return True
|
|
772
|
+
|
|
773
|
+
async def set_humidity(self, humidity: int) -> bool:
|
|
774
|
+
if not Validators.validate_range(humidity, *self.target_minmax):
|
|
775
|
+
logger.debug(
|
|
776
|
+
'Humidity value must be set between %s and %s',
|
|
777
|
+
self.target_minmax[0],
|
|
778
|
+
self.target_minmax[1],
|
|
779
|
+
)
|
|
780
|
+
return False
|
|
781
|
+
|
|
782
|
+
payload_data = {'targetHumidity': humidity}
|
|
783
|
+
r_dict = await self.call_bypassv2_api('setTargetHumidity', payload_data)
|
|
784
|
+
r = Helpers.process_dev_response(logger, 'set_humidity', self, r_dict)
|
|
785
|
+
if r is None:
|
|
786
|
+
return False
|
|
787
|
+
|
|
788
|
+
self.state.auto_target_humidity = humidity
|
|
789
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
790
|
+
return True
|
|
791
|
+
|
|
792
|
+
@deprecated('Use toggle_automatic_stop() instead.')
|
|
793
|
+
async def set_automatic_stop(self, mode: bool) -> bool:
|
|
794
|
+
return await self.toggle_automatic_stop(mode)
|
|
795
|
+
|
|
796
|
+
async def toggle_automatic_stop(self, toggle: bool | None = None) -> bool:
|
|
797
|
+
if toggle is None:
|
|
798
|
+
toggle = self.state.automatic_stop_config != DeviceStatus.ON
|
|
799
|
+
|
|
800
|
+
payload_data = {'autoStopSwitch': int(toggle)}
|
|
801
|
+
r_dict = await self.call_bypassv2_api('setAutoStopSwitch', payload_data)
|
|
802
|
+
r = Helpers.process_dev_response(logger, 'set_automatic_stop', self, r_dict)
|
|
803
|
+
|
|
804
|
+
if r is None:
|
|
805
|
+
return False
|
|
806
|
+
self.state.automatic_stop_config = toggle
|
|
807
|
+
self.state.connection_status = ConnectionStatus.ONLINE
|
|
808
|
+
return True
|