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.
@@ -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 PHONE_SYSTEM_TYPE, APP_VERSION, APP_VER, PHONE_ID, APP_NAME, OLIVE_APP_ID, APP_INFO, SC, SV, APP_PLATFORM, SOURCE
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 olive_create_hms_patch_payload, olive_create_hms_payload, \
18
- olive_create_hms_get_payload, ford_create_payload, olive_create_get_payload, olive_create_post_payload, \
19
- olive_create_user_info_payload, devicemgmt_create_capabilities_payload, devicemgmt_get_iot_props_list
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 check_for_errors_standard, check_for_errors_hms, check_for_errors_lock, \
22
- check_for_errors_iot, wyze_encrypt, check_for_errors_devicemgmt
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 = 0 # preload a value of 0 so that comparison will succeed on the first run
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() # fmt: skip
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(BaseService._update_manager.update_next())
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) -> None:
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
- 'Accept-Encoding': 'gzip',
85
- 'User-Agent': 'myapp',
86
- 'appid': OLIVE_APP_ID,
87
- 'appinfo': APP_INFO,
88
- 'phoneid': PHONE_ID,
89
- 'access_token': self._auth_lib.token.access_token,
90
- 'signature2': signature
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 = 'https://wyze-platform-service.wyzecam.com/app/v2/platform/get_user_profile'
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
- Wraps the api.wyzecam.com/app/v2/home_page/get_object_list endpoint
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
- :return: List of devices
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("https://api.wyzecam.com/app/v2/home_page/get_object_list",
120
- json=payload)
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 = [Device(device) for device in response_json['data']['device_list']]
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(self, device_mac: str = None) -> Dict[str, Optional[Any]]:
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("https://api.wyzecam.com/app/v2/device/get_property_list",
164
- json=payload)
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['data']['property_list']
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['pid'])
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]]) -> None:
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("https://api.wyzecam.com/app/v2/device/set_property_list",
208
- json=payload)
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]]) -> None:
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("https://api.wyzecam.com/app/v2/auto/run_action_list",
249
- json=payload)
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("https://api.wyzecam.com/app/v2/device/get_event_list",
290
- json=payload)
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) -> None:
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("https://api.wyzecam.com/app/v2/auto/run_action",
324
- json=payload)
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) -> None:
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 state: on or off
334
- :return:
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" # OG cam needs this (doesn't matter what the value is)
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("https://devicemgmt-service-beta.wyze.com/device-management/api/action/run_action",
359
- json=payload, headers=headers)
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(self, device: Device, toggleType: DeviceMgmtToggleType, state: str) -> None:
364
- """
365
- Wraps the ai-subscription-service-beta.wyzecam.com/v4/subscription-service/toggle-management endpoint
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
- "toggle_id": toggleType.toggleId,
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("https://ai-subscription-service-beta.wyzecam.com/v4/subscription-service/toggle-management",
408
- json=payload, headers=headers)
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("https://devicemgmt-service-beta.wyze.com/device-management/api/device-property/get_iot_prop",
437
- json=payload, headers=headers)
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) -> None:
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("https://api.wyzecam.com/app/v2/device/set_property",
470
- json=payload)
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) -> None:
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
- 'Accept-Encoding': 'gzip',
490
- 'User-Agent': 'myapp',
491
- 'appid': OLIVE_APP_ID,
492
- 'appinfo': APP_INFO,
493
- 'phoneid': PHONE_ID,
494
- 'access_token': self._auth_lib.token.access_token,
495
- 'signature2': signature,
496
- 'Authorization': self._auth_lib.token.access_token
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
- "state": "home",
501
- "active": home
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
- 'Accept-Encoding': 'gzip',
526
- 'User-Agent': 'myapp',
527
- 'appid': OLIVE_APP_ID,
528
- 'appinfo': APP_INFO,
529
- 'phoneid': PHONE_ID,
530
- 'access_token': self._auth_lib.token.access_token,
531
- 'signature2': signature
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
- "hms_id": hms_id,
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
- 'User-Agent': 'myapp',
574
- 'appid': OLIVE_APP_ID,
575
- 'appinfo': APP_INFO,
576
- 'phoneid': PHONE_ID,
577
- 'access_token': self._auth_lib.token.access_token,
578
- 'signature2': signature,
579
- 'Authorization': self._auth_lib.token.access_token,
580
- 'Content-Type': "application/json"
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(self._auth_lib.token.access_token, payload, url_path, "post")
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(self._auth_lib.token.access_token, payload, url_path, "get")
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("https://api.wyzecam.com/app/v2/device/get_device_Info",
647
- json=payload)
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(self, url: str, device: Device, keys: str) -> Dict[Any, Any]:
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
- 'Accept-Encoding': 'gzip',
660
- 'User-Agent': 'myapp',
661
- 'appid': OLIVE_APP_ID,
662
- 'appinfo': APP_INFO,
663
- 'phoneid': PHONE_ID,
664
- 'access_token': self._auth_lib.token.access_token,
665
- 'signature2': signature
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(self, url: str, device: Device, prop_key: str, value: Any) -> None:
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(device.mac, device.product_model, prop_key, value)
678
- signature = olive_create_signature(json.dumps(payload, separators=(',', ':')),
679
- self._auth_lib.token.access_token)
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
- 'Accept-Encoding': 'gzip',
682
- 'Content-Type': 'application/json',
683
- 'User-Agent': 'myapp',
684
- 'appid': OLIVE_APP_ID,
685
- 'appinfo': APP_INFO,
686
- 'phoneid': PHONE_ID,
687
- 'access_token': self._auth_lib.token.access_token,
688
- 'signature2': signature
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(url, headers=headers, data=payload_str)
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=(',', ':')).replace('\\\\', '\\')
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("Failed to connect to bulb %s, reverting to cloud." % bulb.mac)
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