Browse Source

Add Indoor/Outdoor Meter (#210)

Co-authored-by: J. Nick Koston <nick@koston.org>
boozer2 10 months ago
parent
commit
e5bcffad40

+ 6 - 1
switchbot/adv_parser.py

@@ -77,6 +77,12 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
         "func": process_wocurtain,
         "manufacturer_id": 2409,
     },
+    "w": {
+        "modelName": SwitchbotModel.IO_METER,
+        "modelFriendlyName": "Indoor/Outdoor Meter",
+        "func": process_wosensorth,
+        "manufacturer_id": 2409,
+    },
     "i": {
         "modelName": SwitchbotModel.METER,
         "modelFriendlyName": "Meter Plus",
@@ -93,7 +99,6 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
         "modelName": SwitchbotModel.PLUG_MINI,
         "modelFriendlyName": "Plug Mini",
         "func": process_woplugmini,
-        "manufacturer_data_length": 12,
         "manufacturer_id": 2409,
     },
     "j": {

+ 1 - 1
switchbot/adv_parsers/blind_tilt.py

@@ -23,5 +23,5 @@ def process_woblindtilt(
         "inMotion": _in_motion,
         "tilt": (100 - _tilt) if reverse else _tilt,
         "lightLevel": _light_level,
-        "sequence_number": device_data[0]
+        "sequence_number": device_data[0],
     }

+ 5 - 2
switchbot/adv_parsers/meter.py

@@ -6,12 +6,15 @@ from typing import Any
 
 def process_wosensorth(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
     """Process woSensorTH/Temp sensor services data."""
+    temp_data = None
+    battery = None
+
     if mfr_data:
         temp_data = mfr_data[8:11]
-        battery = None
 
     if data:
-        temp_data = data[3:6]
+        if not temp_data:
+            temp_data = data[3:6]
         battery = data[2] & 0b01111111
 
     if not temp_data:

+ 1 - 0
switchbot/const.py

@@ -34,6 +34,7 @@ class SwitchbotModel(StrEnum):
     CONTACT_SENSOR = "WoContact"
     LIGHT_STRIP = "WoStrip"
     METER = "WoSensorTH"
+    IO_METER = "WoIOSensorTH"
     MOTION_SENSOR = "WoPresence"
     COLOR_BULB = "WoBulb"
     CEILING_LIGHT = "WoCeiling"

+ 5 - 1
switchbot/devices/blind_tilt.py

@@ -4,7 +4,11 @@ from __future__ import annotations
 import logging
 from typing import Any
 
-from switchbot.devices.device import REQ_HEADER, update_after_operation, SwitchbotSequenceDevice
+from switchbot.devices.device import (
+    REQ_HEADER,
+    SwitchbotSequenceDevice,
+    update_after_operation,
+)
 
 from .curtain import CURTAIN_EXT_SUM_KEY, SwitchbotCurtain
 

+ 2 - 1
switchbot/discovery.py

@@ -103,7 +103,8 @@ class GetSwitchbotDevices:
         """Return all WoSensorTH/Temp sensor devices with services data."""
         base_meters = await self._get_devices_by_model("T")
         plus_meters = await self._get_devices_by_model("i")
-        return {**base_meters, **plus_meters}
+        io_meters = await self._get_devices_by_model("w")
+        return {**base_meters, **plus_meters, **io_meters}
 
     async def get_contactsensors(self) -> dict[str, SwitchBotAdvertisement]:
         """Return all WoContact/Contact sensor devices with services data."""

+ 64 - 0
tests/test_adv_parser.py

@@ -857,6 +857,70 @@ def test_wosensor_passive_only():
     )
 
 
+def test_woiosensor_passive_and_active():
+    """Test parsing woiosensor as passive with active data as well."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xaa\xbb\xcc\xdd\xee\xff\xe0\x0f\x06\x985\x00"},
+        service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"w\x00\xe4"},
+        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": 100,
+                "fahrenheit": False,
+                "humidity": 53,
+                "temp": {"c": 24.6, "f": 76.28},
+                "temperature": 24.6,
+            },
+            "isEncrypted": False,
+            "model": "w",
+            "modelFriendlyName": "Indoor/Outdoor Meter",
+            "modelName": SwitchbotModel.IO_METER,
+            "rawAdvData": b"w\x00\xe4",
+        },
+        device=ble_device,
+        rssi=-50,
+        active=True,
+    )
+
+
+def test_woiosensor_passive_only():
+    """Test parsing woiosensor with only passive data."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
+    adv_data = generate_advertisement_data(
+        manufacturer_data={2409: b"\xaa\xbb\xcc\xdd\xee\xff\xe0\x0f\x06\x985\x00"},
+        service_data={},
+        tx_power=-127,
+        rssi=-50,
+    )
+    result = parse_advertisement_data(ble_device, adv_data, SwitchbotModel.IO_METER)
+    assert result == SwitchBotAdvertisement(
+        address="aa:bb:cc:dd:ee:ff",
+        data={
+            "data": {
+                "battery": None,
+                "fahrenheit": False,
+                "humidity": 53,
+                "temp": {"c": 24.6, "f": 76.28},
+                "temperature": 24.6,
+            },
+            "isEncrypted": False,
+            "model": "w",
+            "modelFriendlyName": "Indoor/Outdoor Meter",
+            "modelName": SwitchbotModel.IO_METER,
+            "rawAdvData": None,
+        },
+        device=ble_device,
+        rssi=-50,
+        active=False,
+    )
+
+
 def test_motion_sensor_clear():
     """Test parsing motion sensor with clear data."""
     ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")