import enum
import logging
from collections import defaultdict
from typing import Any, Dict, Optional
import click
from miio import Device, DeviceStatus
from miio.click_common import EnumType, command, format_output
_LOGGER = logging.getLogger(__name__)
MODEL_HUMIDIFIER_MJJSQ = "deerma.humidifier.mjjsq"
MODEL_HUMIDIFIER_JSQ = "deerma.humidifier.jsq"
MODEL_HUMIDIFIER_JSQ1 = "deerma.humidifier.jsq1"
MODEL_HUMIDIFIER_JSQ_COMMON = [
"OnOff_State",
"TemperatureValue",
"Humidity_Value",
"HumiSet_Value",
"Humidifier_Gear",
"Led_State",
"TipSound_State",
"waterstatus",
"watertankstatus",
]
AVAILABLE_PROPERTIES = {
MODEL_HUMIDIFIER_MJJSQ: MODEL_HUMIDIFIER_JSQ_COMMON,
MODEL_HUMIDIFIER_JSQ: MODEL_HUMIDIFIER_JSQ_COMMON,
MODEL_HUMIDIFIER_JSQ1: MODEL_HUMIDIFIER_JSQ_COMMON + ["wet_and_protect"],
}
[docs]
class OperationMode(enum.Enum):
Low = 1
Medium = 2
High = 3
Humidity = 4
WetAndProtect = 5
[docs]
class AirHumidifierStatus(DeviceStatus):
"""Container for status reports from the air humidifier mjjsq."""
def __init__(self, data: Dict[str, Any]) -> None:
"""Response of a Air Humidifier (deerma.humidifier.mjjsq):
{'Humidifier_Gear': 4, 'Humidity_Value': 44, 'HumiSet_Value': 54,
'Led_State': 1, 'OnOff_State': 0, 'TemperatureValue': 21,
'TipSound_State': 1, 'waterstatus': 1, 'watertankstatus': 1}
"""
self.data = data
@property
def power(self) -> str:
"""Power state."""
return "on" if self.data["OnOff_State"] == 1 else "off"
@property
def is_on(self) -> bool:
"""True if device is turned on."""
return self.power == "on"
@property
def mode(self) -> OperationMode:
"""Operation mode.
Can be either low, medium, high or humidity.
"""
return OperationMode(self.data["Humidifier_Gear"])
@property
def temperature(self) -> int:
"""Current temperature in degree celsius."""
return self.data["TemperatureValue"]
@property
def humidity(self) -> int:
"""Current humidity in percent."""
return self.data["Humidity_Value"]
@property
def buzzer(self) -> bool:
"""True if buzzer is turned on."""
return self.data["TipSound_State"] == 1
@property
def led(self) -> bool:
"""True if LED is turned on."""
return self.data["Led_State"] == 1
@property
def target_humidity(self) -> int:
"""Target humiditiy in percent."""
return self.data["HumiSet_Value"]
@property
def no_water(self) -> bool:
"""True if the water tank is empty."""
return self.data["waterstatus"] == 0
@property
def water_tank_detached(self) -> bool:
"""True if the water tank is detached."""
return self.data["watertankstatus"] == 0
@property
def wet_protection(self) -> Optional[bool]:
"""True if wet protection is enabled."""
if self.data["wet_and_protect"] is not None:
return self.data["wet_and_protect"] == 1
return None
@property
def use_time(self) -> Optional[int]:
"""How long the device has been active in seconds.
Not supported by the device, so we return none here.
"""
return None
[docs]
class AirHumidifierMjjsq(Device):
"""Support for deerma.humidifier.(mj)jsq."""
_supported_models = list(AVAILABLE_PROPERTIES.keys())
[docs]
@command(
default_output=format_output(
"",
"Power: {result.power}\n"
"Mode: {result.mode}\n"
"Temperature: {result.temperature} °C\n"
"Humidity: {result.humidity} %\n"
"LED: {result.led}\n"
"Buzzer: {result.buzzer}\n"
"Target humidity: {result.target_humidity} %\n"
"No water: {result.no_water}\n"
"Water tank detached: {result.water_tank_detached}\n"
"Wet protection: {result.wet_protection}\n",
)
)
def status(self) -> AirHumidifierStatus:
"""Retrieve properties."""
properties = AVAILABLE_PROPERTIES.get(
self.model, AVAILABLE_PROPERTIES[MODEL_HUMIDIFIER_MJJSQ]
)
values = self.get_properties(properties, max_properties=1)
return AirHumidifierStatus(defaultdict(lambda: None, zip(properties, values)))
[docs]
@command(default_output=format_output("Powering on"))
def on(self):
"""Power on."""
return self.send("Set_OnOff", [1])
[docs]
@command(default_output=format_output("Powering off"))
def off(self):
"""Power off."""
return self.send("Set_OnOff", [0])
[docs]
@command(
click.argument("mode", type=EnumType(OperationMode)),
default_output=format_output("Setting mode to '{mode.value}'"),
)
def set_mode(self, mode: OperationMode):
"""Set mode."""
return self.send("Set_HumidifierGears", [mode.value])
[docs]
@command(
click.argument("led", type=bool),
default_output=format_output(
lambda led: "Turning on LED" if led else "Turning off LED"
),
)
def set_led(self, led: bool):
"""Turn led on/off."""
return self.send("SetLedState", [int(led)])
[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.send("SetTipSound_Status", [int(buzzer)])
[docs]
@command(
click.argument("humidity", type=int),
default_output=format_output("Setting target humidity to {humidity}"),
)
def set_target_humidity(self, humidity: int):
"""Set the target humidity in percent."""
if humidity < 0 or humidity > 99:
raise ValueError("Invalid target humidity: %s" % humidity)
return self.send("Set_HumiValue", [humidity])
[docs]
@command(
click.argument("protection", type=bool),
default_output=format_output(
lambda protection: (
"Turning on wet protection"
if protection
else "Turning off wet protection"
)
),
)
def set_wet_protection(self, protection: bool):
"""Turn wet protection on/off."""
return self.send("Set_wet_and_protect", [int(protection)])