ソースを参照

Add manufacturer_data parser for contact sensor (#152)

J. Nick Koston 1 年間 前
コミット
e1894f0ffc
2 ファイル変更158 行追加6 行削除
  1. 19 6
      switchbot/adv_parsers/contact.py
  2. 139 0
      tests/test_adv_parser.py

+ 19 - 6
switchbot/adv_parsers/contact.py

@@ -4,14 +4,27 @@ from __future__ import annotations
 
 def process_wocontact(data: bytes, mfr_data: bytes | None) -> dict[str, bool | int]:
     """Process woContact Sensor services data."""
+    battery = data[2] & 0b01111111
+    tested = bool(data[1] & 0b10000000)
     contact_timeout = data[3] & 0b00000100 == 0b00000100
-    contact_open = data[3] & 0b00000010 == 0b00000010
+
+    if mfr_data and len(mfr_data) >= 13:
+        motion_detected = bool(mfr_data[7] & 0b10000000)
+        contact_open = bool(mfr_data[7] & 0b00010000)
+        button_count = mfr_data[12] & 0b00001111
+        is_light = bool(mfr_data[7] & 0b01000000)
+    else:
+        motion_detected = bool(data[1] & 0b01000000)
+        contact_open = data[3] & 0b00000010 == 0b00000010
+        button_count = data[8] & 0b00001111
+        is_light = bool(data[3] & 0b00000001)
+
     return {
-        "tested": bool(data[1] & 0b10000000),
-        "motion_detected": bool(data[1] & 0b01000000),
-        "battery": data[2] & 0b01111111,
+        "tested": tested,
+        "motion_detected": motion_detected,
+        "battery": battery,
         "contact_open": contact_open or contact_timeout,  # timeout still means its open
         "contact_timeout": contact_timeout,
-        "is_light": bool(data[3] & 0b00000001),
-        "button_count": (data[8] & 0b00001111),
+        "is_light": is_light,
+        "button_count": button_count,
     }

+ 139 - 0
tests/test_adv_parser.py

@@ -408,3 +408,142 @@ def test_parse_advertisement_data_curtain_firmware_six_fully_open():
         device=ble_device,
         rssi=-2,
     )
+
+
+def test_contact_sensor_mfr():
+    """Test parsing adv data from new bot firmware."""
+    ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xcb9\xcd\xc4=FA,\x00F\x01\x8f\xc4"},
+        service_data={
+            "0000fd3d-0000-1000-8000-00805f9b34fb": b"d\x00\xda\x04\x00F\x01\x8f\xc4"
+        },
+        tx_power=-127,
+        rssi=-70,
+    )
+    result = parse_advertisement_data(ble_device, adv_data)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "battery": 90,
+                "button_count": 4,
+                "contact_open": True,
+                "contact_timeout": True,
+                "is_light": False,
+                "motion_detected": False,
+                "tested": False,
+            },
+            "isEncrypted": False,
+            "model": "d",
+            "modelFriendlyName": "Contact Sensor",
+            "modelName": SwitchbotModel.CONTACT_SENSOR,
+            "rawAdvData": b"d\x00\xda\x04\x00F\x01\x8f\xc4",
+        },
+        device=ble_device,
+        rssi=-70,
+    )
+
+
+def test_contact_sensor_srv():
+    """Test parsing adv data from new bot firmware."""
+    ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        service_data={
+            "0000fd3d-0000-1000-8000-00805f9b34fb": b"d\x00\xda\x04\x00F\x01\x8f\xc4"
+        },
+        tx_power=-127,
+        rssi=-70,
+    )
+    result = parse_advertisement_data(ble_device, adv_data)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "battery": 90,
+                "button_count": 4,
+                "contact_open": True,
+                "contact_timeout": True,
+                "is_light": False,
+                "motion_detected": False,
+                "tested": False,
+            },
+            "isEncrypted": False,
+            "model": "d",
+            "modelFriendlyName": "Contact Sensor",
+            "modelName": SwitchbotModel.CONTACT_SENSOR,
+            "rawAdvData": b"d\x00\xda\x04\x00F\x01\x8f\xc4",
+        },
+        device=ble_device,
+        rssi=-70,
+    )
+
+
+def test_contact_sensor_open():
+    """Test parsing mfr adv data from new bot firmware."""
+    ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xcb9\xcd\xc4=F\x84\x9c\x00\x17\x00QD"},
+        service_data={
+            "0000fd3d-0000-1000-8000-00805f9b34fb": b"d@\xda\x02\x00\x17\x00QD"
+        },
+        tx_power=-127,
+        rssi=-59,
+    )
+    result = parse_advertisement_data(ble_device, adv_data)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "battery": 90,
+                "button_count": 4,
+                "contact_open": True,
+                "contact_timeout": False,
+                "is_light": False,
+                "motion_detected": True,
+                "tested": False,
+            },
+            "isEncrypted": False,
+            "model": "d",
+            "modelFriendlyName": "Contact Sensor",
+            "modelName": SwitchbotModel.CONTACT_SENSOR,
+            "rawAdvData": b"d@\xda\x02\x00\x17\x00QD",
+        },
+        device=ble_device,
+        rssi=-59,
+    )
+
+
+def test_contact_sensor_closed():
+    """Test parsing mfr adv data from new bot firmware."""
+    ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xcb9\xcd\xc4=F\x89\x8c\x00+\x00\x19\x84"},
+        service_data={
+            "0000fd3d-0000-1000-8000-00805f9b34fb": b"d@\xda\x00\x00+\x00\x19\x84"
+        },
+        tx_power=-127,
+        rssi=-50,
+    )
+    result = parse_advertisement_data(ble_device, adv_data)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "battery": 90,
+                "button_count": 4,
+                "contact_open": False,
+                "contact_timeout": False,
+                "is_light": False,
+                "motion_detected": True,
+                "tested": False,
+            },
+            "isEncrypted": False,
+            "model": "d",
+            "modelFriendlyName": "Contact Sensor",
+            "modelName": SwitchbotModel.CONTACT_SENSOR,
+            "rawAdvData": b"d@\xda\x00\x00+\x00\x19\x84",
+        },
+        device=ble_device,
+        rssi=-50,
+    )