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,315 @@
1
+ """Base Devices for Outlets."""
2
+ from __future__ import annotations
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+ from pyvesync.const import OutletFeatures, IntFlag, StrFlag, NightlightModes
7
+ from pyvesync.utils.helpers import Helpers
8
+ from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseToggleDevice, DeviceState
9
+ from pyvesync.models.outlet_models import RequestEnergyHistory, ResponseEnergyHistory
10
+ from pyvesync.models.base_models import DefaultValues
11
+
12
+ if TYPE_CHECKING:
13
+ from pyvesync import VeSync
14
+ from pyvesync.device_map import OutletMap
15
+ from pyvesync.models.vesync_models import ResponseDeviceDetailsModel
16
+ from pyvesync.models.outlet_models import ResponseEnergyResult
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class OutletState(DeviceState):
22
+ """Base state class for Outlets.
23
+
24
+ This class holds all of the state information for the outlet devices. The state
25
+ instance is stored in the `state` attribute of the outlet device. This is only
26
+ for holding state information and does not contain any methods for controlling
27
+ the device or retrieving information from the API.
28
+
29
+ Args:
30
+ device (VeSyncOutlet): The device object.
31
+ details (ResponseDeviceDetailsModel): The device details.
32
+ feature_map (OutletMap): The feature map for the device.
33
+
34
+ Attributes:
35
+ active_time (int): Active time of device, defaults to None.
36
+ connection_status (str): Connection status of device.
37
+ device (VeSyncOutlet): Device object.
38
+ device_status (str): Device status.
39
+ features (dict): Features of device.
40
+ last_update_ts (int): Last update timestamp of device, defaults to None.
41
+ energy (float): Energy usage in kWh.
42
+ monthly_history (ResponseEnergyResult): Monthly energy history.
43
+ nightlight_automode (str): Nightlight automode status.
44
+ nightlight_brightness (int): Nightlight brightness level.
45
+ nightlight_status (str): Nightlight status.
46
+ power (float): Power usage in Watts.
47
+ voltage (float): Voltage in Volts.
48
+ weekly_history (ResponseEnergyResult): Weekly energy history.
49
+ yearly_history (ResponseEnergyResult): Yearly energy history.
50
+
51
+ Methods:
52
+ update_ts: Update last update timestamp.
53
+ to_dict: Dump state to JSON.
54
+ to_json: Dump state to JSON string.
55
+ to_jsonb: Dump state to JSON bytes.
56
+ as_tuple: Convert state to tuple of (name, value) tuples.
57
+
58
+ Note:
59
+ Not all attributes are available on all devices. Some attributes may be None or
60
+ not supported depending on the device type and features. The attributes are set
61
+ based on the device features and the API response.
62
+ """
63
+
64
+ __slots__ = (
65
+ "energy",
66
+ "monthly_history",
67
+ "nightlight_automode",
68
+ "nightlight_brightness",
69
+ "nightlight_status",
70
+ "power",
71
+ "voltage",
72
+ "weekly_history",
73
+ "yearly_history",
74
+ )
75
+
76
+ def __init__(
77
+ self,
78
+ device: VeSyncOutlet,
79
+ details: ResponseDeviceDetailsModel,
80
+ feature_map: OutletMap
81
+ ) -> None:
82
+ """Initialize VeSync Switch State."""
83
+ super().__init__(device, details, feature_map)
84
+ self._exclude_serialization = [
85
+ "weakly_history",
86
+ "monthly_history",
87
+ "yearly_history",
88
+ ]
89
+ self.device: VeSyncOutlet = device
90
+ self.features: list[str] = feature_map.features
91
+ self.active_time: int | None = 0
92
+ self.power: float = IntFlag.NOT_SUPPORTED
93
+ self.energy: float = IntFlag.NOT_SUPPORTED
94
+ self.voltage: float = IntFlag.NOT_SUPPORTED
95
+ self.nightlight_status: str | None = StrFlag.NOT_SUPPORTED
96
+ self.nightlight_brightness: int | None = IntFlag.NOT_SUPPORTED
97
+ self.nightlight_automode: str | None = StrFlag.NOT_SUPPORTED
98
+ self.weekly_history: ResponseEnergyResult | None = None
99
+ self.monthly_history: ResponseEnergyResult | None = None
100
+ self.yearly_history: ResponseEnergyResult | None = None
101
+
102
+ def annual_history_to_json(self) -> None | str:
103
+ """Dump annual history."""
104
+ if not self.device.supports_energy:
105
+ logger.info("Device does not support energy monitoring.")
106
+ return None
107
+ if self.yearly_history is None:
108
+ logger.info("No yearly history available, run device.get_yearly_history().")
109
+ return None
110
+ return self.yearly_history.to_json()
111
+
112
+ def monthly_history_to_json(self) -> None | str:
113
+ """Dump monthly history."""
114
+ if not self.device.supports_energy:
115
+ logger.info("Device does not support energy monitoring.")
116
+ return None
117
+ if self.monthly_history is None:
118
+ logger.info("No monthly history available, run device.get_monthly_history().")
119
+ return None
120
+ return self.monthly_history.to_json()
121
+
122
+ def weekly_history_to_json(self) -> None | str:
123
+ """Dump weekly history."""
124
+ if not self.device.supports_energy:
125
+ logger.info("Device does not support energy monitoring.")
126
+ return None
127
+ if self.weekly_history is None:
128
+ logger.info("No weekly history available, run device.get_weekly_history().")
129
+ return None
130
+ return self.weekly_history.to_json()
131
+
132
+
133
+ class VeSyncOutlet(VeSyncBaseToggleDevice):
134
+ """Base class for Etekcity Outlets.
135
+
136
+ State is stored in the `state` attribute of the device.
137
+ This is only for holding state information and does not
138
+ contain any methods for controlling the device or retrieving
139
+ information from the API.
140
+
141
+ Args:
142
+ details (ResponseDeviceDetailsModel): The device details.
143
+ manager (VeSync): The VeSync manager.
144
+ feature_map (OutletMap): The feature map for the device.
145
+
146
+ Attributes:
147
+ state (OutletState): The state of the outlet.
148
+ last_response (ResponseInfo): Last response from API call.
149
+ manager (VeSync): Manager object for API calls.
150
+ device_name (str): Name of device.
151
+ device_image (str): URL for device image.
152
+ cid (str): Device ID.
153
+ connection_type (str): Connection type of device.
154
+ device_type (str): Type of device.
155
+ type (str): Type of device.
156
+ uuid (str): UUID of device, not always present.
157
+ config_module (str): Configuration module of device.
158
+ mac_id (str): MAC ID of device.
159
+ current_firm_version (str): Current firmware version of device.
160
+ device_region (str): Region of device. (US, EU, etc.)
161
+ pid (str): Product ID of device, pulled by some devices on update.
162
+ sub_device_no (int): Sub-device number of device.
163
+ product_type (str): Product type of device.
164
+ features (dict): Features of device.
165
+ """
166
+
167
+ # __metaclass__ = ABCMeta
168
+
169
+ def __init__(self, details: ResponseDeviceDetailsModel,
170
+ manager: VeSync, feature_map: OutletMap) -> None:
171
+ """Initialize VeSync Outlet base class."""
172
+ super().__init__(details, manager, feature_map)
173
+ self.state: OutletState = OutletState(self, details, feature_map)
174
+ self.nightlight_modes = feature_map.nightlight_modes
175
+
176
+ def _build_energy_request(self, method: str) -> RequestEnergyHistory:
177
+ """Build energy request post."""
178
+ request_keys = ["acceptLanguage", "accountID", "appVersion", "phoneBrand",
179
+ "phoneOS", "timeZone", "token", "traceId", "userCountryCode",
180
+ "debugMode", "homeTimeZone", "uuid"]
181
+ body = Helpers.get_class_attributes(DefaultValues, request_keys)
182
+ body.update(Helpers.get_class_attributes(self.manager, request_keys))
183
+ body.update(Helpers.get_class_attributes(self, request_keys))
184
+ body['method'] = method
185
+ return RequestEnergyHistory.from_dict(body)
186
+
187
+ async def _get_energy_history(self, history_interval: str) -> None:
188
+ """Internal function to pull energy history.
189
+
190
+ Args:
191
+ history_interval (str): The interval for the energy history,
192
+ options are 'getLastWeekEnergy', 'getLastMonthEnergy', 'getLastYearEnergy'
193
+
194
+ Note:
195
+ Builds the state.<history_interval>_history attribute.
196
+ """
197
+ if not self.supports_energy:
198
+ logger.debug("Device does not support energy monitoring.")
199
+ return
200
+ history_intervals = [
201
+ 'getLastWeekEnergy', 'getLastMonthEnergy', 'getLastYearEnergy'
202
+ ]
203
+ if history_interval not in history_intervals:
204
+ logger.debug("Invalid history interval: %s", history_interval)
205
+ return
206
+ body = self._build_energy_request(history_interval)
207
+ headers = Helpers.req_header_bypass()
208
+ r_bytes, _ = await self.manager.async_call_api(
209
+ f'/cloud/v1/device/{history_interval}',
210
+ 'post',
211
+ headers=headers,
212
+ json_object=body.to_dict(),
213
+ )
214
+
215
+ r = Helpers.process_dev_response(
216
+ logger, history_interval, self, r_bytes
217
+ )
218
+ if r is None:
219
+ return
220
+ response = ResponseEnergyHistory.from_dict(r)
221
+ match history_interval:
222
+ case 'getLastWeekEnergy':
223
+ self.state.weekly_history = response.result
224
+ case 'getLastMonthEnergy':
225
+ self.state.monthly_history = response.result
226
+ case 'getLastYearEnergy':
227
+ self.state.yearly_history = response.result
228
+
229
+ @property
230
+ def supports_nightlight(self) -> bool:
231
+ """Return True if device supports nightlight.
232
+
233
+ Returns:
234
+ bool: True if device supports nightlight, False otherwise.
235
+ """
236
+ return OutletFeatures.NIGHTLIGHT in self.features
237
+
238
+ @property
239
+ def supports_energy(self) -> bool:
240
+ """Return True if device supports energy.
241
+
242
+ Returns:
243
+ bool: True if device supports energy, False otherwise.
244
+ """
245
+ return OutletFeatures.ENERGY_MONITOR in self.features
246
+
247
+ async def get_weekly_energy(self) -> None:
248
+ """Build weekly energy history dictionary.
249
+
250
+ The data is stored in the `device.state.weekly_history` attribute
251
+ as a `ResponseEnergyResult` object.
252
+ """
253
+ await self._get_energy_history('getLastWeekEnergy')
254
+
255
+ async def get_monthly_energy(self) -> None:
256
+ """Build Monthly Energy History Dictionary.
257
+
258
+ The data is stored in the `device.state.monthly_history` attribute
259
+ as a `ResponseEnergyResult` object.
260
+ """
261
+ await self._get_energy_history('getLastMonthEnergy')
262
+
263
+ async def get_yearly_energy(self) -> None:
264
+ """Build Yearly Energy Dictionary.
265
+
266
+ The data is stored in the `device.state.yearly_history` attribute
267
+ as a `ResponseEnergyResult` object.
268
+ """
269
+ await self._get_energy_history('getLastYearEnergy')
270
+
271
+ async def update_energy(self) -> None:
272
+ """Build weekly, monthly and yearly dictionaries."""
273
+ if self.supports_energy:
274
+ await self.get_weekly_energy()
275
+ await self.get_monthly_energy()
276
+ await self.get_yearly_energy()
277
+
278
+ async def set_nightlight_state(self, mode: str) -> bool:
279
+ """Set nightlight mode.
280
+
281
+ Available nightlight states are found in the `device.nightlight_modes` attribute.
282
+
283
+ Args:
284
+ mode (str): Nightlight mode to set.
285
+
286
+ Returns:
287
+ bool: True if nightlight mode set successfully, False otherwise.
288
+ """
289
+ del mode # unused
290
+ if not self.supports_nightlight:
291
+ logger.debug("Device does not support nightlight.")
292
+ else:
293
+ logger.debug("Nightlight mode not configured for %s", self.device_name)
294
+ return False
295
+
296
+ async def turn_on_nightlight(self) -> bool:
297
+ """Turn on nightlight if supported."""
298
+ if not self.supports_nightlight:
299
+ logger.debug("Device does not support nightlight.")
300
+ return False
301
+ return await self.set_nightlight_state(NightlightModes.ON)
302
+
303
+ async def turn_off_nightlight(self) -> bool:
304
+ """Turn off nightlight if supported."""
305
+ if not self.supports_nightlight:
306
+ logger.debug("Device does not support nightlight.")
307
+ return False
308
+ return await self.set_nightlight_state(NightlightModes.OFF)
309
+
310
+ async def set_nightlight_auto(self) -> bool:
311
+ """Set nightlight to auto mode."""
312
+ if not self.supports_nightlight:
313
+ logger.debug("Device does not support nightlight.")
314
+ return False
315
+ return await self.set_nightlight_state(NightlightModes.AUTO)