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