PySwitchbot 0.46.1__tar.gz → 0.48.0__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.
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/PKG-INFO +1 -1
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/PySwitchbot.egg-info/SOURCES.txt +1 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/README.md +4 -1
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/setup.py +1 -1
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parser.py +14 -1
- pyswitchbot-0.48.0/switchbot/adv_parsers/hub2.py +40 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/const.py +2 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/lock.py +33 -10
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/discovery.py +6 -3
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/tests/test_adv_parser.py +103 -37
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/LICENSE +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/MANIFEST.in +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/setup.cfg +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/__init__.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/lock.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/api_config.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/base_light.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/device.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/enum.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/switchbot/models.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/tests/test_base_cover.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.48.0}/tests/test_curtain.py +0 -0
|
@@ -21,6 +21,7 @@ switchbot/adv_parsers/bulb.py
|
|
|
21
21
|
switchbot/adv_parsers/ceiling_light.py
|
|
22
22
|
switchbot/adv_parsers/contact.py
|
|
23
23
|
switchbot/adv_parsers/curtain.py
|
|
24
|
+
switchbot/adv_parsers/hub2.py
|
|
24
25
|
switchbot/adv_parsers/humidifier.py
|
|
25
26
|
switchbot/adv_parsers/light_strip.py
|
|
26
27
|
switchbot/adv_parsers/lock.py
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# pySwitchbot [](https://travis-ci.org/Danielhiversen/pySwitchbot)
|
|
2
|
+
|
|
2
3
|
Library to control Switchbot IoT devices https://www.switch-bot.com/bot
|
|
3
4
|
|
|
4
5
|
## Obtaining locks encryption key
|
|
6
|
+
|
|
5
7
|
Using the script `scripts/get_encryption_key.py` you can manually obtain locks encryption key.
|
|
6
8
|
|
|
7
9
|
Usage:
|
|
10
|
+
|
|
8
11
|
```shell
|
|
9
12
|
$ python3 get_encryption_key.py MAC USERNAME
|
|
10
13
|
Key ID: xx
|
|
@@ -16,7 +19,7 @@ If authentication succeeds then script should output your key id and encryption
|
|
|
16
19
|
|
|
17
20
|
Examples:
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
- WoLock
|
|
20
23
|
|
|
21
24
|
```python
|
|
22
25
|
import asyncio
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Library to handle connection with Switchbot."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import logging
|
|
@@ -15,6 +16,7 @@ from .adv_parsers.bulb import process_color_bulb
|
|
|
15
16
|
from .adv_parsers.ceiling_light import process_woceiling
|
|
16
17
|
from .adv_parsers.contact import process_wocontact
|
|
17
18
|
from .adv_parsers.curtain import process_wocurtain
|
|
19
|
+
from .adv_parsers.hub2 import process_wohub2
|
|
18
20
|
from .adv_parsers.humidifier import process_wohumidifier
|
|
19
21
|
from .adv_parsers.light_strip import process_wostrip
|
|
20
22
|
from .adv_parsers.lock import process_wolock
|
|
@@ -54,7 +56,6 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
|
|
|
54
56
|
"modelName": SwitchbotModel.BOT,
|
|
55
57
|
"modelFriendlyName": "Bot",
|
|
56
58
|
"func": process_wohand,
|
|
57
|
-
"service_uuids": {"cba20d00-224d-11e6-9fb8-0002a5d5c51b"},
|
|
58
59
|
"manufacturer_id": 89,
|
|
59
60
|
},
|
|
60
61
|
"s": {
|
|
@@ -100,6 +101,12 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
|
|
|
100
101
|
"func": process_wosensorth,
|
|
101
102
|
"manufacturer_id": 2409,
|
|
102
103
|
},
|
|
104
|
+
"v": {
|
|
105
|
+
"modelName": SwitchbotModel.HUB2,
|
|
106
|
+
"modelFriendlyName": "Hub 2",
|
|
107
|
+
"func": process_wohub2,
|
|
108
|
+
"manufacturer_id": 2409,
|
|
109
|
+
},
|
|
103
110
|
"g": {
|
|
104
111
|
"modelName": SwitchbotModel.PLUG_MINI,
|
|
105
112
|
"modelFriendlyName": "Plug Mini",
|
|
@@ -143,6 +150,12 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
|
|
|
143
150
|
"func": process_wolock,
|
|
144
151
|
"manufacturer_id": 2409,
|
|
145
152
|
},
|
|
153
|
+
"$": {
|
|
154
|
+
"modelName": SwitchbotModel.LOCK_PRO,
|
|
155
|
+
"modelFriendlyName": "Lock Pro",
|
|
156
|
+
"func": process_wolock,
|
|
157
|
+
"manufacturer_id": 2409,
|
|
158
|
+
},
|
|
146
159
|
"x": {
|
|
147
160
|
"modelName": SwitchbotModel.BLIND_TILT,
|
|
148
161
|
"modelFriendlyName": "Blind Tilt",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Hub2 parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def process_wohub2(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
|
|
9
|
+
"""Process woHub2 sensor manufacturer data."""
|
|
10
|
+
temp_data = None
|
|
11
|
+
|
|
12
|
+
if mfr_data:
|
|
13
|
+
status = mfr_data[12]
|
|
14
|
+
temp_data = mfr_data[13:16]
|
|
15
|
+
|
|
16
|
+
if not temp_data:
|
|
17
|
+
return {}
|
|
18
|
+
|
|
19
|
+
_temp_sign = 1 if temp_data[1] & 0b10000000 else -1
|
|
20
|
+
_temp_c = _temp_sign * (
|
|
21
|
+
(temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
|
|
22
|
+
)
|
|
23
|
+
_temp_f = (_temp_c * 9 / 5) + 32
|
|
24
|
+
_temp_f = (_temp_f * 10) / 10
|
|
25
|
+
humidity = temp_data[2] & 0b01111111
|
|
26
|
+
light_level = status & 0b11111
|
|
27
|
+
|
|
28
|
+
if _temp_c == 0 and humidity == 0:
|
|
29
|
+
return {}
|
|
30
|
+
|
|
31
|
+
_wohub2_data = {
|
|
32
|
+
# Data should be flat, but we keep the original structure for now
|
|
33
|
+
"temp": {"c": _temp_c, "f": _temp_f},
|
|
34
|
+
"temperature": _temp_c,
|
|
35
|
+
"fahrenheit": bool(temp_data[2] & 0b10000000),
|
|
36
|
+
"humidity": humidity,
|
|
37
|
+
"lightLevel": light_level,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return _wohub2_data
|
|
@@ -16,15 +16,28 @@ from ..const import (
|
|
|
16
16
|
SwitchbotAccountConnectionError,
|
|
17
17
|
SwitchbotApiError,
|
|
18
18
|
SwitchbotAuthenticationError,
|
|
19
|
+
SwitchbotModel,
|
|
19
20
|
)
|
|
20
21
|
from .device import SwitchbotDevice, SwitchbotOperationError
|
|
21
22
|
|
|
22
23
|
COMMAND_HEADER = "57"
|
|
23
24
|
COMMAND_GET_CK_IV = f"{COMMAND_HEADER}0f2103"
|
|
24
|
-
COMMAND_LOCK_INFO =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
COMMAND_LOCK_INFO = {
|
|
26
|
+
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4f8101",
|
|
27
|
+
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4f8102",
|
|
28
|
+
}
|
|
29
|
+
COMMAND_UNLOCK = {
|
|
30
|
+
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4e01011080",
|
|
31
|
+
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4e0101000080",
|
|
32
|
+
}
|
|
33
|
+
COMMAND_UNLOCK_WITHOUT_UNLATCH = {
|
|
34
|
+
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4e010110a0",
|
|
35
|
+
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4e01010000a0",
|
|
36
|
+
}
|
|
37
|
+
COMMAND_LOCK = {
|
|
38
|
+
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4e01011000",
|
|
39
|
+
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4e0101000000",
|
|
40
|
+
}
|
|
28
41
|
COMMAND_ENABLE_NOTIFICATIONS = f"{COMMAND_HEADER}0e01001e00008101"
|
|
29
42
|
COMMAND_DISABLE_NOTIFICATIONS = f"{COMMAND_HEADER}0e00"
|
|
30
43
|
|
|
@@ -49,6 +62,7 @@ class SwitchbotLock(SwitchbotDevice):
|
|
|
49
62
|
key_id: str,
|
|
50
63
|
encryption_key: str,
|
|
51
64
|
interface: int = 0,
|
|
65
|
+
model: SwitchbotModel = SwitchbotModel.LOCK,
|
|
52
66
|
**kwargs: Any,
|
|
53
67
|
) -> None:
|
|
54
68
|
if len(key_id) == 0:
|
|
@@ -59,20 +73,27 @@ class SwitchbotLock(SwitchbotDevice):
|
|
|
59
73
|
raise ValueError("encryption_key is missing")
|
|
60
74
|
elif len(encryption_key) != 32:
|
|
61
75
|
raise ValueError("encryption_key is invalid")
|
|
76
|
+
if model not in (SwitchbotModel.LOCK, SwitchbotModel.LOCK_PRO):
|
|
77
|
+
raise ValueError("initializing SwitchbotLock with a non-lock model")
|
|
62
78
|
self._iv = None
|
|
63
79
|
self._cipher = None
|
|
64
80
|
self._key_id = key_id
|
|
65
81
|
self._encryption_key = bytearray.fromhex(encryption_key)
|
|
66
82
|
self._notifications_enabled: bool = False
|
|
83
|
+
self._model: SwitchbotModel = model
|
|
67
84
|
super().__init__(device, None, interface, **kwargs)
|
|
68
85
|
|
|
69
86
|
@staticmethod
|
|
70
87
|
async def verify_encryption_key(
|
|
71
|
-
device: BLEDevice,
|
|
88
|
+
device: BLEDevice,
|
|
89
|
+
key_id: str,
|
|
90
|
+
encryption_key: str,
|
|
91
|
+
model: SwitchbotModel = SwitchbotModel.LOCK,
|
|
92
|
+
**kwargs: Any,
|
|
72
93
|
) -> bool:
|
|
73
94
|
try:
|
|
74
95
|
lock = SwitchbotLock(
|
|
75
|
-
device
|
|
96
|
+
device, key_id=key_id, encryption_key=encryption_key, model=model
|
|
76
97
|
)
|
|
77
98
|
except ValueError:
|
|
78
99
|
return False
|
|
@@ -183,19 +204,19 @@ class SwitchbotLock(SwitchbotDevice):
|
|
|
183
204
|
async def lock(self) -> bool:
|
|
184
205
|
"""Send lock command."""
|
|
185
206
|
return await self._lock_unlock(
|
|
186
|
-
COMMAND_LOCK, {LockStatus.LOCKED, LockStatus.LOCKING}
|
|
207
|
+
COMMAND_LOCK[self._model], {LockStatus.LOCKED, LockStatus.LOCKING}
|
|
187
208
|
)
|
|
188
209
|
|
|
189
210
|
async def unlock(self) -> bool:
|
|
190
211
|
"""Send unlock command. If unlatch feature is enabled in EU firmware, also unlatches door"""
|
|
191
212
|
return await self._lock_unlock(
|
|
192
|
-
COMMAND_UNLOCK, {LockStatus.UNLOCKED, LockStatus.UNLOCKING}
|
|
213
|
+
COMMAND_UNLOCK[self._model], {LockStatus.UNLOCKED, LockStatus.UNLOCKING}
|
|
193
214
|
)
|
|
194
215
|
|
|
195
216
|
async def unlock_without_unlatch(self) -> bool:
|
|
196
217
|
"""Send unlock command. This command will not unlatch the door."""
|
|
197
218
|
return await self._lock_unlock(
|
|
198
|
-
COMMAND_UNLOCK_WITHOUT_UNLATCH,
|
|
219
|
+
COMMAND_UNLOCK_WITHOUT_UNLATCH[self._model],
|
|
199
220
|
{LockStatus.UNLOCKED, LockStatus.UNLOCKING, LockStatus.NOT_FULLY_LOCKED},
|
|
200
221
|
)
|
|
201
222
|
|
|
@@ -275,7 +296,9 @@ class SwitchbotLock(SwitchbotDevice):
|
|
|
275
296
|
|
|
276
297
|
async def _get_lock_info(self) -> bytes | None:
|
|
277
298
|
"""Return lock info of device."""
|
|
278
|
-
_data = await self._send_command(
|
|
299
|
+
_data = await self._send_command(
|
|
300
|
+
key=COMMAND_LOCK_INFO[self._model], retry=self._retry_count
|
|
301
|
+
)
|
|
279
302
|
|
|
280
303
|
if not self._check_command_result(_data, 0, COMMAND_RESULT_EXPECTED_VALUES):
|
|
281
304
|
_LOGGER.error("Unsuccessful, please try again")
|
|
@@ -42,11 +42,11 @@ class GetSwitchbotDevices:
|
|
|
42
42
|
|
|
43
43
|
devices = None
|
|
44
44
|
devices = bleak.BleakScanner(
|
|
45
|
+
detection_callback=self.detection_callback,
|
|
45
46
|
# TODO: Find new UUIDs to filter on. For example, see
|
|
46
47
|
# https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/4ad138bb09f0fbbfa41b152ca327a78c1d0b6ba9/devicetypes/meter.md
|
|
47
48
|
adapter=self._interface,
|
|
48
49
|
)
|
|
49
|
-
devices.register_detection_callback(self.detection_callback)
|
|
50
50
|
|
|
51
51
|
async with CONNECT_LOCK:
|
|
52
52
|
await devices.start()
|
|
@@ -111,7 +111,8 @@ class GetSwitchbotDevices:
|
|
|
111
111
|
base_meters = await self._get_devices_by_model("T")
|
|
112
112
|
plus_meters = await self._get_devices_by_model("i")
|
|
113
113
|
io_meters = await self._get_devices_by_model("w")
|
|
114
|
-
|
|
114
|
+
hub2_meters = await self._get_devices_by_model("v")
|
|
115
|
+
return {**base_meters, **plus_meters, **io_meters, **hub2_meters}
|
|
115
116
|
|
|
116
117
|
async def get_contactsensors(self) -> dict[str, SwitchBotAdvertisement]:
|
|
117
118
|
"""Return all WoContact/Contact sensor devices with services data."""
|
|
@@ -119,7 +120,9 @@ class GetSwitchbotDevices:
|
|
|
119
120
|
|
|
120
121
|
async def get_locks(self) -> dict[str, SwitchBotAdvertisement]:
|
|
121
122
|
"""Return all WoLock/Locks devices with services data."""
|
|
122
|
-
|
|
123
|
+
locks = await self._get_devices_by_model("o")
|
|
124
|
+
lock_pros = await self._get_devices_by_model("$")
|
|
125
|
+
return {**locks, **lock_pros}
|
|
123
126
|
|
|
124
127
|
async def get_device_data(
|
|
125
128
|
self, address: str
|
|
@@ -964,6 +964,40 @@ def test_wosensor_active_zero_data():
|
|
|
964
964
|
)
|
|
965
965
|
|
|
966
966
|
|
|
967
|
+
def test_wohub2_passive_and_active():
|
|
968
|
+
"""Test parsing wosensor as passive with active data as well."""
|
|
969
|
+
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
970
|
+
adv_data = generate_advertisement_data(
|
|
971
|
+
manufacturer_data={
|
|
972
|
+
2409: b"\xaa\xbb\xcc\xdd\xee\xff\x00\xfffT\x1a\xf1\x82\x07\x9a2\x00"
|
|
973
|
+
},
|
|
974
|
+
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"v\x00"},
|
|
975
|
+
tx_power=-127,
|
|
976
|
+
rssi=-50,
|
|
977
|
+
)
|
|
978
|
+
result = parse_advertisement_data(ble_device, adv_data)
|
|
979
|
+
assert result == SwitchBotAdvertisement(
|
|
980
|
+
address="aa:bb:cc:dd:ee:ff",
|
|
981
|
+
data={
|
|
982
|
+
"data": {
|
|
983
|
+
"fahrenheit": False,
|
|
984
|
+
"humidity": 50,
|
|
985
|
+
"lightLevel": 2,
|
|
986
|
+
"temp": {"c": 26.7, "f": 80.06},
|
|
987
|
+
"temperature": 26.7,
|
|
988
|
+
},
|
|
989
|
+
"isEncrypted": False,
|
|
990
|
+
"model": "v",
|
|
991
|
+
"modelFriendlyName": "Hub 2",
|
|
992
|
+
"modelName": SwitchbotModel.HUB2,
|
|
993
|
+
"rawAdvData": b"v\x00",
|
|
994
|
+
},
|
|
995
|
+
device=ble_device,
|
|
996
|
+
rssi=-50,
|
|
997
|
+
active=True,
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
|
|
967
1001
|
def test_woiosensor_passive_and_active():
|
|
968
1002
|
"""Test parsing woiosensor as passive with active data as well."""
|
|
969
1003
|
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
@@ -1286,43 +1320,6 @@ def test_motion_with_light_detected():
|
|
|
1286
1320
|
)
|
|
1287
1321
|
|
|
1288
1322
|
|
|
1289
|
-
def test_motion_sensor_motion_passive():
|
|
1290
|
-
"""Test parsing motion sensor with motion data."""
|
|
1291
|
-
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
1292
|
-
adv_data = generate_advertisement_data(
|
|
1293
|
-
manufacturer_data={2409: b"\xc0!\x9a\xe8\xbcIi\\\x008"},
|
|
1294
|
-
service_data={},
|
|
1295
|
-
tx_power=-127,
|
|
1296
|
-
rssi=-87,
|
|
1297
|
-
)
|
|
1298
|
-
result = parse_advertisement_data(
|
|
1299
|
-
ble_device, adv_data, SwitchbotModel.MOTION_SENSOR
|
|
1300
|
-
)
|
|
1301
|
-
assert result == SwitchBotAdvertisement(
|
|
1302
|
-
address="aa:bb:cc:dd:ee:ff",
|
|
1303
|
-
data={
|
|
1304
|
-
"data": {
|
|
1305
|
-
"battery": None,
|
|
1306
|
-
"iot": None,
|
|
1307
|
-
"is_light": False,
|
|
1308
|
-
"led": None,
|
|
1309
|
-
"light_intensity": None,
|
|
1310
|
-
"motion_detected": True,
|
|
1311
|
-
"sense_distance": None,
|
|
1312
|
-
"tested": None,
|
|
1313
|
-
},
|
|
1314
|
-
"isEncrypted": False,
|
|
1315
|
-
"model": "s",
|
|
1316
|
-
"modelFriendlyName": "Motion Sensor",
|
|
1317
|
-
"modelName": SwitchbotModel.MOTION_SENSOR,
|
|
1318
|
-
"rawAdvData": None,
|
|
1319
|
-
},
|
|
1320
|
-
device=ble_device,
|
|
1321
|
-
rssi=-87,
|
|
1322
|
-
active=False,
|
|
1323
|
-
)
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
1323
|
def test_parsing_lock_active():
|
|
1327
1324
|
"""Test parsing lock with active data."""
|
|
1328
1325
|
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
@@ -1393,6 +1390,75 @@ def test_parsing_lock_passive():
|
|
|
1393
1390
|
)
|
|
1394
1391
|
|
|
1395
1392
|
|
|
1393
|
+
def test_parsing_lock_pro_active():
|
|
1394
|
+
"""Test parsing lock pro with active data."""
|
|
1395
|
+
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
1396
|
+
adv_data = generate_advertisement_data(
|
|
1397
|
+
manufacturer_data={2409: b"\xc8\xf5,\xd9-V\x07\x82\x00d\x00\x00"},
|
|
1398
|
+
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"$\x80d"},
|
|
1399
|
+
rssi=-80,
|
|
1400
|
+
)
|
|
1401
|
+
result = parse_advertisement_data(ble_device, adv_data, SwitchbotModel.LOCK_PRO)
|
|
1402
|
+
assert result == SwitchBotAdvertisement(
|
|
1403
|
+
address="aa:bb:cc:dd:ee:ff",
|
|
1404
|
+
data={
|
|
1405
|
+
"data": {
|
|
1406
|
+
"battery": 100,
|
|
1407
|
+
"calibration": True,
|
|
1408
|
+
"status": LockStatus.LOCKED,
|
|
1409
|
+
"update_from_secondary_lock": False,
|
|
1410
|
+
"door_open": False,
|
|
1411
|
+
"double_lock_mode": False,
|
|
1412
|
+
"unclosed_alarm": False,
|
|
1413
|
+
"unlocked_alarm": False,
|
|
1414
|
+
"auto_lock_paused": False,
|
|
1415
|
+
"night_latch": False,
|
|
1416
|
+
},
|
|
1417
|
+
"model": "$",
|
|
1418
|
+
"isEncrypted": False,
|
|
1419
|
+
"modelFriendlyName": "Lock Pro",
|
|
1420
|
+
"modelName": SwitchbotModel.LOCK_PRO,
|
|
1421
|
+
"rawAdvData": b"$\x80d",
|
|
1422
|
+
},
|
|
1423
|
+
device=ble_device,
|
|
1424
|
+
rssi=-80,
|
|
1425
|
+
active=True,
|
|
1426
|
+
)
|
|
1427
|
+
|
|
1428
|
+
|
|
1429
|
+
def test_parsing_lock_pro_passive():
|
|
1430
|
+
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
1431
|
+
adv_data = generate_advertisement_data(
|
|
1432
|
+
manufacturer_data={2409: bytes.fromhex("aabbccddeeff208200640000")}, rssi=-67
|
|
1433
|
+
)
|
|
1434
|
+
result = parse_advertisement_data(ble_device, adv_data, SwitchbotModel.LOCK_PRO)
|
|
1435
|
+
assert result == SwitchBotAdvertisement(
|
|
1436
|
+
address="aa:bb:cc:dd:ee:ff",
|
|
1437
|
+
data={
|
|
1438
|
+
"data": {
|
|
1439
|
+
"battery": None,
|
|
1440
|
+
"calibration": True,
|
|
1441
|
+
"status": LockStatus.LOCKED,
|
|
1442
|
+
"update_from_secondary_lock": False,
|
|
1443
|
+
"door_open": False,
|
|
1444
|
+
"double_lock_mode": False,
|
|
1445
|
+
"unclosed_alarm": False,
|
|
1446
|
+
"unlocked_alarm": False,
|
|
1447
|
+
"auto_lock_paused": False,
|
|
1448
|
+
"night_latch": False,
|
|
1449
|
+
},
|
|
1450
|
+
"model": "$",
|
|
1451
|
+
"isEncrypted": False,
|
|
1452
|
+
"modelFriendlyName": "Lock Pro",
|
|
1453
|
+
"modelName": SwitchbotModel.LOCK_PRO,
|
|
1454
|
+
"rawAdvData": None,
|
|
1455
|
+
},
|
|
1456
|
+
device=ble_device,
|
|
1457
|
+
rssi=-67,
|
|
1458
|
+
active=False,
|
|
1459
|
+
)
|
|
1460
|
+
|
|
1461
|
+
|
|
1396
1462
|
def test_parsing_lock_active_old_firmware():
|
|
1397
1463
|
"""Test parsing lock with active data. Old firmware."""
|
|
1398
1464
|
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
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
|
|
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
|