wyzeapy 0.5.27__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 +348 -208
- 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 +13 -2
- 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 +110 -14
- 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.27.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.27.dist-info/LICENSES/GPL-3.0-only.txt +0 -232
- wyzeapy-0.5.27.dist-info/METADATA +0 -15
- wyzeapy-0.5.27.dist-info/RECORD +0 -31
wyzeapy/services/base_service.py
CHANGED
|
@@ -12,49 +12,160 @@ from typing import List, Tuple, Any, Dict, Optional
|
|
|
12
12
|
import aiohttp
|
|
13
13
|
|
|
14
14
|
from .update_manager import DeviceUpdater, UpdateManager
|
|
15
|
-
from ..const import
|
|
15
|
+
from ..const import (
|
|
16
|
+
PHONE_SYSTEM_TYPE,
|
|
17
|
+
APP_VERSION,
|
|
18
|
+
APP_VER,
|
|
19
|
+
PHONE_ID,
|
|
20
|
+
APP_NAME,
|
|
21
|
+
OLIVE_APP_ID,
|
|
22
|
+
APP_INFO,
|
|
23
|
+
SC,
|
|
24
|
+
SV,
|
|
25
|
+
APP_PLATFORM,
|
|
26
|
+
SOURCE,
|
|
27
|
+
)
|
|
16
28
|
from ..crypto import olive_create_signature
|
|
17
|
-
from ..payload_factory import
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
from ..payload_factory import (
|
|
30
|
+
olive_create_hms_patch_payload,
|
|
31
|
+
olive_create_hms_payload,
|
|
32
|
+
olive_create_hms_get_payload,
|
|
33
|
+
ford_create_payload,
|
|
34
|
+
olive_create_get_payload,
|
|
35
|
+
olive_create_post_payload,
|
|
36
|
+
olive_create_user_info_payload,
|
|
37
|
+
devicemgmt_create_capabilities_payload,
|
|
38
|
+
devicemgmt_get_iot_props_list,
|
|
39
|
+
)
|
|
20
40
|
from ..types import PropertyIDs, Device, DeviceMgmtToggleType
|
|
21
|
-
from ..utils import
|
|
22
|
-
|
|
41
|
+
from ..utils import (
|
|
42
|
+
check_for_errors_standard,
|
|
43
|
+
check_for_errors_hms,
|
|
44
|
+
check_for_errors_lock,
|
|
45
|
+
check_for_errors_iot,
|
|
46
|
+
wyze_encrypt,
|
|
47
|
+
check_for_errors_devicemgmt,
|
|
48
|
+
)
|
|
23
49
|
from ..wyze_auth_lib import WyzeAuthLib
|
|
24
50
|
|
|
51
|
+
"""
|
|
52
|
+
BaseService provides shared functionality for Wyze device services,
|
|
53
|
+
including authentication, device discovery, and automatic updates.
|
|
54
|
+
"""
|
|
55
|
+
|
|
25
56
|
_LOGGER = logging.getLogger(__name__)
|
|
26
57
|
|
|
27
58
|
|
|
28
59
|
class BaseService:
|
|
60
|
+
"""Base service class providing common functionality for all Wyze device services.
|
|
61
|
+
|
|
62
|
+
This abstract base class provides shared infrastructure for interacting with Wyze devices
|
|
63
|
+
including authentication, API communication, device discovery, and automatic updates.
|
|
64
|
+
All device-specific service classes (BulbService, SwitchService, etc.) inherit from this class.
|
|
65
|
+
|
|
66
|
+
**Key Features:**
|
|
67
|
+
* Device discovery and caching via `get_object_list()`
|
|
68
|
+
* Automatic device updates with configurable intervals
|
|
69
|
+
* Authentication token management and refresh
|
|
70
|
+
* Common API endpoint wrappers for device properties and actions
|
|
71
|
+
* Error handling and validation for API responses
|
|
72
|
+
* Support for both cloud and local device communication
|
|
73
|
+
|
|
74
|
+
**Common Usage Patterns:**
|
|
75
|
+
```python
|
|
76
|
+
# Device services inherit from BaseService
|
|
77
|
+
bulb_service = await wyze.bulb_service
|
|
78
|
+
|
|
79
|
+
# Get all devices (cached after first call)
|
|
80
|
+
devices = await bulb_service.get_object_list()
|
|
81
|
+
|
|
82
|
+
# Register for automatic updates
|
|
83
|
+
bulb_service.register_updater(device, interval=30)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Note:** This class is not meant to be instantiated directly - use device-specific services instead.
|
|
87
|
+
"""
|
|
88
|
+
|
|
29
89
|
_devices: Optional[List[Device]] = None
|
|
30
|
-
_last_updated_time: time =
|
|
90
|
+
_last_updated_time: time = (
|
|
91
|
+
0 # preload a value of 0 so that comparison will succeed on the first run
|
|
92
|
+
)
|
|
31
93
|
_min_update_time = 1200 # lets let the device_params update every 20 minutes for now. This could probably reduced signicficantly.
|
|
32
|
-
_update_lock: asyncio.Lock = asyncio.Lock()
|
|
94
|
+
_update_lock: asyncio.Lock = asyncio.Lock() # fmt: skip
|
|
33
95
|
_update_manager: UpdateManager = UpdateManager()
|
|
34
96
|
_update_loop = None
|
|
35
97
|
_updater: DeviceUpdater = None
|
|
36
98
|
_updater_dict = {}
|
|
37
99
|
|
|
38
100
|
def __init__(self, auth_lib: WyzeAuthLib):
|
|
101
|
+
"""Initialize the base service with authentication.
|
|
102
|
+
|
|
103
|
+
**Args:**
|
|
104
|
+
* `auth_lib` (WyzeAuthLib): The authentication library for API access
|
|
105
|
+
"""
|
|
39
106
|
self._auth_lib = auth_lib
|
|
40
107
|
|
|
41
108
|
@staticmethod
|
|
42
109
|
async def start_update_manager():
|
|
110
|
+
"""Start the global update manager for automatic device state updates.
|
|
111
|
+
|
|
112
|
+
This initializes the background update system that handles periodic
|
|
113
|
+
device state refreshes for all registered devices.
|
|
114
|
+
|
|
115
|
+
**Example:**
|
|
116
|
+
```python
|
|
117
|
+
await BaseService.start_update_manager()
|
|
118
|
+
```
|
|
119
|
+
"""
|
|
43
120
|
if BaseService._update_loop is None:
|
|
44
121
|
BaseService._update_loop = asyncio.get_event_loop()
|
|
45
|
-
BaseService._update_loop.create_task(
|
|
122
|
+
BaseService._update_loop.create_task(
|
|
123
|
+
BaseService._update_manager.update_next()
|
|
124
|
+
)
|
|
46
125
|
|
|
47
126
|
def register_updater(self, device: Device, interval):
|
|
127
|
+
"""Register a device for automatic status updates at a specified interval.
|
|
128
|
+
|
|
129
|
+
This enables automatic background updates for a device, periodically refreshing
|
|
130
|
+
its state from the Wyze servers. Useful for keeping device status current.
|
|
131
|
+
|
|
132
|
+
**Args:**
|
|
133
|
+
* `device` (Device): The device to register for automatic updates
|
|
134
|
+
* `interval` (int): Update interval in seconds
|
|
135
|
+
|
|
136
|
+
**Example:**
|
|
137
|
+
```python
|
|
138
|
+
# Update device state every 30 seconds
|
|
139
|
+
service.register_updater(device, 30)
|
|
140
|
+
```
|
|
141
|
+
"""
|
|
48
142
|
self._updater = DeviceUpdater(self, device, interval)
|
|
49
143
|
BaseService._update_manager.add_updater(self._updater)
|
|
50
144
|
self._updater_dict[self._updater.device] = self._updater
|
|
51
145
|
|
|
52
146
|
def unregister_updater(self, device: Device):
|
|
147
|
+
"""Stop automatic updates for a device.
|
|
148
|
+
|
|
149
|
+
This removes a device from the automatic update system to stop
|
|
150
|
+
background status refreshes.
|
|
151
|
+
|
|
152
|
+
**Args:**
|
|
153
|
+
* `device` (Device): The device to stop updating automatically
|
|
154
|
+
|
|
155
|
+
**Example:**
|
|
156
|
+
```python
|
|
157
|
+
service.unregister_updater(device)
|
|
158
|
+
```
|
|
159
|
+
"""
|
|
53
160
|
if self._updater:
|
|
54
161
|
BaseService._update_manager.del_updater(self._updater_dict[device])
|
|
55
162
|
del self._updater_dict[device]
|
|
56
163
|
|
|
57
|
-
async def set_push_info(self, on: bool)
|
|
164
|
+
async def set_push_info(self, on: bool):
|
|
165
|
+
"""Set push info for the user.
|
|
166
|
+
|
|
167
|
+
:param on: Whether to enable or disable push notifications.
|
|
168
|
+
"""
|
|
58
169
|
await self._auth_lib.refresh_if_should()
|
|
59
170
|
|
|
60
171
|
url = "https://api.wyzecam.com/app/user/set_push_info"
|
|
@@ -68,7 +179,7 @@ class BaseService:
|
|
|
68
179
|
"sv": SV,
|
|
69
180
|
"access_token": self._auth_lib.token.access_token,
|
|
70
181
|
"phone_id": PHONE_ID,
|
|
71
|
-
"app_name": APP_NAME
|
|
182
|
+
"app_name": APP_NAME,
|
|
72
183
|
}
|
|
73
184
|
|
|
74
185
|
response_json = await self._auth_lib.post(url, json=payload)
|
|
@@ -76,31 +187,49 @@ class BaseService:
|
|
|
76
187
|
check_for_errors_standard(self, response_json)
|
|
77
188
|
|
|
78
189
|
async def get_user_profile(self) -> Dict[Any, Any]:
|
|
190
|
+
"""Get user profile.
|
|
191
|
+
|
|
192
|
+
:return: User profile.
|
|
193
|
+
"""
|
|
79
194
|
await self._auth_lib.refresh_if_should()
|
|
80
195
|
|
|
81
196
|
payload = olive_create_user_info_payload()
|
|
82
197
|
signature = olive_create_signature(payload, self._auth_lib.token.access_token)
|
|
83
198
|
headers = {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
199
|
+
"Accept-Encoding": "gzip",
|
|
200
|
+
"User-Agent": "myapp",
|
|
201
|
+
"appid": OLIVE_APP_ID,
|
|
202
|
+
"appinfo": APP_INFO,
|
|
203
|
+
"phoneid": PHONE_ID,
|
|
204
|
+
"access_token": self._auth_lib.token.access_token,
|
|
205
|
+
"signature2": signature,
|
|
91
206
|
}
|
|
92
207
|
|
|
93
|
-
url =
|
|
208
|
+
url = (
|
|
209
|
+
"https://wyze-platform-service.wyzecam.com/app/v2/platform/get_user_profile"
|
|
210
|
+
)
|
|
94
211
|
|
|
95
212
|
response_json = await self._auth_lib.get(url, headers=headers, params=payload)
|
|
96
213
|
|
|
97
214
|
return response_json
|
|
98
215
|
|
|
99
216
|
async def get_object_list(self) -> List[Device]:
|
|
100
|
-
"""
|
|
101
|
-
|
|
217
|
+
"""Discover and retrieve all devices associated with the Wyze account.
|
|
218
|
+
|
|
219
|
+
This method fetches all devices from the Wyze API and caches them for
|
|
220
|
+
efficient subsequent access. Results are shared across all service instances.
|
|
221
|
+
|
|
222
|
+
**Returns:**
|
|
223
|
+
* `List[Device]`: List of all discovered Wyze devices
|
|
102
224
|
|
|
103
|
-
|
|
225
|
+
**Example:**
|
|
226
|
+
```python
|
|
227
|
+
devices = await service.get_object_list()
|
|
228
|
+
for device in devices:
|
|
229
|
+
print(f"Device: {device.nickname} ({device.product_model})")
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Note:** Results are cached and shared across all BaseService instances
|
|
104
233
|
"""
|
|
105
234
|
await self._auth_lib.refresh_if_should()
|
|
106
235
|
|
|
@@ -113,19 +242,29 @@ class BaseService:
|
|
|
113
242
|
"sv": "9d74946e652647e9b6c9d59326aef104",
|
|
114
243
|
"access_token": self._auth_lib.token.access_token,
|
|
115
244
|
"phone_id": PHONE_ID,
|
|
116
|
-
"app_name": APP_NAME
|
|
245
|
+
"app_name": APP_NAME,
|
|
117
246
|
}
|
|
118
247
|
|
|
119
|
-
response_json = await self._auth_lib.post(
|
|
120
|
-
|
|
248
|
+
response_json = await self._auth_lib.post(
|
|
249
|
+
"https://api.wyzecam.com/app/v2/home_page/get_object_list", json=payload
|
|
250
|
+
)
|
|
121
251
|
|
|
122
252
|
check_for_errors_standard(self, response_json)
|
|
123
253
|
# Cache the devices so that update calls can pull more recent device_params
|
|
124
|
-
BaseService._devices = [
|
|
254
|
+
BaseService._devices = [
|
|
255
|
+
Device(device) for device in response_json["data"]["device_list"]
|
|
256
|
+
]
|
|
125
257
|
|
|
126
258
|
return BaseService._devices
|
|
127
259
|
|
|
128
|
-
async def get_updated_params(
|
|
260
|
+
async def get_updated_params(
|
|
261
|
+
self, device_mac: str = None
|
|
262
|
+
) -> Dict[str, Optional[Any]]:
|
|
263
|
+
"""Get updated params for a device.
|
|
264
|
+
|
|
265
|
+
:param device_mac: The device mac to get updated params for.
|
|
266
|
+
:return: Updated params for the device.
|
|
267
|
+
"""
|
|
129
268
|
if time.time() - BaseService._last_updated_time >= BaseService._min_update_time:
|
|
130
269
|
await self.get_object_list()
|
|
131
270
|
BaseService._last_updated_time = time.time()
|
|
@@ -136,8 +275,7 @@ class BaseService:
|
|
|
136
275
|
return ret_params
|
|
137
276
|
|
|
138
277
|
async def _get_property_list(self, device: Device) -> List[Tuple[PropertyIDs, Any]]:
|
|
139
|
-
"""
|
|
140
|
-
Wraps the api.wyzecam.com/app/v2/device/get_property_list endpoint
|
|
278
|
+
"""Wraps the api.wyzecam.com/app/v2/device/get_property_list endpoint
|
|
141
279
|
|
|
142
280
|
:param device: Device to get properties for
|
|
143
281
|
:return: List of PropertyIDs and values
|
|
@@ -157,34 +295,30 @@ class BaseService:
|
|
|
157
295
|
"app_name": APP_NAME,
|
|
158
296
|
"device_model": device.product_model,
|
|
159
297
|
"device_mac": device.mac,
|
|
160
|
-
"target_pid_list": []
|
|
298
|
+
"target_pid_list": [],
|
|
161
299
|
}
|
|
162
300
|
|
|
163
|
-
response_json = await self._auth_lib.post(
|
|
164
|
-
|
|
301
|
+
response_json = await self._auth_lib.post(
|
|
302
|
+
"https://api.wyzecam.com/app/v2/device/get_property_list", json=payload
|
|
303
|
+
)
|
|
165
304
|
|
|
166
305
|
check_for_errors_standard(self, response_json)
|
|
167
|
-
properties = response_json[
|
|
306
|
+
properties = response_json["data"]["property_list"]
|
|
168
307
|
property_list = []
|
|
169
308
|
for prop in properties:
|
|
170
309
|
try:
|
|
171
|
-
property_id = PropertyIDs(prop[
|
|
172
|
-
property_list.append((
|
|
173
|
-
property_id,
|
|
174
|
-
prop['value']
|
|
175
|
-
))
|
|
310
|
+
property_id = PropertyIDs(prop["pid"])
|
|
311
|
+
property_list.append((property_id, prop["value"]))
|
|
176
312
|
except ValueError:
|
|
177
313
|
pass
|
|
178
314
|
|
|
179
315
|
return property_list
|
|
180
316
|
|
|
181
|
-
async def _set_property_list(self, device: Device, plist: List[Dict[str, str]])
|
|
182
|
-
"""
|
|
183
|
-
Wraps the api.wyzecam.com/app/v2/device/set_property_list endpoint
|
|
317
|
+
async def _set_property_list(self, device: Device, plist: List[Dict[str, str]]):
|
|
318
|
+
"""Wraps the api.wyzecam.com/app/v2/device/set_property_list endpoint
|
|
184
319
|
|
|
185
320
|
:param device: The device for which to set the property(ies)
|
|
186
321
|
:param plist: A list of properties [{"pid": pid, "pvalue": pvalue},...]
|
|
187
|
-
:return:
|
|
188
322
|
"""
|
|
189
323
|
|
|
190
324
|
await self._auth_lib.refresh_if_should()
|
|
@@ -201,17 +335,17 @@ class BaseService:
|
|
|
201
335
|
"app_name": APP_NAME,
|
|
202
336
|
"property_list": plist,
|
|
203
337
|
"device_model": device.product_model,
|
|
204
|
-
"device_mac": device.mac
|
|
338
|
+
"device_mac": device.mac,
|
|
205
339
|
}
|
|
206
340
|
|
|
207
|
-
response_json = await self._auth_lib.post(
|
|
208
|
-
|
|
341
|
+
response_json = await self._auth_lib.post(
|
|
342
|
+
"https://api.wyzecam.com/app/v2/device/set_property_list", json=payload
|
|
343
|
+
)
|
|
209
344
|
|
|
210
345
|
check_for_errors_standard(self, response_json)
|
|
211
346
|
|
|
212
|
-
async def _run_action_list(self, device: Device, plist: List[Dict[Any, Any]])
|
|
213
|
-
"""
|
|
214
|
-
Wraps the api.wyzecam.com/app/v2/auto/run_action_list endpoint
|
|
347
|
+
async def _run_action_list(self, device: Device, plist: List[Dict[Any, Any]]):
|
|
348
|
+
"""Wraps the api.wyzecam.com/app/v2/auto/run_action_list endpoint
|
|
215
349
|
|
|
216
350
|
:param device: The device for which to run the action list
|
|
217
351
|
:param plist: A list of properties [{"pid": pid, "pvalue": pvalue},...]
|
|
@@ -231,31 +365,24 @@ class BaseService:
|
|
|
231
365
|
"action_list": [
|
|
232
366
|
{
|
|
233
367
|
"instance_id": device.mac,
|
|
234
|
-
"action_params": {
|
|
235
|
-
"list": [
|
|
236
|
-
{
|
|
237
|
-
"mac": device.mac,
|
|
238
|
-
"plist": plist
|
|
239
|
-
}
|
|
240
|
-
]
|
|
241
|
-
},
|
|
368
|
+
"action_params": {"list": [{"mac": device.mac, "plist": plist}]},
|
|
242
369
|
"provider_key": device.product_model,
|
|
243
|
-
"action_key": "set_mesh_property"
|
|
370
|
+
"action_key": "set_mesh_property",
|
|
244
371
|
}
|
|
245
|
-
]
|
|
372
|
+
],
|
|
246
373
|
}
|
|
247
374
|
|
|
248
|
-
response_json = await self._auth_lib.post(
|
|
249
|
-
|
|
375
|
+
response_json = await self._auth_lib.post(
|
|
376
|
+
"https://api.wyzecam.com/app/v2/auto/run_action_list", json=payload
|
|
377
|
+
)
|
|
250
378
|
|
|
251
379
|
check_for_errors_standard(self, response_json)
|
|
252
380
|
|
|
253
381
|
async def _get_event_list(self, count: int) -> Dict[Any, Any]:
|
|
254
|
-
"""
|
|
255
|
-
Wraps the api.wyzecam.com/app/v2/device/get_event_list endpoint
|
|
382
|
+
"""Wraps the api.wyzecam.com/app/v2/device/get_event_list endpoint
|
|
256
383
|
|
|
257
384
|
:param count: Number of events to gather
|
|
258
|
-
:return: Response from the server
|
|
385
|
+
:return: Response from the server after being validated
|
|
259
386
|
"""
|
|
260
387
|
|
|
261
388
|
await self._auth_lib.refresh_if_should()
|
|
@@ -268,12 +395,7 @@ class BaseService:
|
|
|
268
395
|
"count": count,
|
|
269
396
|
"app_version": APP_VERSION,
|
|
270
397
|
"order_by": 2,
|
|
271
|
-
"event_value_list": [
|
|
272
|
-
"1",
|
|
273
|
-
"13",
|
|
274
|
-
"10",
|
|
275
|
-
"12"
|
|
276
|
-
],
|
|
398
|
+
"event_value_list": ["1", "13", "10", "12"],
|
|
277
399
|
"sc": "9f275790cab94a72bd206c8876429f3c",
|
|
278
400
|
"device_mac_list": [],
|
|
279
401
|
"event_tag_list": [],
|
|
@@ -283,22 +405,21 @@ class BaseService:
|
|
|
283
405
|
"app_ver": APP_VER,
|
|
284
406
|
"ts": 1623612037763,
|
|
285
407
|
"device_mac": "",
|
|
286
|
-
"access_token": self._auth_lib.token.access_token
|
|
408
|
+
"access_token": self._auth_lib.token.access_token,
|
|
287
409
|
}
|
|
288
410
|
|
|
289
|
-
response_json = await self._auth_lib.post(
|
|
290
|
-
|
|
411
|
+
response_json = await self._auth_lib.post(
|
|
412
|
+
"https://api.wyzecam.com/app/v2/device/get_event_list", json=payload
|
|
413
|
+
)
|
|
291
414
|
|
|
292
415
|
check_for_errors_standard(self, response_json)
|
|
293
416
|
return response_json
|
|
294
417
|
|
|
295
|
-
async def _run_action(self, device: Device, action: str)
|
|
296
|
-
"""
|
|
297
|
-
Wraps the api.wyzecam.com/app/v2/auto/run_action endpoint
|
|
418
|
+
async def _run_action(self, device: Device, action: str):
|
|
419
|
+
"""Wraps the api.wyzecam.com/app/v2/auto/run_action endpoint
|
|
298
420
|
|
|
299
421
|
:param device: The device for which to run the action
|
|
300
422
|
:param action: The action to run
|
|
301
|
-
:return:
|
|
302
423
|
"""
|
|
303
424
|
|
|
304
425
|
await self._auth_lib.refresh_if_should()
|
|
@@ -320,18 +441,18 @@ class BaseService:
|
|
|
320
441
|
"custom_string": "",
|
|
321
442
|
}
|
|
322
443
|
|
|
323
|
-
response_json = await self._auth_lib.post(
|
|
324
|
-
|
|
444
|
+
response_json = await self._auth_lib.post(
|
|
445
|
+
"https://api.wyzecam.com/app/v2/auto/run_action", json=payload
|
|
446
|
+
)
|
|
325
447
|
|
|
326
448
|
check_for_errors_standard(self, response_json)
|
|
327
|
-
|
|
328
|
-
async def _run_action_devicemgmt(self, device: Device, type: str, value: str)
|
|
329
|
-
"""
|
|
330
|
-
Wraps the devicemgmt-service-beta.wyze.com/device-management/api/action/run_action endpoint
|
|
449
|
+
|
|
450
|
+
async def _run_action_devicemgmt(self, device: Device, type: str, value: str):
|
|
451
|
+
"""Wraps the devicemgmt-service-beta.wyze.com/device-management/api/action/run_action endpoint
|
|
331
452
|
|
|
332
453
|
:param device: The device for which to run the action
|
|
333
|
-
:param
|
|
334
|
-
:
|
|
454
|
+
:param type: The type of action to run
|
|
455
|
+
:param value: The value of the action to run
|
|
335
456
|
"""
|
|
336
457
|
|
|
337
458
|
await self._auth_lib.refresh_if_should()
|
|
@@ -339,30 +460,32 @@ class BaseService:
|
|
|
339
460
|
capabilities = devicemgmt_create_capabilities_payload(type, value)
|
|
340
461
|
|
|
341
462
|
payload = {
|
|
342
|
-
"capabilities": [
|
|
343
|
-
capabilities
|
|
344
|
-
],
|
|
463
|
+
"capabilities": [capabilities],
|
|
345
464
|
"nonce": int(time.time() * 1000),
|
|
346
465
|
"targetInfo": {
|
|
347
466
|
"id": device.mac,
|
|
348
467
|
"productModel": device.product_model,
|
|
349
|
-
"type": "DEVICE"
|
|
468
|
+
"type": "DEVICE",
|
|
350
469
|
},
|
|
351
|
-
"transactionId": "0a5b20591fedd4du1b93f90743ba0csd"
|
|
470
|
+
"transactionId": "0a5b20591fedd4du1b93f90743ba0csd", # OG cam needs this (doesn't matter what the value is)
|
|
352
471
|
}
|
|
353
472
|
|
|
354
473
|
headers = {
|
|
355
474
|
"authorization": self._auth_lib.token.access_token,
|
|
356
475
|
}
|
|
357
476
|
|
|
358
|
-
response_json = await self._auth_lib.post(
|
|
359
|
-
|
|
477
|
+
response_json = await self._auth_lib.post(
|
|
478
|
+
"https://devicemgmt-service-beta.wyze.com/device-management/api/action/run_action",
|
|
479
|
+
json=payload,
|
|
480
|
+
headers=headers,
|
|
481
|
+
)
|
|
360
482
|
|
|
361
483
|
check_for_errors_iot(self, response_json)
|
|
362
|
-
|
|
363
|
-
async def _set_toggle(
|
|
364
|
-
|
|
365
|
-
|
|
484
|
+
|
|
485
|
+
async def _set_toggle(
|
|
486
|
+
self, device: Device, toggleType: DeviceMgmtToggleType, state: str
|
|
487
|
+
):
|
|
488
|
+
"""Wraps the ai-subscription-service-beta.wyzecam.com/v4/subscription-service/toggle-management endpoint
|
|
366
489
|
|
|
367
490
|
:param device: The device for which to get the state
|
|
368
491
|
:param toggleType: Enum for the toggle type
|
|
@@ -377,21 +500,15 @@ class BaseService:
|
|
|
377
500
|
"device_firmware": "1234567890",
|
|
378
501
|
"device_id": device.mac,
|
|
379
502
|
"device_model": device.product_model,
|
|
380
|
-
"page_id": [
|
|
381
|
-
toggleType.pageId
|
|
382
|
-
],
|
|
503
|
+
"page_id": [toggleType.pageId],
|
|
383
504
|
"toggle_update": [
|
|
384
|
-
{
|
|
385
|
-
|
|
386
|
-
"toggle_status": state
|
|
387
|
-
}
|
|
388
|
-
]
|
|
505
|
+
{"toggle_id": toggleType.toggleId, "toggle_status": state}
|
|
506
|
+
],
|
|
389
507
|
}
|
|
390
508
|
],
|
|
391
|
-
"nonce": str(int(time.time() * 1000))
|
|
509
|
+
"nonce": str(int(time.time() * 1000)),
|
|
392
510
|
}
|
|
393
511
|
|
|
394
|
-
|
|
395
512
|
signature = olive_create_signature(payload, self._auth_lib.token.access_token)
|
|
396
513
|
headers = {
|
|
397
514
|
"access_token": self._auth_lib.token.access_token,
|
|
@@ -401,20 +518,22 @@ class BaseService:
|
|
|
401
518
|
"signature2": signature,
|
|
402
519
|
"appplatform": APP_PLATFORM,
|
|
403
520
|
"appversion": APP_VERSION,
|
|
404
|
-
"requestid": "35374158s4s313b9a2be7c057f2da5d1"
|
|
521
|
+
"requestid": "35374158s4s313b9a2be7c057f2da5d1",
|
|
405
522
|
}
|
|
406
523
|
|
|
407
|
-
response_json = await self._auth_lib.put(
|
|
408
|
-
|
|
409
|
-
|
|
524
|
+
response_json = await self._auth_lib.put(
|
|
525
|
+
"https://ai-subscription-service-beta.wyzecam.com/v4/subscription-service/toggle-management",
|
|
526
|
+
json=payload,
|
|
527
|
+
headers=headers,
|
|
528
|
+
)
|
|
529
|
+
|
|
410
530
|
check_for_errors_devicemgmt(self, response_json)
|
|
411
|
-
|
|
531
|
+
|
|
412
532
|
async def _get_iot_prop_devicemgmt(self, device: Device) -> Dict[str, Any]:
|
|
413
|
-
"""
|
|
414
|
-
Wraps the devicemgmt-service-beta.wyze.com/device-management/api/device-property/get_iot_prop endpoint
|
|
533
|
+
"""Wraps the devicemgmt-service-beta.wyze.com/device-management/api/device-property/get_iot_prop endpoint
|
|
415
534
|
|
|
416
535
|
:param device: The device for which to get the state
|
|
417
|
-
:return:
|
|
536
|
+
:return: Response from the server after being validated
|
|
418
537
|
"""
|
|
419
538
|
|
|
420
539
|
await self._auth_lib.refresh_if_should()
|
|
@@ -425,24 +544,26 @@ class BaseService:
|
|
|
425
544
|
"targetInfo": {
|
|
426
545
|
"id": device.mac,
|
|
427
546
|
"productModel": device.product_model,
|
|
428
|
-
"type": "DEVICE"
|
|
429
|
-
}
|
|
547
|
+
"type": "DEVICE",
|
|
548
|
+
},
|
|
430
549
|
}
|
|
431
550
|
|
|
432
551
|
headers = {
|
|
433
552
|
"authorization": self._auth_lib.token.access_token,
|
|
434
553
|
}
|
|
435
554
|
|
|
436
|
-
response_json = await self._auth_lib.post(
|
|
437
|
-
|
|
438
|
-
|
|
555
|
+
response_json = await self._auth_lib.post(
|
|
556
|
+
"https://devicemgmt-service-beta.wyze.com/device-management/api/device-property/get_iot_prop",
|
|
557
|
+
json=payload,
|
|
558
|
+
headers=headers,
|
|
559
|
+
)
|
|
560
|
+
|
|
439
561
|
check_for_errors_iot(self, response_json)
|
|
440
562
|
|
|
441
563
|
return response_json
|
|
442
564
|
|
|
443
|
-
async def _set_property(self, device: Device, pid: str, pvalue: str)
|
|
444
|
-
"""
|
|
445
|
-
Wraps the api.wyzecam.com/app/v2/device/set_property endpoint
|
|
565
|
+
async def _set_property(self, device: Device, pid: str, pvalue: str):
|
|
566
|
+
"""Wraps the api.wyzecam.com/app/v2/device/set_property endpoint
|
|
446
567
|
|
|
447
568
|
:param device: The device for which to set the property
|
|
448
569
|
:param pid: The property id
|
|
@@ -463,22 +584,21 @@ class BaseService:
|
|
|
463
584
|
"pvalue": pvalue,
|
|
464
585
|
"pid": pid,
|
|
465
586
|
"device_model": device.product_model,
|
|
466
|
-
"device_mac": device.mac
|
|
587
|
+
"device_mac": device.mac,
|
|
467
588
|
}
|
|
468
589
|
|
|
469
|
-
response_json = await self._auth_lib.post(
|
|
470
|
-
|
|
590
|
+
response_json = await self._auth_lib.post(
|
|
591
|
+
"https://api.wyzecam.com/app/v2/device/set_property", json=payload
|
|
592
|
+
)
|
|
471
593
|
|
|
472
594
|
check_for_errors_standard(self, response_json)
|
|
473
595
|
|
|
474
|
-
async def _monitoring_profile_active(self, hms_id: str, home: int, away: int)
|
|
475
|
-
"""
|
|
476
|
-
Wraps the hms.api.wyze.com/api/v1/monitoring/v1/profile/active endpoint
|
|
596
|
+
async def _monitoring_profile_active(self, hms_id: str, home: int, away: int):
|
|
597
|
+
"""Wraps the hms.api.wyze.com/api/v1/monitoring/v1/profile/active endpoint
|
|
477
598
|
|
|
478
599
|
:param hms_id: The hms id
|
|
479
600
|
:param home: 1 for home 0 for not
|
|
480
601
|
:param away: 1 for away 0 for not
|
|
481
|
-
:return:
|
|
482
602
|
"""
|
|
483
603
|
await self._auth_lib.refresh_if_should()
|
|
484
604
|
|
|
@@ -486,31 +606,23 @@ class BaseService:
|
|
|
486
606
|
query = olive_create_hms_patch_payload(hms_id)
|
|
487
607
|
signature = olive_create_signature(query, self._auth_lib.token.access_token)
|
|
488
608
|
headers = {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
609
|
+
"Accept-Encoding": "gzip",
|
|
610
|
+
"User-Agent": "myapp",
|
|
611
|
+
"appid": OLIVE_APP_ID,
|
|
612
|
+
"appinfo": APP_INFO,
|
|
613
|
+
"phoneid": PHONE_ID,
|
|
614
|
+
"access_token": self._auth_lib.token.access_token,
|
|
615
|
+
"signature2": signature,
|
|
616
|
+
"Authorization": self._auth_lib.token.access_token,
|
|
497
617
|
}
|
|
498
|
-
payload = [
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
"state": "away",
|
|
505
|
-
"active": away
|
|
506
|
-
}
|
|
507
|
-
]
|
|
508
|
-
response_json = await self._auth_lib.patch(url, headers=headers, params=query, json=payload)
|
|
618
|
+
payload = [{"state": "home", "active": home}, {"state": "away", "active": away}]
|
|
619
|
+
response_json = await self._auth_lib.patch(
|
|
620
|
+
url, headers=headers, params=query, json=payload
|
|
621
|
+
)
|
|
509
622
|
check_for_errors_hms(self, response_json)
|
|
510
623
|
|
|
511
624
|
async def _get_plan_binding_list_by_user(self) -> Dict[Any, Any]:
|
|
512
|
-
"""
|
|
513
|
-
Wraps the wyze-membership-service.wyzecam.com/platform/v2/membership/get_plan_binding_list_by_user endpoint
|
|
625
|
+
"""Wraps the wyze-membership-service.wyzecam.com/platform/v2/membership/get_plan_binding_list_by_user endpoint
|
|
514
626
|
|
|
515
627
|
:return: The response to gathering the plan for the current user
|
|
516
628
|
"""
|
|
@@ -522,13 +634,13 @@ class BaseService:
|
|
|
522
634
|
payload = olive_create_hms_payload()
|
|
523
635
|
signature = olive_create_signature(payload, self._auth_lib.token.access_token)
|
|
524
636
|
headers = {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
637
|
+
"Accept-Encoding": "gzip",
|
|
638
|
+
"User-Agent": "myapp",
|
|
639
|
+
"appid": OLIVE_APP_ID,
|
|
640
|
+
"appinfo": APP_INFO,
|
|
641
|
+
"phoneid": PHONE_ID,
|
|
642
|
+
"access_token": self._auth_lib.token.access_token,
|
|
643
|
+
"signature2": signature,
|
|
532
644
|
}
|
|
533
645
|
|
|
534
646
|
response_json = await self._auth_lib.get(url, headers=headers, params=payload)
|
|
@@ -544,13 +656,8 @@ class BaseService:
|
|
|
544
656
|
await self._auth_lib.refresh_if_should()
|
|
545
657
|
|
|
546
658
|
url = "https://hms.api.wyze.com/api/v1/reme-alarm"
|
|
547
|
-
payload = {
|
|
548
|
-
|
|
549
|
-
"remediation_id": "emergency"
|
|
550
|
-
}
|
|
551
|
-
headers = {
|
|
552
|
-
"Authorization": self._auth_lib.token.access_token
|
|
553
|
-
}
|
|
659
|
+
payload = {"hms_id": hms_id, "remediation_id": "emergency"}
|
|
660
|
+
headers = {"Authorization": self._auth_lib.token.access_token}
|
|
554
661
|
|
|
555
662
|
response_json = await self._auth_lib.delete(url, headers=headers, json=payload)
|
|
556
663
|
|
|
@@ -570,14 +677,14 @@ class BaseService:
|
|
|
570
677
|
query = olive_create_hms_get_payload(hms_id)
|
|
571
678
|
signature = olive_create_signature(query, self._auth_lib.token.access_token)
|
|
572
679
|
headers = {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
680
|
+
"User-Agent": "myapp",
|
|
681
|
+
"appid": OLIVE_APP_ID,
|
|
682
|
+
"appinfo": APP_INFO,
|
|
683
|
+
"phoneid": PHONE_ID,
|
|
684
|
+
"access_token": self._auth_lib.token.access_token,
|
|
685
|
+
"signature2": signature,
|
|
686
|
+
"Authorization": self._auth_lib.token.access_token,
|
|
687
|
+
"Content-Type": "application/json",
|
|
581
688
|
}
|
|
582
689
|
|
|
583
690
|
response_json = await self._auth_lib.get(url, headers=headers, params=query)
|
|
@@ -594,9 +701,11 @@ class BaseService:
|
|
|
594
701
|
|
|
595
702
|
payload = {
|
|
596
703
|
"uuid": device_uuid,
|
|
597
|
-
"action": action # "remoteLock" or "remoteUnlock"
|
|
704
|
+
"action": action, # "remoteLock" or "remoteUnlock"
|
|
598
705
|
}
|
|
599
|
-
payload = ford_create_payload(
|
|
706
|
+
payload = ford_create_payload(
|
|
707
|
+
self._auth_lib.token.access_token, payload, url_path, "post"
|
|
708
|
+
)
|
|
600
709
|
|
|
601
710
|
url = "https://yd-saas-toc.wyzecam.com/openapi/lock/v1/control"
|
|
602
711
|
|
|
@@ -611,12 +720,11 @@ class BaseService:
|
|
|
611
720
|
|
|
612
721
|
device_uuid = device.mac.split(".")[-1]
|
|
613
722
|
|
|
614
|
-
payload = {
|
|
615
|
-
"uuid": device_uuid,
|
|
616
|
-
"with_keypad": "1"
|
|
617
|
-
}
|
|
723
|
+
payload = {"uuid": device_uuid, "with_keypad": "1"}
|
|
618
724
|
|
|
619
|
-
payload = ford_create_payload(
|
|
725
|
+
payload = ford_create_payload(
|
|
726
|
+
self._auth_lib.token.access_token, payload, url_path, "get"
|
|
727
|
+
)
|
|
620
728
|
|
|
621
729
|
url = "https://yd-saas-toc.wyzecam.com/openapi/lock/v1/info"
|
|
622
730
|
|
|
@@ -626,6 +734,25 @@ class BaseService:
|
|
|
626
734
|
|
|
627
735
|
return response_json
|
|
628
736
|
|
|
737
|
+
async def _get_lock_ble_token(self, device: Device) -> Dict[str, Optional[Any]]:
|
|
738
|
+
await self._auth_lib.refresh_if_should()
|
|
739
|
+
|
|
740
|
+
url_path = "/openapi/lock/v1/ble/token"
|
|
741
|
+
|
|
742
|
+
payload = {"uuid": device.mac}
|
|
743
|
+
|
|
744
|
+
payload = ford_create_payload(
|
|
745
|
+
self._auth_lib.token.access_token, payload, url_path, "get"
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
url = f"https://yd-saas-toc.wyzecam.com{url_path}"
|
|
749
|
+
|
|
750
|
+
response_json = await self._auth_lib.get(url, params=payload)
|
|
751
|
+
|
|
752
|
+
check_for_errors_lock(self, response_json)
|
|
753
|
+
|
|
754
|
+
return response_json
|
|
755
|
+
|
|
629
756
|
async def _get_device_info(self, device: Device) -> Dict[Any, Any]:
|
|
630
757
|
await self._auth_lib.refresh_if_should()
|
|
631
758
|
|
|
@@ -640,29 +767,32 @@ class BaseService:
|
|
|
640
767
|
"sv": "c86fa16fc99d4d6580f82ef3b942e586",
|
|
641
768
|
"access_token": self._auth_lib.token.access_token,
|
|
642
769
|
"phone_id": PHONE_ID,
|
|
643
|
-
"app_name": APP_NAME
|
|
770
|
+
"app_name": APP_NAME,
|
|
644
771
|
}
|
|
645
772
|
|
|
646
|
-
response_json = await self._auth_lib.post(
|
|
647
|
-
|
|
773
|
+
response_json = await self._auth_lib.post(
|
|
774
|
+
"https://api.wyzecam.com/app/v2/device/get_device_Info", json=payload
|
|
775
|
+
)
|
|
648
776
|
|
|
649
777
|
check_for_errors_standard(self, response_json)
|
|
650
778
|
|
|
651
779
|
return response_json
|
|
652
780
|
|
|
653
|
-
async def _get_iot_prop(
|
|
781
|
+
async def _get_iot_prop(
|
|
782
|
+
self, url: str, device: Device, keys: str
|
|
783
|
+
) -> Dict[Any, Any]:
|
|
654
784
|
await self._auth_lib.refresh_if_should()
|
|
655
785
|
|
|
656
786
|
payload = olive_create_get_payload(device.mac, keys)
|
|
657
787
|
signature = olive_create_signature(payload, self._auth_lib.token.access_token)
|
|
658
788
|
headers = {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
789
|
+
"Accept-Encoding": "gzip",
|
|
790
|
+
"User-Agent": "myapp",
|
|
791
|
+
"appid": OLIVE_APP_ID,
|
|
792
|
+
"appinfo": APP_INFO,
|
|
793
|
+
"phoneid": PHONE_ID,
|
|
794
|
+
"access_token": self._auth_lib.token.access_token,
|
|
795
|
+
"signature2": signature,
|
|
666
796
|
}
|
|
667
797
|
|
|
668
798
|
response_json = await self._auth_lib.get(url, headers=headers, params=payload)
|
|
@@ -671,26 +801,34 @@ class BaseService:
|
|
|
671
801
|
|
|
672
802
|
return response_json
|
|
673
803
|
|
|
674
|
-
async def _set_iot_prop(
|
|
804
|
+
async def _set_iot_prop(
|
|
805
|
+
self, url: str, device: Device, prop_key: str, value: Any
|
|
806
|
+
) -> None:
|
|
675
807
|
await self._auth_lib.refresh_if_should()
|
|
676
808
|
|
|
677
|
-
payload = olive_create_post_payload(
|
|
678
|
-
|
|
679
|
-
|
|
809
|
+
payload = olive_create_post_payload(
|
|
810
|
+
device.mac, device.product_model, prop_key, value
|
|
811
|
+
)
|
|
812
|
+
signature = olive_create_signature(
|
|
813
|
+
json.dumps(payload, separators=(",", ":")),
|
|
814
|
+
self._auth_lib.token.access_token,
|
|
815
|
+
)
|
|
680
816
|
headers = {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
817
|
+
"Accept-Encoding": "gzip",
|
|
818
|
+
"Content-Type": "application/json",
|
|
819
|
+
"User-Agent": "myapp",
|
|
820
|
+
"appid": OLIVE_APP_ID,
|
|
821
|
+
"appinfo": APP_INFO,
|
|
822
|
+
"phoneid": PHONE_ID,
|
|
823
|
+
"access_token": self._auth_lib.token.access_token,
|
|
824
|
+
"signature2": signature,
|
|
689
825
|
}
|
|
690
826
|
|
|
691
|
-
payload_str = json.dumps(payload, separators=(
|
|
827
|
+
payload_str = json.dumps(payload, separators=(",", ":"))
|
|
692
828
|
|
|
693
|
-
response_json = await self._auth_lib.post(
|
|
829
|
+
response_json = await self._auth_lib.post(
|
|
830
|
+
url, headers=headers, data=payload_str
|
|
831
|
+
)
|
|
694
832
|
|
|
695
833
|
check_for_errors_iot(self, response_json)
|
|
696
834
|
|
|
@@ -701,20 +839,20 @@ class BaseService:
|
|
|
701
839
|
"mac": bulb.mac.upper(),
|
|
702
840
|
"index": "1",
|
|
703
841
|
"ts": str(int(time.time_ns() // 1000000)),
|
|
704
|
-
"plist": plist
|
|
842
|
+
"plist": plist,
|
|
705
843
|
}
|
|
706
844
|
|
|
707
|
-
characteristics_str = json.dumps(characteristics, separators=(
|
|
845
|
+
characteristics_str = json.dumps(characteristics, separators=(",", ":"))
|
|
708
846
|
characteristics_enc = wyze_encrypt(bulb.enr, characteristics_str)
|
|
709
847
|
|
|
710
848
|
payload = {
|
|
711
849
|
"request": "set_status",
|
|
712
850
|
"isSendQueue": 0,
|
|
713
|
-
"characteristics": characteristics_enc
|
|
851
|
+
"characteristics": characteristics_enc,
|
|
714
852
|
}
|
|
715
853
|
|
|
716
854
|
# JSON likes to add a second \ so we have to remove it for the bulb to be happy
|
|
717
|
-
payload_str = json.dumps(payload, separators=(
|
|
855
|
+
payload_str = json.dumps(payload, separators=(",", ":")).replace("\\\\", "\\")
|
|
718
856
|
|
|
719
857
|
url = "http://%s:88/device_request" % bulb.ip
|
|
720
858
|
|
|
@@ -723,7 +861,9 @@ class BaseService:
|
|
|
723
861
|
async with session.post(url, data=payload_str) as response:
|
|
724
862
|
print(await response.text())
|
|
725
863
|
except aiohttp.ClientConnectionError:
|
|
726
|
-
_LOGGER.warning(
|
|
864
|
+
_LOGGER.warning(
|
|
865
|
+
"Failed to connect to bulb %s, reverting to cloud." % bulb.mac
|
|
866
|
+
)
|
|
727
867
|
await self._run_action_list(bulb, plist)
|
|
728
868
|
bulb.cloud_fallback = True
|
|
729
869
|
|