Parcourir la source

feat: add support for Plug Mini IT (#602)

* feat: add support for Plug Mini IT

* fix: recognize Plug Mini IT cloud product ID

* fix: recognize Plug Mini IT pairing advertisements
Onero-testdev il y a 6 heures
Parent
commit
fea44922e1

+ 12 - 0
switchbot/adv_parser.py

@@ -923,6 +923,18 @@ SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
         "func": process_lock2,
         "manufacturer_id": 2409,
     },
+    b"\x00\x12\x43\xc8": {
+        "modelName": SwitchbotModel.PLUG_MINI_IT,
+        "modelFriendlyName": "Plug Mini (IT)",
+        "func": process_relay_switch_1pm,
+        "manufacturer_id": 2409,
+    },
+    b"\x01\x12\x43\xc8": {
+        "modelName": SwitchbotModel.PLUG_MINI_IT,
+        "modelFriendlyName": "Plug Mini (IT)",
+        "func": process_relay_switch_1pm,
+        "manufacturer_id": 2409,
+    },
     b"\x00\x11\x07\x60": {
         "modelName": SwitchbotModel.STANDING_FAN,
         "modelFriendlyName": "Standing Fan",

+ 1 - 0
switchbot/const/__init__.py

@@ -120,6 +120,7 @@ class SwitchbotModel(StrEnum):
     FLOOR_LAMP = "Floor Lamp"
     CANDLE_WARMER_LAMP = "Candle Warmer Lamp"
     PLUG_MINI_EU = "Plug Mini (EU)"
+    PLUG_MINI_IT = "Plug Mini (IT)"
     RGBICWW_STRIP_LIGHT = "RGBICWW Strip Light"
     RGBICWW_FLOOR_LAMP = "RGBICWW Floor Lamp"
     RGBICWW_LIGHT_BARS = "RGBICWW Light Bars"

+ 1 - 0
switchbot/devices/device.py

@@ -118,6 +118,7 @@ API_MODEL_TO_ENUM: dict[str, SwitchbotModel] = {
     "W1162000": SwitchbotModel.RGBICWW_CEILING_LIGHT,
     "W1160000": SwitchbotModel.CIRCULATOR_FAN_PRO,
     "W1104000": SwitchbotModel.PLUG_MINI_EU,
+    "W1197000": SwitchbotModel.PLUG_MINI_IT,
     "W1213000": SwitchbotModel.HUB4,
     "W1128000": SwitchbotModel.SMART_THERMOSTAT_RADIATOR,
     "W1111000": SwitchbotModel.CLIMATE_PANEL,

+ 1 - 0
switchbot/devices/relay_switch.py

@@ -104,6 +104,7 @@ class SwitchbotRelaySwitch(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
             SwitchbotModel.RELAY_SWITCH_1PM,
             SwitchbotModel.RELAY_SWITCH_2PM,
             SwitchbotModel.PLUG_MINI_EU,
+            SwitchbotModel.PLUG_MINI_IT,
         ):
             if channel is None:
                 adv_data["voltage"] = self._get_adv_value("voltage") or 0

+ 25 - 0
tests/test_device.py

@@ -1028,3 +1028,28 @@ async def test_hub4_cloud_model_cache(mock_user_info: dict[str, Any]) -> None:
         )
         assert devices == {"E6:A1:CD:1F:5B:65": SwitchbotModel.HUB4}
         assert _MODEL_TO_MAC_CACHE["E6:A1:CD:1F:5B:65"] is SwitchbotModel.HUB4
+
+
+@pytest.mark.asyncio
+async def test_plug_mini_it_cloud_model_cache(mock_user_info: dict[str, Any]) -> None:
+    """Map the backend product ID and populate the Bluetooth model cache."""
+    response = {
+        "Items": [
+            {
+                "device_mac": "e6a1cd1f5b65",
+                "device_detail": {"device_type": "W1197000"},
+            }
+        ]
+    }
+    with (
+        patch.dict(_MODEL_TO_MAC_CACHE, {}, clear=True),
+        patch.object(
+            SwitchbotBaseDevice, "_async_get_user_info", return_value=mock_user_info
+        ),
+        patch.object(SwitchbotBaseDevice, "api_request", return_value=response),
+    ):
+        devices = await fetch_cloud_devices_by_token(
+            MagicMock(spec=aiohttp.ClientSession), "test-token"
+        )
+        assert devices == {"E6:A1:CD:1F:5B:65": SwitchbotModel.PLUG_MINI_IT}
+        assert _MODEL_TO_MAC_CACHE["E6:A1:CD:1F:5B:65"] is SwitchbotModel.PLUG_MINI_IT

+ 37 - 0
tests/test_plug_mini_it.py

@@ -0,0 +1,37 @@
+import pytest
+
+from switchbot import SwitchbotModel, parse_advertisement_data
+
+from .test_adv_parser import generate_advertisement_data, generate_ble_device
+
+
+@pytest.mark.parametrize("pairing", [0, 1])
+@pytest.mark.parametrize("network", [0, 0x15, 0x75])
+@pytest.mark.parametrize(("status", "is_on"), [(0x01, False), (0x87, True)])
+@pytest.mark.parametrize("encrypted", [False, True])
+def test_plug_mini_it_advertisement(pairing, network, status, is_on, encrypted):
+    service = bytes(
+        [0x80 if encrypted else 0, 0, 0, pairing, 0x12, 0x43, 0xC8, network]
+    )
+    manufacturer = (
+        bytes.fromhex("010203040506")
+        + bytes([99, status, 0, 0])
+        + bytes.fromhex("04e600000000")
+    )
+    adv = parse_advertisement_data(
+        generate_ble_device("01:02:03:04:05:06", "Plug Mini (IT)"),
+        generate_advertisement_data(
+            manufacturer_data={2409: manufacturer},
+            service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": service},
+        ),
+    )
+    assert adv is not None
+    assert adv.data["modelName"] == SwitchbotModel.PLUG_MINI_IT
+    assert adv.data["modelFriendlyName"] == "Plug Mini (IT)"
+    assert adv.data["isEncrypted"] is encrypted
+    assert adv.data["data"] == {
+        "switchMode": True,
+        "sequence_number": 99,
+        "isOn": is_on,
+        "power": 1254.0,
+    }

+ 1 - 0
tests/test_relay_switch.py

@@ -14,6 +14,7 @@ common_params = [
     (b"<\x00\x00\x00", SwitchbotModel.RELAY_SWITCH_1PM),
     (b">\x00\x00\x00", SwitchbotModel.GARAGE_DOOR_OPENER),
     (b"?\x00\x00\x00", SwitchbotModel.PLUG_MINI_EU),
+    (b"\x00\x00\x00\x00\x12\x43\xc8\x15", SwitchbotModel.PLUG_MINI_IT),
 ]