4 Commits e7ad3c8740 ... eec843fcd7

Author SHA1 Message Date
  J. Nick Koston eec843fcd7 Bump version to 0.50.1 (#259) 17 hours ago
  J. Nick Koston 21bb730f18 Remove leftover debug prints from tests (#258) 17 hours ago
  J. Nick Koston 8e655b6626 Bump version to 0.50.0 (#257) 17 hours ago
  J. Nick Koston c385099ca2 Add support for WoTHPc (#256) 17 hours ago
5 changed files with 112 additions and 3 deletions
  1. 1 1
      setup.py
  2. 7 1
      switchbot/adv_parser.py
  3. 39 0
      switchbot/adv_parsers/meter.py
  4. 2 1
      switchbot/const.py
  5. 63 0
      tests/test_adv_parser.py

+ 1 - 1
setup.py

@@ -10,7 +10,7 @@ setup(
         "cryptography>=39.0.0",
         "pyOpenSSL>=23.0.0",
     ],
-    version="0.49.0",
+    version="0.50.1",
     description="A library to communicate with Switchbot",
     author="Daniel Hjelseth Hoyer",
     url="https://github.com/Danielhiversen/pySwitchbot/",

+ 7 - 1
switchbot/adv_parser.py

@@ -20,7 +20,7 @@ from .adv_parsers.hub2 import process_wohub2
 from .adv_parsers.humidifier import process_wohumidifier
 from .adv_parsers.light_strip import process_wostrip
 from .adv_parsers.lock import process_wolock, process_wolock_pro
-from .adv_parsers.meter import process_wosensorth
+from .adv_parsers.meter import process_wosensorth, process_wosensorth_c
 from .adv_parsers.motion import process_wopresence
 from .adv_parsers.plug import process_woplugmini
 from .const import SwitchbotModel
@@ -107,6 +107,12 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
         "func": process_wosensorth,
         "manufacturer_id": 2409,
     },
+    "5": {
+        "modelName": SwitchbotModel.METER_PRO_C,
+        "modelFriendlyName": "Meter",
+        "func": process_wosensorth_c,
+        "manufacturer_id": 2409,
+    },
     "v": {
         "modelName": SwitchbotModel.HUB2,
         "modelFriendlyName": "Hub 2",

+ 39 - 0
switchbot/adv_parsers/meter.py

@@ -41,3 +41,42 @@ def process_wosensorth(data: bytes | None, mfr_data: bytes | None) -> dict[str,
     }
 
     return _wosensorth_data
+
+
+def process_wosensorth_c(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
+    """Process woSensorTH/Temp sensor services data with CO2."""
+    temp_data = None
+    battery = None
+
+    if mfr_data:
+        temp_data = mfr_data[8:11]
+
+    if data:
+        if not temp_data:
+            temp_data = data[3:6]
+        battery = data[2] & 0b01111111
+
+    if not temp_data:
+        return {}
+
+    _temp_sign = 1 if temp_data[1] & 0b10000000 else -1
+    _temp_c = _temp_sign * (
+        (temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
+    )
+    _temp_f = (_temp_c * 9 / 5) + 32
+    _temp_f = (_temp_f * 10) / 10
+    humidity = temp_data[2] & 0b01111111
+
+    if _temp_c == 0 and humidity == 0 and battery == 0:
+        return {}
+
+    _wosensorth_data = {
+        # Data should be flat, but we keep the original structure for now
+        "temp": {"c": _temp_c, "f": _temp_f},
+        "temperature": _temp_c,
+        "fahrenheit": bool(temp_data[2] & 0b10000000),
+        "humidity": humidity,
+        "battery": battery,
+    }
+
+    return _wosensorth_data

+ 2 - 1
switchbot/const.py

@@ -42,7 +42,8 @@ class SwitchbotModel(StrEnum):
     CONTACT_SENSOR = "WoContact"
     LIGHT_STRIP = "WoStrip"
     METER = "WoSensorTH"
-    METER_PRO = "WoSensorTHP"
+    METER_PRO = "WoTHP"
+    METER_PRO_C = "WoTHPc"
     IO_METER = "WoIOSensorTH"
     MOTION_SENSOR = "WoPresence"
     COLOR_BULB = "WoBulb"

+ 63 - 0
tests/test_adv_parser.py

@@ -1619,3 +1619,66 @@ def test_meter_pro_passive() -> None:
         rssi=-67,
         active=False,
     )
+
+
+def test_meter_pro_c_active() -> None:
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={
+            2409: b"\xb0\xe9\xfeT2\x15\xb7\xe4\x07\x9b\xa4\x007\x02\xd5\x00"
+        },
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"5\x00d"},
+        rssi=-67,
+    )
+    result = parse_advertisement_data(ble_device, adv_data)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "battery": 100,
+                "fahrenheit": True,
+                "humidity": 36,
+                "temp": {"c": 27.7, "f": 81.86},
+                "temperature": 27.7,
+            },
+            "isEncrypted": False,
+            "model": "5",
+            "modelFriendlyName": "Meter",
+            "modelName": SwitchbotModel.METER_PRO_C,
+            "rawAdvData": b"5\x00d",
+        },
+        device=ble_device,
+        rssi=-67,
+        active=True,
+    )
+
+
+def test_meter_pro_c_passive() -> None:
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={
+            2409: b"\xb0\xe9\xfeT2\x15\xb7\xe4\x07\x9b\xa4\x007\x02\xd5\x00"
+        },
+        rssi=-67,
+    )
+    result = parse_advertisement_data(ble_device, adv_data, SwitchbotModel.METER_PRO_C)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "battery": None,
+                "fahrenheit": True,
+                "humidity": 36,
+                "temp": {"c": 27.7, "f": 81.86},
+                "temperature": 27.7,
+            },
+            "isEncrypted": False,
+            "model": "5",
+            "modelFriendlyName": "Meter",
+            "modelName": SwitchbotModel.METER_PRO_C,
+            "rawAdvData": None,
+        },
+        device=ble_device,
+        rssi=-67,
+        active=False,
+    )