Skip to content

Commit efc0cd4

Browse files
zerzhangpre-commit-ci[bot]bdraco
authored
Fix the lock status broadcast parsing (#360)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston <[email protected]>
1 parent fc26e86 commit efc0cd4

File tree

5 files changed

+691
-26
lines changed

5 files changed

+691
-26
lines changed

switchbot/adv_parser.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525
from .adv_parsers.keypad import process_wokeypad
2626
from .adv_parsers.leak import process_leak
2727
from .adv_parsers.light_strip import process_light, process_wostrip
28-
from .adv_parsers.lock import process_lock2, process_wolock, process_wolock_pro
28+
from .adv_parsers.lock import (
29+
process_lock2,
30+
process_locklite,
31+
process_wolock,
32+
process_wolock_pro,
33+
)
2934
from .adv_parsers.meter import process_wosensorth, process_wosensorth_c
3035
from .adv_parsers.motion import process_wopresence
3136
from .adv_parsers.plug import process_woplugmini
@@ -304,7 +309,7 @@ class SwitchbotSupportedType(TypedDict):
304309
"-": {
305310
"modelName": SwitchbotModel.LOCK_LITE,
306311
"modelFriendlyName": "Lock Lite",
307-
"func": process_wolock,
312+
"func": process_locklite,
308313
"manufacturer_id": 2409,
309314
},
310315
b"\x00\x10\xa5\xb8": {

switchbot/adv_parsers/lock.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@
1111

1212
def process_wolock(data: bytes | None, mfr_data: bytes | None) -> dict[str, bool | int]:
1313
"""Support for lock and lock lite process data."""
14+
common_data = process_locklite(data, mfr_data)
15+
if not common_data:
16+
return {}
17+
18+
common_data["door_open"] = bool(mfr_data[7] & 0b00000100)
19+
common_data["unclosed_alarm"] = bool(mfr_data[8] & 0b00100000)
20+
common_data["auto_lock_paused"] = bool(mfr_data[8] & 0b00000010)
21+
22+
return common_data
23+
24+
25+
def process_locklite(
26+
data: bytes | None, mfr_data: bytes | None
27+
) -> dict[str, bool | int]:
28+
"""Support for lock lite process data."""
1429
if mfr_data is None:
1530
return {}
1631

@@ -24,11 +39,8 @@ def process_wolock(data: bytes | None, mfr_data: bytes | None) -> dict[str, bool
2439
"calibration": bool(mfr_data[7] & 0b10000000),
2540
"status": LockStatus((mfr_data[7] & 0b01110000) >> 4),
2641
"update_from_secondary_lock": bool(mfr_data[7] & 0b00001000),
27-
"door_open": bool(mfr_data[7] & 0b00000100),
2842
"double_lock_mode": bool(mfr_data[8] & 0b10000000),
29-
"unclosed_alarm": bool(mfr_data[8] & 0b00100000),
3043
"unlocked_alarm": bool(mfr_data[8] & 0b00010000),
31-
"auto_lock_paused": bool(mfr_data[8] & 0b00000010),
3244
"night_latch": bool(mfr_data[9] & 0b00000001) if len(mfr_data) > 9 else False,
3345
}
3446

@@ -37,10 +49,11 @@ def parse_common_data(mfr_data: bytes | None) -> dict[str, bool | int]:
3749
if mfr_data is None:
3850
return {}
3951

52+
_LOGGER.debug("mfr_data: %s", mfr_data.hex())
4053
return {
4154
"sequence_number": mfr_data[6],
4255
"calibration": bool(mfr_data[7] & 0b10000000),
43-
"status": LockStatus((mfr_data[7] & 0b01111000) >> 4),
56+
"status": LockStatus((mfr_data[7] & 0b01111000) >> 3),
4457
"update_from_secondary_lock": bool(mfr_data[8] & 0b11000000),
4558
"door_open_from_secondary_lock": bool(mfr_data[8] & 0b00100000),
4659
"door_open": bool(mfr_data[8] & 0b00010000),

switchbot/devices/lock.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
COMMAND_LOCK_INFO = {
1717
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4f8101",
1818
SwitchbotModel.LOCK_LITE: f"{COMMAND_HEADER}0f4f8101",
19-
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4f8102",
20-
SwitchbotModel.LOCK_ULTRA: f"{COMMAND_HEADER}0f4f8102",
19+
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4f8104",
20+
SwitchbotModel.LOCK_ULTRA: f"{COMMAND_HEADER}0f4f8107",
2121
}
2222
COMMAND_UNLOCK = {
2323
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4e01011080",
@@ -143,14 +143,18 @@ async def get_basic_info(self) -> dict[str, Any] | None:
143143
lock_raw_data = await self._get_lock_info()
144144
if not lock_raw_data:
145145
return None
146-
146+
_LOGGER.debug(
147+
"lock_raw_data: %s, address: %s", lock_raw_data.hex(), self._device.address
148+
)
147149
basic_data = await self._get_basic_info()
148150
if not basic_data:
149151
return None
150-
151-
return self._parse_lock_data(lock_raw_data[1:]) | self._parse_basic_data(
152-
basic_data
152+
_LOGGER.debug(
153+
"basic_data: %s, address: %s", basic_data.hex(), self._device.address
153154
)
155+
return self._parse_lock_data(
156+
lock_raw_data[1:], self._model
157+
) | self._parse_basic_data(basic_data)
154158

155159
def is_calibrated(self) -> Any:
156160
"""Return True if lock is calibrated."""
@@ -215,7 +219,7 @@ def _notification_handler(self, _sender: int, data: bytearray) -> None:
215219
super()._notification_handler(_sender, data)
216220

217221
def _update_lock_status(self, data: bytearray) -> None:
218-
lock_data = self._parse_lock_data(self._decrypt(data[4:]))
222+
lock_data = self._parse_lock_data(self._decrypt(data[4:]), self._model)
219223
if self._update_parsed_data(lock_data):
220224
# We leave notifications enabled in case
221225
# the lock is operated manually before we
@@ -224,11 +228,25 @@ def _update_lock_status(self, data: bytearray) -> None:
224228
self._fire_callbacks()
225229

226230
@staticmethod
227-
def _parse_lock_data(data: bytes) -> dict[str, Any]:
231+
def _parse_lock_data(data: bytes, model: SwitchbotModel) -> dict[str, Any]:
232+
if model == SwitchbotModel.LOCK:
233+
return {
234+
"calibration": bool(data[0] & 0b10000000),
235+
"status": LockStatus((data[0] & 0b01110000) >> 4),
236+
"door_open": bool(data[0] & 0b00000100),
237+
"unclosed_alarm": bool(data[1] & 0b00100000),
238+
"unlocked_alarm": bool(data[1] & 0b00010000),
239+
}
240+
if model == SwitchbotModel.LOCK_LITE:
241+
return {
242+
"calibration": bool(data[0] & 0b10000000),
243+
"status": LockStatus((data[0] & 0b01110000) >> 4),
244+
"unlocked_alarm": bool(data[1] & 0b00010000),
245+
}
228246
return {
229247
"calibration": bool(data[0] & 0b10000000),
230-
"status": LockStatus((data[0] & 0b01110000) >> 4),
231-
"door_open": bool(data[0] & 0b00000100),
232-
"unclosed_alarm": bool(data[1] & 0b00100000),
233-
"unlocked_alarm": bool(data[1] & 0b00010000),
248+
"status": LockStatus((data[0] & 0b01111000) >> 3),
249+
"door_open": bool(data[1] & 0b00010000),
250+
"unclosed_alarm": bool(data[5] & 0b10000000),
251+
"unlocked_alarm": bool(data[5] & 0b01000000),
234252
}

tests/test_adv_parser.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2650,11 +2650,8 @@ def test_hub3_with_empty_data() -> None:
26502650
"calibration": True,
26512651
"status": LockStatus.UNLOCKED,
26522652
"update_from_secondary_lock": False,
2653-
"door_open": False,
26542653
"double_lock_mode": False,
2655-
"unclosed_alarm": False,
26562654
"unlocked_alarm": False,
2657-
"auto_lock_paused": False,
26582655
"night_latch": False,
26592656
},
26602657
"-",
@@ -2688,7 +2685,7 @@ def test_hub3_with_empty_data() -> None:
26882685
"sequence_number": 58,
26892686
"battery": 100,
26902687
"calibration": True,
2691-
"status": LockStatus.LOCKED,
2688+
"status": LockStatus.UNLOCKED,
26922689
"update_from_secondary_lock": False,
26932690
"door_open": False,
26942691
"door_open_from_secondary_lock": False,
@@ -2771,11 +2768,8 @@ def test_lock_active(test_case: AdvTestCase) -> None:
27712768
"calibration": True,
27722769
"status": LockStatus.UNLOCKED,
27732770
"update_from_secondary_lock": False,
2774-
"door_open": False,
27752771
"double_lock_mode": False,
2776-
"unclosed_alarm": False,
27772772
"unlocked_alarm": False,
2778-
"auto_lock_paused": False,
27792773
"night_latch": False,
27802774
},
27812775
"-",
@@ -2809,7 +2803,7 @@ def test_lock_active(test_case: AdvTestCase) -> None:
28092803
"sequence_number": 58,
28102804
"battery": 100,
28112805
"calibration": True,
2812-
"status": LockStatus.LOCKED,
2806+
"status": LockStatus.UNLOCKED,
28132807
"update_from_secondary_lock": False,
28142808
"door_open": False,
28152809
"door_open_from_secondary_lock": False,

0 commit comments

Comments
 (0)