wyzeapy 0.5.27__tar.gz → 0.5.28__tar.gz
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-0.5.27 → wyzeapy-0.5.28}/PKG-INFO +3 -2
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/pyproject.toml +1 -1
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/base_service.py +19 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/lock_service.py +9 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/utils.py +15 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/LICENSES/GPL-3.0-only.txt +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/__init__.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/const.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/crypto.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/exceptions.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/payload_factory.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/__init__.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/bulb_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/camera_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/hms_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/sensor_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/switch_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/thermostat_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/update_manager.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/services/wall_switch_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/tests/test_bulb_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/tests/test_camera_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/tests/test_hms_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/tests/test_lock_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/tests/test_sensor_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/tests/test_switch_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/tests/test_thermostat_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/tests/test_wall_switch_service.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/types.py +0 -0
- {wyzeapy-0.5.27 → wyzeapy-0.5.28}/src/wyzeapy/wyze_auth_lib.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: wyzeapy
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.28
|
|
4
4
|
Summary: A library for interacting with Wyze devices
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Author: Katie Mulliken
|
|
@@ -10,6 +10,7 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
14
|
Requires-Dist: aiodns (>=3.2.0,<4.0.0)
|
|
14
15
|
Requires-Dist: aiohttp (>=3.11.12,<4.0.0)
|
|
15
16
|
Requires-Dist: pycryptodome (>=3.21.0,<4.0.0)
|
|
@@ -626,6 +626,25 @@ class BaseService:
|
|
|
626
626
|
|
|
627
627
|
return response_json
|
|
628
628
|
|
|
629
|
+
async def _get_lock_ble_token(self, device: Device) -> Dict[str, Optional[Any]]:
|
|
630
|
+
await self._auth_lib.refresh_if_should()
|
|
631
|
+
|
|
632
|
+
url_path = "/openapi/lock/v1/ble/token"
|
|
633
|
+
|
|
634
|
+
payload = {
|
|
635
|
+
"uuid": device.mac
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
payload = ford_create_payload(self._auth_lib.token.access_token, payload, url_path, "get")
|
|
639
|
+
|
|
640
|
+
url = f"https://yd-saas-toc.wyzecam.com{url_path}"
|
|
641
|
+
|
|
642
|
+
response_json = await self._auth_lib.get(url, params=payload)
|
|
643
|
+
|
|
644
|
+
check_for_errors_lock(self, response_json)
|
|
645
|
+
|
|
646
|
+
return response_json
|
|
647
|
+
|
|
629
648
|
async def _get_device_info(self, device: Device) -> Dict[Any, Any]:
|
|
630
649
|
await self._auth_lib.refresh_if_should()
|
|
631
650
|
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
# the license with this file. If not, please write to:
|
|
5
5
|
# katie@mulliken.net to receive a copy
|
|
6
6
|
from .base_service import BaseService
|
|
7
|
+
from ..const import FORD_APP_SECRET
|
|
7
8
|
from ..types import Device, DeviceTypes
|
|
9
|
+
from ..utils import wyze_decrypt_cbc
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class Lock(Device):
|
|
@@ -13,12 +15,19 @@ class Lock(Device):
|
|
|
13
15
|
unlocking = False
|
|
14
16
|
door_open = False
|
|
15
17
|
trash_mode = False
|
|
18
|
+
ble_id = None
|
|
19
|
+
ble_token = None
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
class LockService(BaseService):
|
|
19
23
|
async def update(self, lock: Lock):
|
|
20
24
|
device_info = await self._get_lock_info(lock)
|
|
21
25
|
lock.raw_dict = device_info["device"]
|
|
26
|
+
if lock.product_model == "YD_BT1":
|
|
27
|
+
ble_token_info = await self._get_lock_ble_token(lock)
|
|
28
|
+
lock.raw_dict["token"] = ble_token_info["token"]
|
|
29
|
+
lock.ble_id = ble_token_info["token"]["id"]
|
|
30
|
+
lock.ble_token = wyze_decrypt_cbc(FORD_APP_SECRET[:16], ble_token_info["token"]["token"])
|
|
22
31
|
|
|
23
32
|
lock.available = lock.raw_dict.get("onoff_line") == 1
|
|
24
33
|
lock.door_open = lock.raw_dict.get("door_open_status") == 1
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# the license with this file. If not, please write to:
|
|
5
5
|
# katie@mulliken.net to receive a copy
|
|
6
6
|
import base64
|
|
7
|
+
import binascii
|
|
7
8
|
import hashlib
|
|
8
9
|
from typing import Dict, Any, List, Optional
|
|
9
10
|
|
|
@@ -66,6 +67,20 @@ def wyze_decrypt(key, enc):
|
|
|
66
67
|
return decrypt_txt
|
|
67
68
|
|
|
68
69
|
|
|
70
|
+
def wyze_decrypt_cbc(key: str, enc_hex_str: str) -> str:
|
|
71
|
+
key_hash = hashlib.md5(key.encode("utf-8")).digest()
|
|
72
|
+
|
|
73
|
+
iv = b"0123456789ABCDEF"
|
|
74
|
+
cipher = AES.new(key_hash, AES.MODE_CBC, iv)
|
|
75
|
+
|
|
76
|
+
encrypted_bytes = binascii.unhexlify(enc_hex_str)
|
|
77
|
+
decrypted_bytes = cipher.decrypt(encrypted_bytes)
|
|
78
|
+
|
|
79
|
+
# PKCS5Padding
|
|
80
|
+
padding_length = decrypted_bytes[-1]
|
|
81
|
+
return decrypted_bytes[:-padding_length].decode()
|
|
82
|
+
|
|
83
|
+
|
|
69
84
|
def create_password(password: str) -> str:
|
|
70
85
|
hex1 = hashlib.md5(password.encode()).hexdigest()
|
|
71
86
|
hex2 = hashlib.md5(hex1.encode()).hexdigest()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|