Source code for miio.integrations.deerma.humidifier.airhumidifier_jsqs

import enum
import logging
from typing import Any, Dict, Optional

import click

from miio.click_common import EnumType, command, format_output
from miio.miot_device import DeviceStatus, MiotDevice

_LOGGER = logging.getLogger(__name__)
_MAPPING = {
    # Source https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:humidifier:0000A00E:deerma-jsqs:2
    # Air Humidifier (siid=2)
    "power": {"siid": 2, "piid": 1},  # bool
    "fault": {"siid": 2, "piid": 2},  # 0
    "mode": {"siid": 2, "piid": 5},  # 1 - lvl1, 2 - lvl2, 3 - lvl3, 4 - auto
    "target_humidity": {"siid": 2, "piid": 6},  # [40, 80] step 1
    # Environment (siid=3)
    "relative_humidity": {"siid": 3, "piid": 1},  # [0, 100] step 1
    "temperature": {"siid": 3, "piid": 7},  # [-30, 100] step 1
    # Alarm (siid=5)
    "buzzer": {"siid": 5, "piid": 1},  # bool
    # Light (siid=6)
    "led_light": {"siid": 6, "piid": 1},  # bool
    # Other (siid=7)
    "water_shortage_fault": {"siid": 7, "piid": 1},  # bool
    "tank_filed": {"siid": 7, "piid": 2},  # bool
    "overwet_protect": {"siid": 7, "piid": 3},  # bool
}

SUPPORTED_MODELS = [
    "deerma.humidifier.jsqs",
    "deerma.humidifier.jsq5",
    "deerma.humidifier.jsq2w",
]
MIOT_MAPPING = {model: _MAPPING for model in SUPPORTED_MODELS}


[docs] class OperationMode(enum.Enum): Low = 1 Mid = 2 High = 3 Auto = 4
[docs] class AirHumidifierJsqsStatus(DeviceStatus): """Container for status reports from the air humidifier. Xiaomi Mi Smart Humidifer S (deerma.humidifier.[jsqs, jsq5, jsq2w]) response (MIoT format):: [ {'did': 'power', 'siid': 2, 'piid': 1, 'code': 0, 'value': True}, {'did': 'fault', 'siid': 2, 'piid': 2, 'code': 0, 'value': 0}, {'did': 'mode', 'siid': 2, 'piid': 5, 'code': 0, 'value': 1}, {'did': 'target_humidity', 'siid': 2, 'piid': 6, 'code': 0, 'value': 50}, {'did': 'relative_humidity', 'siid': 3, 'piid': 1, 'code': 0, 'value': 40}, {'did': 'temperature', 'siid': 3, 'piid': 7, 'code': 0, 'value': 22.7}, {'did': 'buzzer', 'siid': 5, 'piid': 1, 'code': 0, 'value': False}, {'did': 'led_light', 'siid': 6, 'piid': 1, 'code': 0, 'value': True}, {'did': 'water_shortage_fault', 'siid': 7, 'piid': 1, 'code': 0, 'value': False}, {'did': 'tank_filed', 'siid': 7, 'piid': 2, 'code': 0, 'value': False}, {'did': 'overwet_protect', 'siid': 7, 'piid': 3, 'code': 0, 'value': False} ] """ def __init__(self, data: Dict[str, Any]) -> None: self.data = data # Air Humidifier @property def is_on(self) -> bool: """Return True if device is on.""" return self.data["power"] @property def power(self) -> str: """Return power state.""" return "on" if self.is_on else "off" @property def error(self) -> int: """Return error state.""" return self.data["fault"] @property def mode(self) -> OperationMode: """Return current operation mode.""" try: mode = OperationMode(self.data["mode"]) except ValueError as e: _LOGGER.exception("Cannot parse mode: %s", e) return OperationMode.Auto return mode @property def target_humidity(self) -> Optional[int]: """Return target humidity.""" return self.data.get("target_humidity") # Environment @property def relative_humidity(self) -> Optional[int]: """Return current humidity.""" return self.data.get("relative_humidity") @property def temperature(self) -> Optional[float]: """Return current temperature, if available.""" return self.data.get("temperature") # Alarm @property def buzzer(self) -> Optional[bool]: """Return True if buzzer is on.""" return self.data.get("buzzer") # Indicator Light @property def led_light(self) -> Optional[bool]: """Return status of the LED.""" return self.data.get("led_light") # Other @property def tank_filed(self) -> Optional[bool]: """Return the tank filed.""" return self.data.get("tank_filed") @property def water_shortage_fault(self) -> Optional[bool]: """Return water shortage fault.""" return self.data.get("water_shortage_fault") @property def overwet_protect(self) -> Optional[bool]: """Return True if overwet mode is active.""" return self.data.get("overwet_protect")
[docs] class AirHumidifierJsqs(MiotDevice): """Main class representing the air humidifier which uses MIoT protocol.""" _mappings = MIOT_MAPPING
[docs] @command( default_output=format_output( "", "Power: {result.power}\n" "Error: {result.error}\n" "Target Humidity: {result.target_humidity} %\n" "Relative Humidity: {result.relative_humidity} %\n" "Temperature: {result.temperature} °C\n" "Water tank detached: {result.tank_filed}\n" "Mode: {result.mode}\n" "LED light: {result.led_light}\n" "Buzzer: {result.buzzer}\n" "Overwet protection: {result.overwet_protect}\n", ) ) def status(self) -> AirHumidifierJsqsStatus: """Retrieve properties.""" return AirHumidifierJsqsStatus( { prop["did"]: prop["value"] if prop["code"] == 0 else None for prop in self.get_properties_for_mapping() } )
[docs] @command(default_output=format_output("Powering on")) def on(self): """Power on.""" return self.set_property("power", True)
[docs] @command(default_output=format_output("Powering off")) def off(self): """Power off.""" return self.set_property("power", False)
[docs] @command( click.argument("humidity", type=int), default_output=format_output("Setting target humidity {humidity}%"), ) def set_target_humidity(self, humidity: int): """Set target humidity.""" if humidity < 40 or humidity > 80: raise ValueError( "Invalid target humidity: %s. Must be between 40 and 80" % humidity ) return self.set_property("target_humidity", humidity)
[docs] @command( click.argument("mode", type=EnumType(OperationMode)), default_output=format_output("Setting mode to '{mode.value}'"), ) def set_mode(self, mode: OperationMode): """Set working mode.""" return self.set_property("mode", mode.value)
[docs] @command( click.argument("light", type=bool), default_output=format_output( lambda light: "Turning on LED light" if light else "Turning off LED light" ), ) def set_light(self, light: bool): """Set led light.""" return self.set_property("led_light", light)
[docs] @command( click.argument("buzzer", type=bool), default_output=format_output( lambda buzzer: "Turning on buzzer" if buzzer else "Turning off buzzer" ), ) def set_buzzer(self, buzzer: bool): """Set buzzer on/off.""" return self.set_property("buzzer", buzzer)
[docs] @command( click.argument("overwet", type=bool), default_output=format_output( lambda overwet: "Turning on overwet" if overwet else "Turning off overwet" ), ) def set_overwet_protect(self, overwet: bool): """Set overwet mode on/off.""" return self.set_property("overwet_protect", overwet)