Browse Source

Fix lock discovery when first advertisement is missing service data (#171)

J. Nick Koston 1 year ago
parent
commit
2caa798bc8
4 changed files with 77 additions and 5 deletions
  1. 2 1
      switchbot/__init__.py
  2. 1 1
      switchbot/adv_parser.py
  3. 2 1
      switchbot/adv_parsers/lock.py
  4. 72 2
      tests/test_adv_parser.py

+ 2 - 1
switchbot/__init__.py

@@ -4,7 +4,7 @@ from __future__ import annotations
 from bleak_retry_connector import close_stale_connections, get_device
 
 from .adv_parser import SwitchbotSupportedType, parse_advertisement_data
-from .const import SwitchbotModel
+from .const import LockStatus, SwitchbotModel
 from .devices.base_light import SwitchbotBaseLight
 from .devices.bot import Switchbot
 from .devices.bulb import SwitchbotBulb
@@ -25,6 +25,7 @@ __all__ = [
     "GetSwitchbotDevices",
     "SwitchBotAdvertisement",
     "ColorMode",
+    "LockStatus",
     "SwitchbotBaseLight",
     "SwitchbotBulb",
     "SwitchbotCeilingLight",

+ 1 - 1
switchbot/adv_parser.py

@@ -62,7 +62,6 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
         "modelFriendlyName": "Motion Sensor",
         "func": process_wopresence,
         "manufacturer_id": 2409,
-        "manufacturer_data_length": 10,
     },
     "r": {
         "modelName": SwitchbotModel.LIGHT_STRIP,
@@ -125,6 +124,7 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
         "modelName": SwitchbotModel.LOCK,
         "modelFriendlyName": "Lock",
         "func": process_wolock,
+        "manufacturer_id": 2409,
     },
 }
 

+ 2 - 1
switchbot/adv_parsers/lock.py

@@ -14,7 +14,8 @@ def process_wolock(data: bytes | None, mfr_data: bytes | None) -> dict[str, bool
         return {}
 
     _LOGGER.debug("mfr_data: %s", mfr_data.hex())
-    _LOGGER.debug("data: %s", data.hex())
+    if data:
+        _LOGGER.debug("data: %s", data.hex())
 
     return {
         "battery": data[2] & 0b01111111 if data else None,

+ 72 - 2
tests/test_adv_parser.py

@@ -3,7 +3,7 @@ from typing import Any
 from bleak.backends.device import BLEDevice
 from bleak.backends.scanner import AdvertisementData
 
-from switchbot import SwitchbotModel
+from switchbot import LockStatus, SwitchbotModel
 from switchbot.adv_parser import parse_advertisement_data
 from switchbot.models import SwitchBotAdvertisement
 
@@ -1094,7 +1094,9 @@ def test_motion_sensor_motion_passive():
         tx_power=-127,
         rssi=-87,
     )
-    result = parse_advertisement_data(ble_device, adv_data)
+    result = parse_advertisement_data(
+        ble_device, adv_data, SwitchbotModel.MOTION_SENSOR
+    )
     assert result == SwitchBotAdvertisement(
         address="aa:bb:cc:dd:ee:ff",
         data={
@@ -1118,3 +1120,71 @@ def test_motion_sensor_motion_passive():
         rssi=-87,
         active=False,
     )
+
+
+def test_parsing_lock_active():
+    """Test parsing lock with active data."""
+    ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xf1\t\x9fE\x1a]\x07\x83\x00 "},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"o\x80d"},
+        rssi=-67,
+    )
+    result = parse_advertisement_data(ble_device, adv_data)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "auto_lock_paused": False,
+                "battery": 100,
+                "calibration": True,
+                "door_open": False,
+                "double_lock_mode": False,
+                "status": LockStatus.LOCKED,
+                "unclosed_alarm": False,
+                "unlocked_alarm": False,
+                "update_from_secondary_lock": False,
+            },
+            "isEncrypted": False,
+            "model": "o",
+            "modelFriendlyName": "Lock",
+            "modelName": SwitchbotModel.LOCK,
+            "rawAdvData": b"o\x80d",
+        },
+        device=ble_device,
+        rssi=-67,
+        active=True,
+    )
+
+
+def test_parsing_lock_passive():
+    """Test parsing lock with active data."""
+    ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xf1\t\x9fE\x1a]\x07\x83\x00 "}, rssi=-67
+    )
+    result = parse_advertisement_data(ble_device, adv_data, SwitchbotModel.LOCK)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "auto_lock_paused": False,
+                "battery": None,
+                "calibration": True,
+                "door_open": False,
+                "double_lock_mode": False,
+                "status": LockStatus.LOCKED,
+                "unclosed_alarm": False,
+                "unlocked_alarm": False,
+                "update_from_secondary_lock": False,
+            },
+            "isEncrypted": False,
+            "model": "o",
+            "modelFriendlyName": "Lock",
+            "modelName": SwitchbotModel.LOCK,
+            "rawAdvData": None,
+        },
+        device=ble_device,
+        rssi=-67,
+        active=False,
+    )