wyzeapy 0.5.28__py3-none-any.whl → 0.5.29__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.
- wyzeapy/__init__.py +267 -45
- wyzeapy/const.py +9 -4
- wyzeapy/crypto.py +31 -2
- wyzeapy/exceptions.py +11 -8
- wyzeapy/payload_factory.py +177 -172
- wyzeapy/services/__init__.py +3 -0
- wyzeapy/services/base_service.py +333 -212
- wyzeapy/services/bulb_service.py +67 -63
- wyzeapy/services/camera_service.py +136 -50
- wyzeapy/services/hms_service.py +8 -17
- wyzeapy/services/lock_service.py +5 -3
- wyzeapy/services/sensor_service.py +32 -11
- wyzeapy/services/switch_service.py +6 -2
- wyzeapy/services/thermostat_service.py +29 -15
- wyzeapy/services/update_manager.py +38 -11
- wyzeapy/services/wall_switch_service.py +18 -8
- wyzeapy/types.py +20 -12
- wyzeapy/utils.py +98 -17
- wyzeapy/wyze_auth_lib.py +195 -37
- wyzeapy-0.5.29.dist-info/METADATA +13 -0
- wyzeapy-0.5.29.dist-info/RECORD +22 -0
- {wyzeapy-0.5.28.dist-info → wyzeapy-0.5.29.dist-info}/WHEEL +1 -1
- wyzeapy/tests/test_bulb_service.py +0 -135
- wyzeapy/tests/test_camera_service.py +0 -180
- wyzeapy/tests/test_hms_service.py +0 -90
- wyzeapy/tests/test_lock_service.py +0 -114
- wyzeapy/tests/test_sensor_service.py +0 -159
- wyzeapy/tests/test_switch_service.py +0 -138
- wyzeapy/tests/test_thermostat_service.py +0 -136
- wyzeapy/tests/test_wall_switch_service.py +0 -161
- wyzeapy-0.5.28.dist-info/LICENSES/GPL-3.0-only.txt +0 -232
- wyzeapy-0.5.28.dist-info/METADATA +0 -16
- wyzeapy-0.5.28.dist-info/RECORD +0 -31
wyzeapy/__init__.py
CHANGED
|
@@ -7,10 +7,7 @@ import logging
|
|
|
7
7
|
from inspect import iscoroutinefunction
|
|
8
8
|
from typing import List, Optional, Set, Callable
|
|
9
9
|
|
|
10
|
-
from .const import PHONE_SYSTEM_TYPE, APP_VERSION, SC, APP_VER, SV, PHONE_ID, APP_NAME, OLIVE_APP_ID, APP_INFO
|
|
11
|
-
from .crypto import olive_create_signature
|
|
12
10
|
from .exceptions import TwoFactorAuthenticationEnabled
|
|
13
|
-
from .payload_factory import olive_create_user_info_payload
|
|
14
11
|
from .services.base_service import BaseService
|
|
15
12
|
from .services.bulb_service import BulbService
|
|
16
13
|
from .services.camera_service import CameraService
|
|
@@ -26,7 +23,22 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
26
23
|
|
|
27
24
|
|
|
28
25
|
class Wyzeapy:
|
|
29
|
-
"""A module to assist developers in interacting with the Wyze service
|
|
26
|
+
"""A Python module to assist developers in interacting with the Wyze service API.
|
|
27
|
+
|
|
28
|
+
This class provides methods for authentication, device management, and accessing
|
|
29
|
+
various Wyze device services including:
|
|
30
|
+
|
|
31
|
+
* **Bulbs** - Control brightness, color, and power state
|
|
32
|
+
* **Switches** - Toggle power and monitor usage
|
|
33
|
+
* **Cameras** - Access video streams and control settings
|
|
34
|
+
* **Thermostats** - Manage temperature settings and modes
|
|
35
|
+
* **Locks** - Control and monitor door locks
|
|
36
|
+
* **Sensors** - Monitor motion, contact, and environmental sensors
|
|
37
|
+
* **HMS** - Manage home monitoring system
|
|
38
|
+
|
|
39
|
+
Most interactions with Wyze devices should go through this class.
|
|
40
|
+
"""
|
|
41
|
+
|
|
30
42
|
# _client: Client
|
|
31
43
|
_auth_lib: WyzeAuthLib
|
|
32
44
|
|
|
@@ -50,9 +62,13 @@ class Wyzeapy:
|
|
|
50
62
|
@classmethod
|
|
51
63
|
async def create(cls):
|
|
52
64
|
"""
|
|
53
|
-
Creates the Wyzeapy class
|
|
65
|
+
Creates and initializes the Wyzeapy class asynchronously.
|
|
54
66
|
|
|
55
|
-
|
|
67
|
+
This factory method provides a way to instantiate the class using async/await syntax,
|
|
68
|
+
though it's currently a simple implementation that may be expanded in the future.
|
|
69
|
+
|
|
70
|
+
**Returns:**
|
|
71
|
+
`Wyzeapy`: A new instance of the Wyzeapy class ready for authentication.
|
|
56
72
|
"""
|
|
57
73
|
self = cls()
|
|
58
74
|
return self
|
|
@@ -61,16 +77,21 @@ class Wyzeapy:
|
|
|
61
77
|
self, email, password, key_id, api_key, token: Optional[Token] = None
|
|
62
78
|
):
|
|
63
79
|
"""
|
|
64
|
-
|
|
80
|
+
Authenticates with the Wyze API and retrieves the user's access token.
|
|
81
|
+
|
|
82
|
+
This method handles the authentication process, including token management
|
|
83
|
+
and service initialization. If two-factor authentication is enabled on the account,
|
|
84
|
+
it will raise an exception requiring the use of `login_with_2fa()` instead.
|
|
65
85
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
**Args:**
|
|
87
|
+
* `email` (str): User's email address for Wyze account
|
|
88
|
+
* `password` (str): User's password for Wyze account
|
|
89
|
+
* `key_id` (str): Key ID for third-party API access
|
|
90
|
+
* `api_key` (str): API Key for third-party API access
|
|
91
|
+
* `token` (Optional[Token], optional): Existing token from a previous session. Defaults to None.
|
|
71
92
|
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
**Raises:**
|
|
94
|
+
* `TwoFactorAuthenticationEnabled`: When the account has 2FA enabled and requires verification
|
|
74
95
|
"""
|
|
75
96
|
|
|
76
97
|
self._email = email
|
|
@@ -95,10 +116,17 @@ class Wyzeapy:
|
|
|
95
116
|
|
|
96
117
|
async def login_with_2fa(self, verification_code) -> Token:
|
|
97
118
|
"""
|
|
98
|
-
|
|
119
|
+
Completes the login process for accounts with two-factor authentication enabled.
|
|
120
|
+
|
|
121
|
+
This method should be called after receiving a `TwoFactorAuthenticationEnabled`
|
|
122
|
+
exception from the `login()` method. It completes the authentication process
|
|
123
|
+
using the verification code sent to the user.
|
|
99
124
|
|
|
100
|
-
|
|
125
|
+
**Args:**
|
|
126
|
+
* `verification_code` (str): The 2FA verification code received by the user
|
|
101
127
|
|
|
128
|
+
**Returns:**
|
|
129
|
+
* `Token`: The authenticated user token object
|
|
102
130
|
"""
|
|
103
131
|
|
|
104
132
|
_LOGGER.debug(f"Verification Code: {verification_code}")
|
|
@@ -109,10 +137,13 @@ class Wyzeapy:
|
|
|
109
137
|
|
|
110
138
|
async def execute_token_callbacks(self, token: Token):
|
|
111
139
|
"""
|
|
112
|
-
Sends the token to
|
|
140
|
+
Sends the token to all registered callback functions.
|
|
113
141
|
|
|
114
|
-
|
|
142
|
+
This method is called internally whenever the token is refreshed or updated,
|
|
143
|
+
allowing external components to stay in sync with token changes.
|
|
115
144
|
|
|
145
|
+
**Args:**
|
|
146
|
+
* `token` (Token): The current user token object
|
|
116
147
|
"""
|
|
117
148
|
for callback in self._token_callbacks:
|
|
118
149
|
if iscoroutinefunction(callback):
|
|
@@ -122,27 +153,52 @@ class Wyzeapy:
|
|
|
122
153
|
|
|
123
154
|
def register_for_token_callback(self, callback_function):
|
|
124
155
|
"""
|
|
125
|
-
|
|
156
|
+
Registers a callback function to be called whenever the user's token is modified.
|
|
126
157
|
|
|
127
|
-
|
|
158
|
+
This allows external components to be notified of token changes for persistence
|
|
159
|
+
or other token-dependent operations.
|
|
128
160
|
|
|
161
|
+
**Args:**
|
|
162
|
+
* `callback_function`: A function that accepts a Token object as its parameter
|
|
163
|
+
|
|
164
|
+
**Example:**
|
|
165
|
+
```python
|
|
166
|
+
def token_updated(token):
|
|
167
|
+
print(f"Token refreshed: {token.access_token[:10]}...")
|
|
168
|
+
|
|
169
|
+
wyze = Wyzeapy()
|
|
170
|
+
wyze.register_for_token_callback(token_updated)
|
|
171
|
+
```
|
|
129
172
|
"""
|
|
130
173
|
self._token_callbacks.append(callback_function)
|
|
131
174
|
|
|
132
175
|
def unregister_for_token_callback(self, callback_function):
|
|
133
176
|
"""
|
|
134
|
-
|
|
177
|
+
Removes a previously registered token callback function.
|
|
135
178
|
|
|
136
|
-
|
|
179
|
+
This stops the specified callback from receiving token updates.
|
|
137
180
|
|
|
181
|
+
**Args:**
|
|
182
|
+
* `callback_function`: The callback function to remove from the notification list
|
|
138
183
|
"""
|
|
139
184
|
self._token_callbacks.remove(callback_function)
|
|
140
185
|
|
|
141
186
|
@property
|
|
142
187
|
async def unique_device_ids(self) -> Set[str]:
|
|
143
188
|
"""
|
|
144
|
-
|
|
145
|
-
|
|
189
|
+
Retrieves a set of all unique device IDs known to the Wyze server.
|
|
190
|
+
|
|
191
|
+
This property fetches all devices associated with the account and
|
|
192
|
+
extracts their MAC addresses as unique identifiers.
|
|
193
|
+
|
|
194
|
+
**Returns:**
|
|
195
|
+
* `Set[str]`: A set containing all unique device IDs (MAC addresses)
|
|
196
|
+
|
|
197
|
+
**Example:**
|
|
198
|
+
```python
|
|
199
|
+
device_ids = await wyze.unique_device_ids
|
|
200
|
+
print(f"Found {len(device_ids)} devices")
|
|
201
|
+
```
|
|
146
202
|
"""
|
|
147
203
|
|
|
148
204
|
devices = await self._service.get_object_list()
|
|
@@ -155,21 +211,45 @@ class Wyzeapy:
|
|
|
155
211
|
@property
|
|
156
212
|
async def notifications_are_on(self) -> bool:
|
|
157
213
|
"""
|
|
158
|
-
|
|
214
|
+
Checks if push notifications are enabled for the account.
|
|
215
|
+
|
|
216
|
+
This property queries the user profile to determine the current
|
|
217
|
+
notification settings status.
|
|
159
218
|
|
|
160
|
-
|
|
219
|
+
**Returns:**
|
|
220
|
+
* `bool`: True if notifications are enabled, False otherwise
|
|
161
221
|
"""
|
|
162
222
|
|
|
163
223
|
response_json = await self._service.get_user_profile()
|
|
164
|
-
return response_json[
|
|
224
|
+
return response_json["data"]["notification"]
|
|
165
225
|
|
|
166
226
|
async def enable_notifications(self):
|
|
167
|
-
"""Enables notifications
|
|
227
|
+
"""Enables push notifications for the Wyze account.
|
|
228
|
+
|
|
229
|
+
This method updates the user's profile to turn on push notifications
|
|
230
|
+
for all supported devices and events.
|
|
231
|
+
|
|
232
|
+
**Example:**
|
|
233
|
+
```python
|
|
234
|
+
# Turn on notifications
|
|
235
|
+
await wyze.enable_notifications()
|
|
236
|
+
```
|
|
237
|
+
"""
|
|
168
238
|
|
|
169
239
|
await self._service.set_push_info(True)
|
|
170
240
|
|
|
171
241
|
async def disable_notifications(self):
|
|
172
|
-
"""Disables notifications
|
|
242
|
+
"""Disables push notifications for the Wyze account.
|
|
243
|
+
|
|
244
|
+
This method updates the user's profile to turn off push notifications
|
|
245
|
+
for all devices and events.
|
|
246
|
+
|
|
247
|
+
**Example:**
|
|
248
|
+
```python
|
|
249
|
+
# Turn off notifications
|
|
250
|
+
await wyze.disable_notifications()
|
|
251
|
+
```
|
|
252
|
+
"""
|
|
173
253
|
|
|
174
254
|
await self._service.set_push_info(False)
|
|
175
255
|
|
|
@@ -178,13 +258,29 @@ class Wyzeapy:
|
|
|
178
258
|
cls, email: str, password: str, key_id: str, api_key: str
|
|
179
259
|
) -> bool:
|
|
180
260
|
"""
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
261
|
+
Validates if the provided credentials can successfully authenticate with the Wyze API.
|
|
262
|
+
|
|
263
|
+
This method attempts to log in with the provided credentials and returns whether
|
|
264
|
+
the authentication was successful. It's useful for validating credentials without
|
|
265
|
+
needing to handle the full login process.
|
|
266
|
+
|
|
267
|
+
**Args:**
|
|
268
|
+
* `email` (str): The user's email address
|
|
269
|
+
* `password` (str): The user's password
|
|
270
|
+
* `key_id` (str): Key ID for third-party API access
|
|
271
|
+
* `api_key` (str): API Key for third-party API access
|
|
272
|
+
|
|
273
|
+
**Returns:**
|
|
274
|
+
* `bool`: True if the credentials are valid and authentication succeeded
|
|
275
|
+
|
|
276
|
+
**Example:**
|
|
277
|
+
```python
|
|
278
|
+
is_valid = await Wyzeapy.valid_login("user@example.com", "password123", "key_id", "api_key")
|
|
279
|
+
if is_valid:
|
|
280
|
+
print("Credentials are valid")
|
|
281
|
+
else:
|
|
282
|
+
print("Invalid credentials")
|
|
283
|
+
```
|
|
188
284
|
"""
|
|
189
285
|
|
|
190
286
|
self = cls()
|
|
@@ -194,7 +290,21 @@ class Wyzeapy:
|
|
|
194
290
|
|
|
195
291
|
@property
|
|
196
292
|
async def bulb_service(self) -> BulbService:
|
|
197
|
-
"""
|
|
293
|
+
"""Provides access to the Wyze Bulb service.
|
|
294
|
+
|
|
295
|
+
This property lazily initializes and returns a BulbService instance
|
|
296
|
+
for controlling and monitoring Wyze bulbs.
|
|
297
|
+
|
|
298
|
+
**Returns:**
|
|
299
|
+
* `BulbService`: An instance of the bulb service for interacting with Wyze bulbs
|
|
300
|
+
|
|
301
|
+
**Example:**
|
|
302
|
+
```python
|
|
303
|
+
# Get all bulbs
|
|
304
|
+
bulb_service = await wyze.bulb_service
|
|
305
|
+
bulbs = await bulb_service.get_bulbs()
|
|
306
|
+
```
|
|
307
|
+
"""
|
|
198
308
|
|
|
199
309
|
if self._bulb_service is None:
|
|
200
310
|
self._bulb_service = BulbService(self._auth_lib)
|
|
@@ -202,7 +312,21 @@ class Wyzeapy:
|
|
|
202
312
|
|
|
203
313
|
@property
|
|
204
314
|
async def switch_service(self) -> SwitchService:
|
|
205
|
-
"""
|
|
315
|
+
"""Provides access to the Wyze Switch service.
|
|
316
|
+
|
|
317
|
+
This property lazily initializes and returns a SwitchService instance
|
|
318
|
+
for controlling and monitoring Wyze plugs and switches.
|
|
319
|
+
|
|
320
|
+
**Returns:**
|
|
321
|
+
* `SwitchService`: An instance of the switch service for interacting with Wyze switches
|
|
322
|
+
|
|
323
|
+
**Example:**
|
|
324
|
+
```python
|
|
325
|
+
# Get all switches
|
|
326
|
+
switch_service = await wyze.switch_service
|
|
327
|
+
switches = await switch_service.get_switches()
|
|
328
|
+
```
|
|
329
|
+
"""
|
|
206
330
|
|
|
207
331
|
if self._switch_service is None:
|
|
208
332
|
self._switch_service = SwitchService(self._auth_lib)
|
|
@@ -210,7 +334,21 @@ class Wyzeapy:
|
|
|
210
334
|
|
|
211
335
|
@property
|
|
212
336
|
async def camera_service(self) -> CameraService:
|
|
213
|
-
"""
|
|
337
|
+
"""Provides access to the Wyze Camera service.
|
|
338
|
+
|
|
339
|
+
This property lazily initializes and returns a CameraService instance
|
|
340
|
+
for controlling and monitoring Wyze cameras.
|
|
341
|
+
|
|
342
|
+
**Returns:**
|
|
343
|
+
* `CameraService`: An instance of the camera service for interacting with Wyze cameras
|
|
344
|
+
|
|
345
|
+
**Example:**
|
|
346
|
+
```python
|
|
347
|
+
# Get all cameras
|
|
348
|
+
camera_service = await wyze.camera_service
|
|
349
|
+
cameras = await camera_service.get_cameras()
|
|
350
|
+
```
|
|
351
|
+
"""
|
|
214
352
|
|
|
215
353
|
if self._camera_service is None:
|
|
216
354
|
self._camera_service = CameraService(self._auth_lib)
|
|
@@ -218,7 +356,21 @@ class Wyzeapy:
|
|
|
218
356
|
|
|
219
357
|
@property
|
|
220
358
|
async def thermostat_service(self) -> ThermostatService:
|
|
221
|
-
"""
|
|
359
|
+
"""Provides access to the Wyze Thermostat service.
|
|
360
|
+
|
|
361
|
+
This property lazily initializes and returns a ThermostatService instance
|
|
362
|
+
for controlling and monitoring Wyze thermostats.
|
|
363
|
+
|
|
364
|
+
**Returns:**
|
|
365
|
+
* `ThermostatService`: An instance of the thermostat service for interacting with Wyze thermostats
|
|
366
|
+
|
|
367
|
+
**Example:**
|
|
368
|
+
```python
|
|
369
|
+
# Get all thermostats
|
|
370
|
+
thermostat_service = await wyze.thermostat_service
|
|
371
|
+
thermostats = await thermostat_service.get_thermostats()
|
|
372
|
+
```
|
|
373
|
+
"""
|
|
222
374
|
|
|
223
375
|
if self._thermostat_service is None:
|
|
224
376
|
self._thermostat_service = ThermostatService(self._auth_lib)
|
|
@@ -226,7 +378,21 @@ class Wyzeapy:
|
|
|
226
378
|
|
|
227
379
|
@property
|
|
228
380
|
async def hms_service(self) -> HMSService:
|
|
229
|
-
"""
|
|
381
|
+
"""Provides access to the Wyze Home Monitoring Service (HMS).
|
|
382
|
+
|
|
383
|
+
This property lazily initializes and returns an HMSService instance
|
|
384
|
+
for controlling and monitoring the Wyze home security system.
|
|
385
|
+
|
|
386
|
+
**Returns:**
|
|
387
|
+
* `HMSService`: An instance of the HMS service for interacting with Wyze home monitoring
|
|
388
|
+
|
|
389
|
+
**Example:**
|
|
390
|
+
```python
|
|
391
|
+
# Get HMS status
|
|
392
|
+
hms_service = await wyze.hms_service
|
|
393
|
+
status = await hms_service.get_hms_status()
|
|
394
|
+
```
|
|
395
|
+
"""
|
|
230
396
|
|
|
231
397
|
if self._hms_service is None:
|
|
232
398
|
self._hms_service = await HMSService.create(self._auth_lib)
|
|
@@ -234,7 +400,21 @@ class Wyzeapy:
|
|
|
234
400
|
|
|
235
401
|
@property
|
|
236
402
|
async def lock_service(self) -> LockService:
|
|
237
|
-
"""
|
|
403
|
+
"""Provides access to the Wyze Lock service.
|
|
404
|
+
|
|
405
|
+
This property lazily initializes and returns a LockService instance
|
|
406
|
+
for controlling and monitoring Wyze locks.
|
|
407
|
+
|
|
408
|
+
**Returns:**
|
|
409
|
+
* `LockService`: An instance of the lock service for interacting with Wyze locks
|
|
410
|
+
|
|
411
|
+
**Example:**
|
|
412
|
+
```python
|
|
413
|
+
# Get all locks
|
|
414
|
+
lock_service = await wyze.lock_service
|
|
415
|
+
locks = await lock_service.get_locks()
|
|
416
|
+
```
|
|
417
|
+
"""
|
|
238
418
|
|
|
239
419
|
if self._lock_service is None:
|
|
240
420
|
self._lock_service = LockService(self._auth_lib)
|
|
@@ -242,7 +422,21 @@ class Wyzeapy:
|
|
|
242
422
|
|
|
243
423
|
@property
|
|
244
424
|
async def sensor_service(self) -> SensorService:
|
|
245
|
-
"""
|
|
425
|
+
"""Provides access to the Wyze Sensor service.
|
|
426
|
+
|
|
427
|
+
This property lazily initializes and returns a SensorService instance
|
|
428
|
+
for monitoring Wyze sensors such as contact sensors, motion sensors, etc.
|
|
429
|
+
|
|
430
|
+
**Returns:**
|
|
431
|
+
* `SensorService`: An instance of the sensor service for interacting with Wyze sensors
|
|
432
|
+
|
|
433
|
+
**Example:**
|
|
434
|
+
```python
|
|
435
|
+
# Get all sensors
|
|
436
|
+
sensor_service = await wyze.sensor_service
|
|
437
|
+
sensors = await sensor_service.get_sensors()
|
|
438
|
+
```
|
|
439
|
+
"""
|
|
246
440
|
|
|
247
441
|
if self._sensor_service is None:
|
|
248
442
|
self._sensor_service = SensorService(self._auth_lib)
|
|
@@ -250,7 +444,21 @@ class Wyzeapy:
|
|
|
250
444
|
|
|
251
445
|
@property
|
|
252
446
|
async def wall_switch_service(self) -> WallSwitchService:
|
|
253
|
-
"""
|
|
447
|
+
"""Provides access to the Wyze Wall Switch service.
|
|
448
|
+
|
|
449
|
+
This property lazily initializes and returns a WallSwitchService instance
|
|
450
|
+
for controlling and monitoring Wyze wall switches.
|
|
451
|
+
|
|
452
|
+
**Returns:**
|
|
453
|
+
* `WallSwitchService`: An instance of the wall switch service for interacting with Wyze wall switches
|
|
454
|
+
|
|
455
|
+
**Example:**
|
|
456
|
+
```python
|
|
457
|
+
# Get all wall switches
|
|
458
|
+
wall_switch_service = await wyze.wall_switch_service
|
|
459
|
+
switches = await wall_switch_service.get_wall_switches()
|
|
460
|
+
```
|
|
461
|
+
"""
|
|
254
462
|
|
|
255
463
|
if self._wall_switch_service is None:
|
|
256
464
|
self._wall_switch_service = WallSwitchService(self._auth_lib)
|
|
@@ -258,7 +466,21 @@ class Wyzeapy:
|
|
|
258
466
|
|
|
259
467
|
@property
|
|
260
468
|
async def switch_usage_service(self) -> SwitchUsageService:
|
|
261
|
-
"""
|
|
469
|
+
"""Provides access to the Wyze Switch Usage service.
|
|
470
|
+
|
|
471
|
+
This property lazily initializes and returns a SwitchUsageService instance
|
|
472
|
+
for retrieving usage statistics from Wyze switches and plugs.
|
|
473
|
+
|
|
474
|
+
**Returns:**
|
|
475
|
+
* `SwitchUsageService`: An instance of the switch usage service for accessing Wyze switch usage data
|
|
476
|
+
|
|
477
|
+
**Example:**
|
|
478
|
+
```python
|
|
479
|
+
# Get usage data for a switch
|
|
480
|
+
usage_service = await wyze.switch_usage_service
|
|
481
|
+
usage = await usage_service.get_usage_records(switch_mac)
|
|
482
|
+
```
|
|
483
|
+
"""
|
|
262
484
|
if self._switch_usage_service is None:
|
|
263
485
|
self._switch_usage_service = SwitchUsageService(self._auth_lib)
|
|
264
486
|
return self._switch_usage_service
|
wyzeapy/const.py
CHANGED
|
@@ -3,16 +3,21 @@
|
|
|
3
3
|
# of the attached license. You should have received a copy of
|
|
4
4
|
# the license with this file. If not, please write to:
|
|
5
5
|
# katie@mulliken.net to receive a copy
|
|
6
|
+
# Standard library imports
|
|
6
7
|
import uuid
|
|
7
8
|
|
|
8
|
-
#
|
|
9
|
+
# Module constants for application identification, API credentials, and crypto secrets
|
|
10
|
+
"""
|
|
11
|
+
Configuration constants for Wyze API integration, including app metadata,
|
|
12
|
+
API keys, and cryptographic secrets.
|
|
13
|
+
"""
|
|
9
14
|
PHONE_SYSTEM_TYPE = "1"
|
|
10
15
|
API_KEY = "WMXHYf79Nr5gIlt3r0r7p9Tcw5bvs6BB4U8O8nGJ"
|
|
11
16
|
APP_VERSION = "2.18.43"
|
|
12
17
|
APP_VER = "com.hualai.WyzeCam___2.18.43"
|
|
13
18
|
APP_NAME = "com.hualai.WyzeCam"
|
|
14
19
|
PHONE_ID = str(uuid.uuid4())
|
|
15
|
-
APP_INFO =
|
|
20
|
+
APP_INFO = "wyze_android_2.19.14" # Required for the thermostat
|
|
16
21
|
SC = "9f275790cab94a72bd206c8876429f3c"
|
|
17
22
|
SV = "9d74946e652647e9b6c9d59326aef104"
|
|
18
23
|
CLIENT_VER = "2"
|
|
@@ -20,7 +25,7 @@ SOURCE = "ios/WZCameraSDK"
|
|
|
20
25
|
APP_PLATFORM = "ios"
|
|
21
26
|
|
|
22
27
|
# Crypto secrets
|
|
23
|
-
OLIVE_SIGNING_SECRET =
|
|
24
|
-
OLIVE_APP_ID =
|
|
28
|
+
OLIVE_SIGNING_SECRET = "wyze_app_secret_key_132" # Required for the thermostat
|
|
29
|
+
OLIVE_APP_ID = "9319141212m2ik" # Required for the thermostat
|
|
25
30
|
FORD_APP_KEY = "275965684684dbdaf29a0ed9" # Required for the locks
|
|
26
31
|
FORD_APP_SECRET = "4deekof1ba311c5c33a9cb8e12787e8c" # Required for the locks
|
wyzeapy/crypto.py
CHANGED
|
@@ -10,8 +10,24 @@ from typing import Dict, Union, Any
|
|
|
10
10
|
|
|
11
11
|
from .const import FORD_APP_SECRET, OLIVE_SIGNING_SECRET
|
|
12
12
|
|
|
13
|
+
"""
|
|
14
|
+
Cryptographic helper functions for creating API request signatures.
|
|
15
|
+
"""
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
|
|
18
|
+
def olive_create_signature(
|
|
19
|
+
payload: Union[Dict[Any, Any], str], access_token: str
|
|
20
|
+
) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Compute the olive (Wyze) API request signature using HMAC-MD5.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
payload: The request payload as a dict or raw string.
|
|
26
|
+
access_token: The access token string for signing.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
The computed signature as a hex string.
|
|
30
|
+
"""
|
|
15
31
|
if isinstance(payload, dict):
|
|
16
32
|
body = ""
|
|
17
33
|
for item in sorted(payload):
|
|
@@ -28,7 +44,20 @@ def olive_create_signature(payload: Union[Dict[Any, Any], str], access_token: st
|
|
|
28
44
|
return hmac.new(secret.encode(), body.encode(), hashlib.md5).hexdigest()
|
|
29
45
|
|
|
30
46
|
|
|
31
|
-
def ford_create_signature(
|
|
47
|
+
def ford_create_signature(
|
|
48
|
+
url_path: str, request_method: str, payload: Dict[Any, Any]
|
|
49
|
+
) -> str:
|
|
50
|
+
"""
|
|
51
|
+
Compute the ford (Lock) API request signature using MD5 of URL-encoded buffer.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
url_path: The URL path of the request.
|
|
55
|
+
request_method: HTTP method (e.g., 'GET', 'POST').
|
|
56
|
+
payload: The request payload dict to include in the signature.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The computed signature as a hex string.
|
|
60
|
+
"""
|
|
32
61
|
string_buf = request_method + url_path
|
|
33
62
|
for entry in sorted(payload.keys()):
|
|
34
63
|
string_buf += entry + "=" + payload[entry] + "&"
|
wyzeapy/exceptions.py
CHANGED
|
@@ -3,31 +3,34 @@
|
|
|
3
3
|
# of the attached license. You should have received a copy of
|
|
4
4
|
# the license with this file. If not, please write to:
|
|
5
5
|
# katie@mulliken.net to receive a copy
|
|
6
|
-
|
|
6
|
+
"""
|
|
7
|
+
Custom exception classes for Wyzeapy API interactions.
|
|
8
|
+
"""
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class ActionNotSupported(Exception):
|
|
10
|
-
|
|
11
|
-
message = "The action specified is not supported by device type: {}".format(device_type)
|
|
12
|
+
"""Raised when an unsupported action is requested for a device type."""
|
|
12
13
|
|
|
14
|
+
def __init__(self, device_type: str):
|
|
15
|
+
message = f"The action specified is not supported by device type: {device_type}"
|
|
13
16
|
super().__init__(message)
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class ParameterError(Exception):
|
|
17
|
-
|
|
20
|
+
"""Raised when invalid parameters are provided to an API call."""
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
class AccessTokenError(Exception):
|
|
21
|
-
|
|
24
|
+
"""Raised when the access token is invalid or has expired."""
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
class LoginError(Exception):
|
|
25
|
-
|
|
28
|
+
"""Raised during authentication or login failures."""
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
class UnknownApiError(Exception):
|
|
29
|
-
|
|
32
|
+
"""Raised for unexpected or generic API errors."""
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
class TwoFactorAuthenticationEnabled(Exception):
|
|
33
|
-
|
|
36
|
+
"""Raised when two-factor authentication is required for login."""
|