Browse Source

Add humidifier (#97)

J. Nick Koston 1 year ago
parent
commit
1d8c1053dd

+ 2 - 0
switchbot/__init__.py

@@ -9,6 +9,7 @@ from .devices.bulb import SwitchbotBulb
 from .devices.ceiling_light import SwitchbotCeilingLight
 from .devices.curtain import SwitchbotCurtain
 from .devices.device import ColorMode, SwitchbotDevice
+from .devices.humidifer import SwitchbotHumidifier
 from .devices.light_strip import SwitchbotLightStrip
 from .devices.plug import SwitchbotPlugMini
 from .discovery import GetSwitchbotDevices
@@ -25,6 +26,7 @@ __all__ = [
     "SwitchbotDevice",
     "SwitchbotCurtain",
     "SwitchbotLightStrip",
+    "SwitchbotHumidifier",
     "Switchbot",
     "SwitchbotPlugMini",
     "SwitchbotSupportedType",

+ 7 - 2
switchbot/adv_parser.py

@@ -8,12 +8,12 @@ from typing import TypedDict
 from bleak.backends.device import BLEDevice
 from bleak.backends.scanner import AdvertisementData
 
-from switchbot.adv_parsers.ceiling_light import process_woceiling
-
 from .adv_parsers.bot import process_wohand
 from .adv_parsers.bulb import process_color_bulb
+from .adv_parsers.ceiling_light import process_woceiling
 from .adv_parsers.contact import process_wocontact
 from .adv_parsers.curtain import process_wocurtain
+from .adv_parsers.humidifier import process_wohumidifier
 from .adv_parsers.light_strip import process_wostrip
 from .adv_parsers.meter import process_wosensorth
 from .adv_parsers.motion import process_wopresence
@@ -83,6 +83,11 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
         "modelFriendlyName": "Ceiling Light",
         "func": process_woceiling,
     },
+    "e": {
+        "modelName": SwitchbotModel.HUMIDIFIER,
+        "modelFriendlyName": "Humidifier",
+        "func": process_wohumidifier,
+    },
 }
 
 

+ 26 - 0
switchbot/adv_parsers/humidifier.py

@@ -0,0 +1,26 @@
+"""Humidifier adv parser."""
+from __future__ import annotations
+
+import logging
+
+_LOGGER = logging.getLogger(__name__)
+
+# mfr_data: 943cc68d3d2e
+# data: 650000cd802b6300
+# data: 650000cd802b6300
+# data: 658000c9802b6300
+
+# Low:  658000c5222b6300
+# Med:  658000c5432b6300
+# High: 658000c5642b6300
+def process_wohumidifier(data: bytes, mfr_data: bytes | None) -> dict[str, bool | int]:
+    """Process WoHumi services data."""
+    assert mfr_data is not None
+    _LOGGER.debug("mfr_data: %s", mfr_data.hex())
+    _LOGGER.debug("data: %s", data.hex())
+
+    return {
+        "isOn": bool(data[1]),
+        "level": data[4],
+        "switchMode": True,
+    }

+ 1 - 0
switchbot/const.py

@@ -12,6 +12,7 @@ class SwitchbotModel(StrEnum):
 
     BOT = "WoHand"
     CURTAIN = "WoCurtain"
+    HUMIDIFIER = "WoHumi"
     PLUG_MINI = "WoPlug"
     CONTACT_SENSOR = "WoContact"
     LIGHT_STRIP = "WoStrip"

+ 2 - 2
switchbot/devices/ceiling_light.py

@@ -6,10 +6,10 @@ from typing import Any
 from .base_light import SwitchbotBaseLight
 from .device import REQ_HEADER, ColorMode
 
-CEILING_LIGHT_COMMMAND_HEADER = "5401"
+CEILING_LIGHT_COMMAND_HEADER = "5401"
 CEILING_LIGHT_REQUEST = f"{REQ_HEADER}5501"
 
-CEILING_LIGHT_COMMAND = f"{REQ_HEADER}{CEILING_LIGHT_COMMMAND_HEADER}"
+CEILING_LIGHT_COMMAND = f"{REQ_HEADER}{CEILING_LIGHT_COMMAND_HEADER}"
 CEILING_LIGHT_ON_KEY = f"{CEILING_LIGHT_COMMAND}01FF01FFFF"
 CEILING_LIGHT_OFF_KEY = f"{CEILING_LIGHT_COMMAND}02FF01FFFF"
 CW_BRIGHTNESS_KEY = f"{CEILING_LIGHT_COMMAND}010001"

+ 56 - 0
switchbot/devices/humidifier.py

@@ -0,0 +1,56 @@
+"""Library to handle connection with Switchbot."""
+from __future__ import annotations
+
+from .device import REQ_HEADER, SwitchbotDevice
+
+HUMIDIFIER_COMMAND_HEADER = "4381"
+HUMIDIFIER_REQUEST = f"{REQ_HEADER}4481"
+HUMIDIFIER_COMMAND = f"{REQ_HEADER}{HUMIDIFIER_COMMAND_HEADER}"
+HUMIDIFIER_OFF_KEY = f"{HUMIDIFIER_COMMAND}010080FFFFFFFF"
+HUMIDIFIER_ON_KEY = f"{HUMIDIFIER_COMMAND}010180FFFFFFFF"
+##
+# OFF    570F 4381 0100 80FF FFFF FF
+# ON     570F 4381 0101 80FF FFFF FF
+# AUTO   570F 4381 0101 80FF FFFF FF
+# 1.     570F 4381 0101 22FF FFFF FF
+# 2.     570F 4381 0101 43FF FFFF FF
+# 3    . 570F 4381 0101 64FF FFFF FF
+
+
+class SwitchbotHumidifier(SwitchbotDevice):
+    """Representation of a Switchbot humidifier."""
+
+    async def update(self, interface: int | None = None) -> None:
+        """Update state of device."""
+        await self.get_device_data(retry=self._retry_count, interface=interface)
+
+    async def turn_on(self) -> bool:
+        """Turn device on."""
+        result = await self._send_command(HUMIDIFIER_ON_KEY)
+        ret = self._check_command_result(result, 0, {0x01})
+        self._override_adv_data = {"isOn": True}
+        self._fire_callbacks()
+        return ret
+
+    async def turn_off(self) -> bool:
+        """Turn device off."""
+        result = await self._send_command(HUMIDIFIER_OFF_KEY)
+        ret = self._check_command_result(result, 0, {0x01})
+        self._override_adv_data = {"isOn": False}
+        self._fire_callbacks()
+        return ret
+
+    async def set_level(self, level: int) -> bool:
+        """Set level."""
+        assert 1 <= level <= 100, "Level must be between 1 and 100"
+        result = await self._send_command(
+            f"{HUMIDIFIER_COMMAND}0101{level:02X}FFFFFFFF"
+        )
+        ret = self._check_command_result(result, 0, {0x01})
+        self._override_adv_data = {"isOn": False, "level": level}
+        self._fire_callbacks()
+        return ret
+
+    def is_on(self) -> bool | None:
+        """Return switch state from cache."""
+        return self._get_adv_value("isOn")