From a786d75ac9ac1bf03d1ef0cf36903d98519952be Mon Sep 17 00:00:00 2001 From: Andrzej Pomirski Date: Fri, 27 Mar 2026 01:35:26 +0100 Subject: [PATCH 1/2] Add @sensor and @setting descriptors to all remaining integrations Adds introspectable interface decorators to 44 integration files that previously lacked them, bringing descriptor coverage from ~12 to ~56 integrations. This enables downstream consumers like Home Assistant to automatically discover available sensors, settings, and their metadata (icons, units, device classes) without hardcoding per-device knowledge. Addresses #1617 --- .../airdog/airpurifier/airpurifier_airdog.py | 21 +++ .../cgllc/airmonitor/airqualitymonitor.py | 27 ++++ .../airmonitor/airqualitymonitor_miot.py | 40 +++++- .../chuangmi/camera/chuangmi_camera.py | 14 ++ .../chuangmi/plug/chuangmi_plug.py | 7 + miio/integrations/chunmi/cooker/cooker.py | 124 ++++++++++++------ .../deerma/humidifier/airhumidifier_jsqs.py | 25 ++++ .../deerma/humidifier/airhumidifier_mjjsq.py | 25 ++++ .../dmaker/airfresh/airfresh_t2017.py | 36 +++++ miio/integrations/dmaker/fan/fan.py | 30 +++++ miio/integrations/dmaker/fan/fan_miot.py | 34 +++++ .../dreame/vacuum/dreamevacuum_miot.py | 89 +++++++++---- miio/integrations/huayi/light/huizuo.py | 39 ++++++ .../ksmb/walkingpad/walkingpad.py | 35 +++++ miio/integrations/leshow/fan/fan_leshow.py | 31 +++++ .../acpartner/airconditioningcompanion.py | 17 +++ .../acpartner/airconditioningcompanionMCN.py | 8 ++ miio/integrations/lumi/camera/aqaracamera.py | 19 +++ .../lumi/curtain/curtain_youpin.py | 30 +++++ miio/integrations/mijia/vacuum/g1vacuum.py | 61 ++++++--- .../mmgg/petwaterdispenser/status.py | 25 ++++ .../nwt/dehumidifier/airdehumidifier.py | 31 +++++ miio/integrations/philips/light/ceil.py | 37 ++++++ .../philips/light/philips_bulb.py | 7 + .../philips/light/philips_eyecare.py | 31 +++++ .../philips/light/philips_moonlight.py | 32 +++++ .../philips/light/philips_rwread.py | 35 +++++ miio/integrations/pwzn/relay/pwzn_relay.py | 4 + .../roidmi/vacuum/roidmivacuum_miot.py | 121 +++++++++++------ .../shuii/humidifier/airhumidifier_jsq.py | 23 ++++ .../tinymu/toiletlid/toiletlid.py | 7 + .../viomi/viomidishwasher/viomidishwasher.py | 18 +++ .../aircondition/airconditioner_miot.py | 79 +++++++---- .../xiaomi/repeater/wifirepeater.py | 6 + .../xiaomi/wifispeaker/wifispeaker.py | 13 +- .../dual_switch/yeelight_dual_switch.py | 28 ++++ .../yunmi/waterpurifier/waterpurifier.py | 19 +++ .../waterpurifier/waterpurifier_yunmi.py | 76 +++++++---- .../zhimi/airpurifier/airfresh.py | 58 +++++--- .../zhimi/airpurifier/airpurifier.py | 94 ++++++++----- miio/integrations/zhimi/fan/zhimi_miot.py | 55 +++++--- miio/integrations/zhimi/heater/heater.py | 24 ++++ miio/integrations/zhimi/heater/heater_miot.py | 23 ++++ miio/integrations/zimi/clock/alarmclock.py | 8 +- 44 files changed, 1313 insertions(+), 253 deletions(-) diff --git a/miio/integrations/airdog/airpurifier/airpurifier_airdog.py b/miio/integrations/airdog/airpurifier/airpurifier_airdog.py index 6d8ca06ba..70eeea6ce 100644 --- a/miio/integrations/airdog/airpurifier/airpurifier_airdog.py +++ b/miio/integrations/airdog/airpurifier/airpurifier_airdog.py @@ -7,6 +7,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -48,16 +49,24 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is turned on.""" return self.power == "on" @property + @setting( + "Mode", + setter_name="set_mode_and_speed", + icon="mdi:fan", + choices=OperationMode, + ) def mode(self) -> OperationMode: """Operation mode. @@ -66,26 +75,38 @@ def mode(self) -> OperationMode: return OperationMode(self.data["mode"]) @property + @setting( + "Speed", + setter_name="set_mode_and_speed", + icon="mdi:speedometer", + min_value=1, + max_value=5, + step=1, + ) def speed(self) -> int: """Current speed level.""" return self.data["speed"] @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Return True if child lock is on.""" return self.data["lock"] == "lock" @property + @sensor("Clean Filters", icon="mdi:air-filter") def clean_filters(self) -> bool: """True if the display shows "-C-" and the filter must be cleaned.""" return self.data["clean"] == "y" @property + @sensor("PM2.5", unit="μg/m³", icon="mdi:blur", device_class="pm25") def pm25(self) -> int: """Return particulate matter value (0...300μg/m³).""" return self.data["pm"] @property + @sensor("Formaldehyde", icon="mdi:chemical-weapon") def hcho(self) -> Optional[int]: """Return formaldehyde value.""" if self.data["hcho"] is not None: diff --git a/miio/integrations/cgllc/airmonitor/airqualitymonitor.py b/miio/integrations/cgllc/airmonitor/airqualitymonitor.py index 358f987b3..e616efb67 100644 --- a/miio/integrations/cgllc/airmonitor/airqualitymonitor.py +++ b/miio/integrations/cgllc/airmonitor/airqualitymonitor.py @@ -6,6 +6,7 @@ from miio.click_common import command, format_output from miio.device import Device, DeviceStatus +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -58,16 +59,19 @@ def __init__(self, data): self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> Optional[str]: """Current power state.""" return self.data.get("power") @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """Return True if the device is turned on.""" return self.power == "on" @property + @sensor("USB Power", icon="mdi:usb") def usb_power(self) -> Optional[bool]: """Return True if the device's usb is on.""" if "usb_state" in self.data and self.data["usb_state"] is not None: @@ -75,16 +79,19 @@ def usb_power(self) -> Optional[bool]: return None @property + @sensor("AQI", icon="mdi:air-filter") def aqi(self) -> Optional[int]: """Air quality index value (0..600).""" return self.data.get("aqi") @property + @sensor("Battery", unit="%", device_class="battery", icon="mdi:battery") def battery(self) -> Optional[int]: """Current battery level (0..100).""" return self.data.get("battery") @property + @setting("Display Clock", setter_name="set_display_clock", icon="mdi:clock-outline") def display_clock(self) -> Optional[bool]: """Display a clock instead the AQI.""" if "time_state" in self.data and self.data["time_state"] is not None: @@ -92,6 +99,7 @@ def display_clock(self) -> Optional[bool]: return None @property + @setting("Night Mode", setter_name="set_night_mode", icon="mdi:weather-night") def night_mode(self) -> Optional[bool]: """Return True if the night mode is on.""" if "night_state" in self.data and self.data["night_state"] is not None: @@ -99,46 +107,65 @@ def night_mode(self) -> Optional[bool]: return None @property + @sensor("Night Time Begin", icon="mdi:clock-start") def night_time_begin(self) -> Optional[str]: """Return the begin of the night time.""" return self.data.get("night_beg_time") @property + @sensor("Night Time End", icon="mdi:clock-end") def night_time_end(self) -> Optional[str]: """Return the end of the night time.""" return self.data.get("night_end_time") @property + @sensor("Sensor State", icon="mdi:eye") def sensor_state(self) -> Optional[str]: """Sensor state.""" return self.data.get("sensor_state") @property + @sensor( + "CO2", + unit="ppm", + device_class="carbon_dioxide", + icon="mdi:molecule-co2", + ) def co2(self) -> Optional[int]: """Return co2 value (400...9999ppm).""" return self.data.get("co2") @property + @sensor( + "CO2e", + unit="ppm", + device_class="carbon_dioxide", + icon="mdi:molecule-co2", + ) def co2e(self) -> Optional[int]: """Return co2e value (400...9999ppm).""" return self.data.get("co2e") @property + @sensor("Humidity", unit="%", device_class="humidity", icon="mdi:water-percent") def humidity(self) -> Optional[float]: """Return humidity value (0...100%).""" return self.data.get("humidity") @property + @sensor("PM2.5", unit="μg/m³", device_class="pm25", icon="mdi:blur") def pm25(self) -> Optional[float]: """Return pm2.5 value (0...999μg/m³).""" return self.data.get("pm25") @property + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> Optional[float]: """Return temperature value (-10...50°C).""" return self.data.get("temperature") @property + @sensor("TVOC", icon="mdi:cloud") def tvoc(self) -> Optional[int]: """Return tvoc value.""" return self.data.get("tvoc") diff --git a/miio/integrations/cgllc/airmonitor/airqualitymonitor_miot.py b/miio/integrations/cgllc/airmonitor/airqualitymonitor_miot.py index a405f9a46..a734a3bb2 100644 --- a/miio/integrations/cgllc/airmonitor/airqualitymonitor_miot.py +++ b/miio/integrations/cgllc/airmonitor/airqualitymonitor_miot.py @@ -4,6 +4,7 @@ import click from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting from miio.miot_device import DeviceStatus, MiotDevice _LOGGER = logging.getLogger(__name__) @@ -107,57 +108,94 @@ def __init__(self, data): self.data = data @property + @sensor("Humidity", unit="%", icon="mdi:water-percent", device_class="humidity") def humidity(self) -> int: """Return humidity value (0...100%).""" return self.data["humidity"] @property + @sensor("PM2.5", unit="µg/m³", icon="mdi:blur") def pm25(self) -> int: """Return PM 2.5 value (0...1000ppm).""" return self.data["pm25"] @property + @sensor("PM10", unit="µg/m³", icon="mdi:blur-linear") def pm10(self) -> int: """Return PM 10 value (0...1000ppm).""" return self.data["pm10"] @property + @sensor("Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature") def temperature(self) -> float: """Return temperature value (-30...100°C).""" return self.data["temperature"] @property + @sensor("CO2", unit="ppm", icon="mdi:molecule-co2", device_class="carbon_dioxide") def co2(self) -> int: """Return co2 value (0...9999ppm).""" return self.data["co2"] @property + @sensor("Battery", unit="%", icon="mdi:battery", device_class="battery") def battery(self) -> int: """Return battery level (0...100%).""" return self.data["battery"] @property + @sensor("Charging State", icon="mdi:battery-charging") def charging_state(self) -> ChargingState: """Return charging state.""" return ChargingState(self.data["charging_state"]) @property + @setting( + "Monitoring Frequency", + unit="s", + setter_name="set_monitoring_frequency_duration", + min_value=0, + max_value=600, + icon="mdi:update", + ) def monitoring_frequency(self) -> int: """Return monitoring frequency time (0..600 s).""" return self.data["monitoring_frequency"] @property + @setting( + "Screen Off", + unit="s", + setter_name="set_screen_off_duration", + min_value=0, + max_value=300, + icon="mdi:monitor-off", + ) def screen_off(self) -> int: """Return screen off time (0..300 s).""" return self.data["screen_off"] @property + @setting( + "Device Off", + unit="min", + setter_name="set_device_off_duration", + min_value=0, + max_value=60, + icon="mdi:power-off", + ) def device_off(self) -> int: """Return device off time (0..60 min).""" return self.data["device_off"] @property - def display_temperature_unit(self): + @setting( + "Display Temperature Unit", + setter_name="set_display_temperature_unit", + choices=DisplayTemperatureUnitCGDN1, + icon="mdi:temperature-celsius", + ) + def display_temperature_unit(self) -> DisplayTemperatureUnitCGDN1: """Return display temperature unit.""" return DisplayTemperatureUnitCGDN1(self.data["temperature_unit"]) diff --git a/miio/integrations/chuangmi/camera/chuangmi_camera.py b/miio/integrations/chuangmi/camera/chuangmi_camera.py index b53867209..c85deae8c 100644 --- a/miio/integrations/chuangmi/camera/chuangmi_camera.py +++ b/miio/integrations/chuangmi/camera/chuangmi_camera.py @@ -11,6 +11,7 @@ from miio.click_common import EnumType, command, format_output from miio.device import Device, DeviceStatus +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -91,66 +92,79 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @setting(name="Power", setter_name="on", icon="mdi:power") def power(self) -> bool: """Camera power.""" return self.data["power"] == "on" @property + @setting(name="Motion Record", setter_name="motion_record_on", icon="mdi:motion-sensor") def motion_record(self) -> bool: """Motion record status.""" return self.data["motion_record"] == "on" @property + @setting(name="Light", setter_name="light_on", icon="mdi:lightbulb") def light(self) -> bool: """Camera light status.""" return self.data["light"] == "on" @property + @setting(name="Full Color", setter_name="full_color_on", icon="mdi:palette") def full_color(self) -> bool: """Full color with bad lighting conditions.""" return self.data["full_color"] == "on" @property + @setting(name="Flip", setter_name="flip_on", icon="mdi:flip-vertical") def flip(self) -> bool: """Image 180 degrees flip status.""" return self.data["flip"] == "on" @property + @setting(name="Improve Program", setter_name="improve_program_on", icon="mdi:chart-line") def improve_program(self) -> bool: """Customer experience improvement program status.""" return self.data["improve_program"] == "on" @property + @setting(name="WDR", setter_name="wdr_on", icon="mdi:contrast-box") def wdr(self) -> bool: """Wide dynamic range status.""" return self.data["wdr"] == "on" @property + @sensor(name="Track", icon="mdi:crosshairs-gps") def track(self) -> bool: """Tracking status.""" return self.data["track"] == "on" @property + @setting(name="Watermark", setter_name="watermark_on", icon="mdi:watermark") def watermark(self) -> bool: """Apply watermark to video.""" return self.data["watermark"] == "on" @property + @sensor(name="SD Card Status", icon="mdi:sd") def sdcard_status(self) -> int: """SD card status.""" return self.data["sdcard_status"] @property + @sensor(name="Max Client", icon="mdi:account-multiple") def max_client(self) -> int: """Unknown.""" return self.data["max_client"] @property + @sensor(name="Night Mode", icon="mdi:weather-night") def night_mode(self) -> int: """Night mode.""" return self.data["night_mode"] @property + @sensor(name="Mini Level", icon="mdi:volume-low") def mini_level(self) -> int: """Unknown.""" return self.data["mini_level"] diff --git a/miio/integrations/chuangmi/plug/chuangmi_plug.py b/miio/integrations/chuangmi/plug/chuangmi_plug.py index f60e616aa..087a2144e 100644 --- a/miio/integrations/chuangmi/plug/chuangmi_plug.py +++ b/miio/integrations/chuangmi/plug/chuangmi_plug.py @@ -6,6 +6,7 @@ from miio import Device, DeviceException, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting from miio.utils import deprecated _LOGGER = logging.getLogger(__name__) @@ -45,6 +46,7 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power-plug") def power(self) -> bool: """Current power state.""" if "on" in self.data: @@ -55,15 +57,18 @@ def power(self) -> bool: raise DeviceException("There was neither 'on' or 'power' in data") @property + @setting("Power", setter_name="on", icon="mdi:power-plug") def is_on(self) -> bool: """True if device is on.""" return self.power @property + @sensor("Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature") def temperature(self) -> int: return self.data["temperature"] @property + @setting("USB Power", setter_name="usb_on", icon="mdi:usb") def usb_power(self) -> Optional[bool]: """True if USB is on.""" if "usb_on" in self.data and self.data["usb_on"] is not None: @@ -71,6 +76,7 @@ def usb_power(self) -> Optional[bool]: return None @property + @sensor("Load Power", unit="W", icon="mdi:flash", device_class="power") def load_power(self) -> Optional[float]: """Current power load, if available.""" if "load_power" in self.data and self.data["load_power"] is not None: @@ -84,6 +90,7 @@ def wifi_led(self) -> Optional[bool]: return self.led @property + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> Optional[bool]: """True if the wifi led is turned on.""" if "wifi_led" in self.data and self.data["wifi_led"] is not None: diff --git a/miio/integrations/chunmi/cooker/cooker.py b/miio/integrations/chunmi/cooker/cooker.py index 3470b5916..df19cf177 100644 --- a/miio/integrations/chunmi/cooker/cooker.py +++ b/miio/integrations/chunmi/cooker/cooker.py @@ -9,6 +9,7 @@ from miio.click_common import command, format_output from miio.device import Device, DeviceStatus +from miio.devicestatus import sensor _LOGGER = logging.getLogger(__name__) @@ -124,12 +125,14 @@ def __init__(self, data: str): self.data = [int(data[i : i + 2], 16) for i in range(0, len(data), 2)] else: self.data = [] - @property + + @sensor("Temperatures", unit="°C", icon="mdi:thermometer") def temperatures(self) -> list[int]: return self.data - @property + + @sensor("Raw", icon="mdi:code-string") def raw(self) -> str: return "".join([f"{value:02x}" for value in self.data]) @@ -163,28 +166,34 @@ def __init__(self, custom: str): Octet 11-16 (01 00 00 00 1d 1f): Meaning unknown """ self.custom = [int(custom[i : i + 2], 16) for i in range(0, len(custom), 2)] - @property + + @sensor("Jingzhu Appointment", icon="mdi:clock-outline") def jingzhu_appointment(self) -> time: return time(hour=self.custom[0], minute=self.custom[1]) - @property + + @sensor("Kuaizhu Appointment", icon="mdi:clock-outline") def kuaizhu_appointment(self) -> time: return time(hour=self.custom[2], minute=self.custom[3]) - @property + + @sensor("Zhuzhou Appointment", icon="mdi:clock-outline") def zhuzhou_appointment(self) -> time: return time(hour=self.custom[4], minute=self.custom[5]) - @property + + @sensor("Zhuzhou Cooking", icon="mdi:clock-outline") def zhuzhou_cooking(self) -> time: return time(hour=self.custom[6], minute=self.custom[7]) - @property + + @sensor("Favorite Appointment", icon="mdi:clock-outline") def favorite_appointment(self) -> time: return time(hour=self.custom[8], minute=self.custom[9]) - @property + + @sensor("Favorite Cooking", icon="mdi:clock-outline") def favorite_cooking(self) -> time: return time(hour=self.custom[10], minute=self.custom[11]) @@ -206,8 +215,9 @@ def __init__(self, stage: str): Octet 5 (ff): Meaning unknown. """ self.stage = stage - @property + + @sensor("State", icon="mdi:pot-steam") def state(self) -> int: """ @@ -216,38 +226,44 @@ def state(self) -> int: 12: Cooking finished """ return int(self.stage[0:2], 16) - @property + + @sensor("Rice ID", icon="mdi:rice") def rice_id(self) -> int: return int(self.stage[2:6], 16) - @property + + @sensor("Taste", icon="mdi:food-variant") def taste(self) -> int: return int(self.stage[6:8], 16) - @property + + @sensor("Taste Phase", icon="mdi:food-variant") def taste_phase(self) -> int: phase = int(self.taste / 33) if phase > 2: return 2 return phase - @property + + @sensor("Name", icon="mdi:label") def name(self) -> str: try: return COOKING_STAGES[self.state]["name"] except KeyError: return "Unknown stage" - @property + + @sensor("Description", icon="mdi:text") def description(self) -> str: try: return COOKING_STAGES[self.state]["description"] except KeyError: return "" - @property + + @sensor("Raw", icon="mdi:code-string") def raw(self) -> str: return self.stage @@ -268,24 +284,27 @@ def __init__(self, timeouts: Optional[str] = None): self.timeouts = [ int(timeouts[i : i + 2], 16) for i in range(0, len(timeouts), 2) ] - @property + + @sensor("LED Off Timeout", unit="s", icon="mdi:led-off") def led_off(self) -> int: return self.timeouts[0] @led_off.setter def led_off(self, delay: int): self.timeouts[0] = delay - @property + + @sensor("Lid Open Timeout", unit="s", icon="mdi:pot-steam-outline") def lid_open(self) -> int: return self.timeouts[1] @lid_open.setter def lid_open(self, timeout: int): self.timeouts[1] = timeout - @property + + @sensor("Lid Open Warning Timeout", unit="s", icon="mdi:alert") def lid_open_warning(self) -> int: return self.timeouts[2] @@ -323,8 +342,9 @@ def __init__(self, settings: Optional[str] = None): self._settings = [ int(settings[i : i + 2], 16) for i in range(0, len(settings), 2) ] - @property + + @sensor("Pressure Supported", icon="mdi:gauge") def pressure_supported(self) -> bool: return self._settings[0] & 1 != 0 @@ -334,8 +354,9 @@ def pressure_supported(self, supported: bool): self._settings[0] |= 1 else: self._settings[0] &= 254 - @property + + @sensor("LED On", icon="mdi:led-on") def led_on(self) -> bool: return self._settings[0] & 2 != 0 @@ -345,8 +366,9 @@ def led_on(self, on: bool): self._settings[0] |= 2 else: self._settings[0] &= 253 - @property + + @sensor("Auto Keep Warm", icon="mdi:pot-steam") def auto_keep_warm(self) -> bool: return self._settings[0] & 4 != 0 @@ -356,8 +378,9 @@ def auto_keep_warm(self, keep_warm: bool): self._settings[0] |= 4 else: self._settings[0] &= 251 - @property + + @sensor("Lid Open Warning", icon="mdi:alert") def lid_open_warning(self) -> bool: return self._settings[0] & 8 != 0 @@ -367,8 +390,9 @@ def lid_open_warning(self, alarm: bool): self._settings[0] |= 8 else: self._settings[0] &= 247 - @property + + @sensor("Lid Open Warning Delayed", icon="mdi:alert-outline") def lid_open_warning_delayed(self) -> bool: return self._settings[0] & 16 != 0 @@ -378,8 +402,9 @@ def lid_open_warning_delayed(self, alarm: bool): self._settings[0] |= 16 else: self._settings[0] &= 239 - @property + + @sensor("Jingzhu Auto Keep Warm", icon="mdi:pot-steam") def jingzhu_auto_keep_warm(self) -> bool: return self._settings[1] & 1 != 0 @@ -389,8 +414,9 @@ def jingzhu_auto_keep_warm(self, auto_keep_warm: bool): self._settings[1] |= 1 else: self._settings[1] &= 254 - @property + + @sensor("Kuaizhu Auto Keep Warm", icon="mdi:pot-steam") def kuaizhu_auto_keep_warm(self) -> bool: return self._settings[1] & 2 != 0 @@ -400,8 +426,9 @@ def kuaizhu_auto_keep_warm(self, auto_keep_warm: bool): self._settings[1] |= 2 else: self._settings[1] &= 253 - @property + + @sensor("Zhuzhou Auto Keep Warm", icon="mdi:pot-steam") def zhuzhou_auto_keep_warm(self) -> bool: return self._settings[1] & 4 != 0 @@ -411,8 +438,9 @@ def zhuzhou_auto_keep_warm(self, auto_keep_warm: bool): self._settings[1] |= 4 else: self._settings[1] &= 251 - @property + + @sensor("Favorite Auto Keep Warm", icon="mdi:pot-steam") def favorite_auto_keep_warm(self) -> bool: return self._settings[1] & 8 != 0 @@ -469,18 +497,21 @@ def __init__(self, data): meal is ready: ['autokeepwarm', '0001', '1000000000', '031e0b23031e', '1', '750', '60', '0207', '05040f', '00030017', '0100', 'ffffffffffff011effff01000000535d'] """ self.data = data - @property + + @sensor("Mode", icon="mdi:pot-steam") def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["func"]) - @property + + @sensor("Menu", icon="mdi:book-open-variant") def menu(self) -> int: """Selected recipe id.""" return int(self.data["menu"], 16) - @property + + @sensor("Stage", icon="mdi:pot-steam") def stage(self) -> Optional[CookingStage]: """Current stage if cooking.""" stage = self.data["stage"] @@ -488,8 +519,9 @@ def stage(self) -> Optional[CookingStage]: return CookingStage(stage) return None - @property + + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> Optional[int]: """Current temperature, if idle. @@ -500,8 +532,9 @@ def temperature(self) -> Optional[int]: return int(value) return None - @property + + @sensor("Start Time", icon="mdi:clock-start") def start_time(self) -> Optional[time]: """Start time of cooking? @@ -513,13 +546,15 @@ def start_time(self) -> Optional[time]: return time(hour=int(value[4:6], 16), minute=int(value[6:8], 16)) return None - @property + + @sensor("Remaining", unit="min", icon="mdi:timer-outline") def remaining(self) -> int: """Remaining minutes of the cooking process.""" return int(self.data["t_func"]) - @property + + @sensor("Cooking Delayed", unit="min", icon="mdi:timer-sand") def cooking_delayed(self) -> Optional[int]: """Wait n minutes before cooking / scheduled cooking.""" delay = int(self.data["t_precook"]) @@ -528,41 +563,48 @@ def cooking_delayed(self) -> Optional[int]: return delay return None - @property + + @sensor("Duration", unit="min", icon="mdi:timer-outline") def duration(self) -> int: """Duration of the cooking process.""" return int(self.data["t_cook"]) - @property + + @sensor("Cooker Settings", icon="mdi:cog") def cooker_settings(self) -> CookerSettings: """Settings of the cooker.""" return CookerSettings(self.data["setting"]) - @property + + @sensor("Interaction Timeouts", icon="mdi:timer-cog-outline") def interaction_timeouts(self) -> InteractionTimeouts: """Interaction timeouts.""" return InteractionTimeouts(self.data["delay"]) - @property + + @sensor("Hardware Version", icon="mdi:chip") def hardware_version(self) -> int: """Hardware version.""" return int(self.data["version"][0:4], 16) - @property + + @sensor("Firmware Version", icon="mdi:update") def firmware_version(self) -> int: """Firmware version.""" return int(self.data["version"][4:8], 16) - @property + + @sensor("Favorite", icon="mdi:star") def favorite(self) -> int: """Favored recipe id. Can be compared with the menu property. """ return int(self.data["favorite"], 16) - @property + + @sensor("Custom", icon="mdi:tune") def custom(self) -> Optional[CookerCustomizations]: custom = self.data["custom"] diff --git a/miio/integrations/deerma/humidifier/airhumidifier_jsqs.py b/miio/integrations/deerma/humidifier/airhumidifier_jsqs.py index 84d4b3837..fe029b289 100644 --- a/miio/integrations/deerma/humidifier/airhumidifier_jsqs.py +++ b/miio/integrations/deerma/humidifier/airhumidifier_jsqs.py @@ -5,6 +5,7 @@ import click from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting from miio.miot_device import DeviceStatus, MiotDevice _LOGGER = logging.getLogger(__name__) @@ -69,21 +70,30 @@ def __init__(self, data: dict[str, Any]) -> None: # Air Humidifier @property + @setting(name="Power", setter_name="on", icon="mdi:power") def is_on(self) -> bool: """Return True if device is on.""" return self.data["power"] @property + @sensor(name="Power", icon="mdi:power") def power(self) -> str: """Return power state.""" return "on" if self.is_on else "off" @property + @sensor(name="Error", icon="mdi:alert-circle") def error(self) -> int: """Return error state.""" return self.data["fault"] @property + @setting( + name="Mode", + setter_name="set_mode", + icon="mdi:fan", + choices=OperationMode, + ) def mode(self) -> OperationMode: """Return current operation mode.""" @@ -96,6 +106,14 @@ def mode(self) -> OperationMode: return mode @property + @setting( + name="Target Humidity", + setter_name="set_target_humidity", + unit="%", + icon="mdi:water-percent", + min_value=40, + max_value=80, + ) def target_humidity(self) -> Optional[int]: """Return target humidity.""" return self.data.get("target_humidity") @@ -103,11 +121,13 @@ def target_humidity(self) -> Optional[int]: # Environment @property + @sensor(name="Relative Humidity", unit="%", device_class="humidity") def relative_humidity(self) -> Optional[int]: """Return current humidity.""" return self.data.get("relative_humidity") @property + @sensor(name="Temperature", unit="C", device_class="temperature") def temperature(self) -> Optional[float]: """Return current temperature, if available.""" return self.data.get("temperature") @@ -115,6 +135,7 @@ def temperature(self) -> Optional[float]: # Alarm @property + @setting(name="Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> Optional[bool]: """Return True if buzzer is on.""" return self.data.get("buzzer") @@ -122,6 +143,7 @@ def buzzer(self) -> Optional[bool]: # Indicator Light @property + @setting(name="LED Light", setter_name="set_light", icon="mdi:led-outline") def led_light(self) -> Optional[bool]: """Return status of the LED.""" return self.data.get("led_light") @@ -129,16 +151,19 @@ def led_light(self) -> Optional[bool]: # Other @property + @sensor(name="Tank Filed", icon="mdi:cup-water") def tank_filed(self) -> Optional[bool]: """Return the tank filed.""" return self.data.get("tank_filed") @property + @sensor(name="Water Shortage Fault", icon="mdi:water-off") def water_shortage_fault(self) -> Optional[bool]: """Return water shortage fault.""" return self.data.get("water_shortage_fault") @property + @setting(name="Overwet Protect", setter_name="set_overwet_protect", icon="mdi:shield-check") def overwet_protect(self) -> Optional[bool]: """Return True if overwet mode is active.""" return self.data.get("overwet_protect") diff --git a/miio/integrations/deerma/humidifier/airhumidifier_mjjsq.py b/miio/integrations/deerma/humidifier/airhumidifier_mjjsq.py index 5a85bc3d2..2a59fd8a3 100644 --- a/miio/integrations/deerma/humidifier/airhumidifier_mjjsq.py +++ b/miio/integrations/deerma/humidifier/airhumidifier_mjjsq.py @@ -7,6 +7,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -55,16 +56,24 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor(name="Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["OnOff_State"] == 1 else "off" @property + @setting(name="Power", setter_name="on", icon="mdi:power") def is_on(self) -> bool: """True if device is turned on.""" return self.power == "on" @property + @setting( + name="Mode", + setter_name="set_mode", + icon="mdi:fan", + choices=OperationMode, + ) def mode(self) -> OperationMode: """Operation mode. @@ -73,41 +82,56 @@ def mode(self) -> OperationMode: return OperationMode(self.data["Humidifier_Gear"]) @property + @sensor(name="Temperature", unit="C", device_class="temperature") def temperature(self) -> int: """Current temperature in degree celsius.""" return self.data["TemperatureValue"] @property + @sensor(name="Humidity", unit="%", device_class="humidity") def humidity(self) -> int: """Current humidity in percent.""" return self.data["Humidity_Value"] @property + @setting(name="Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["TipSound_State"] == 1 @property + @setting(name="LED", setter_name="set_led", icon="mdi:led-outline") def led(self) -> bool: """True if LED is turned on.""" return self.data["Led_State"] == 1 @property + @setting( + name="Target Humidity", + setter_name="set_target_humidity", + unit="%", + icon="mdi:water-percent", + min_value=0, + max_value=99, + ) def target_humidity(self) -> int: """Target humiditiy in percent.""" return self.data["HumiSet_Value"] @property + @sensor(name="No Water", icon="mdi:water-off") def no_water(self) -> bool: """True if the water tank is empty.""" return self.data["waterstatus"] == 0 @property + @sensor(name="Water Tank Detached", icon="mdi:cup-water") def water_tank_detached(self) -> bool: """True if the water tank is detached.""" return self.data["watertankstatus"] == 0 @property + @setting(name="Wet Protection", setter_name="set_wet_protection", icon="mdi:shield-check") def wet_protection(self) -> Optional[bool]: """True if wet protection is enabled.""" if self.data["wet_and_protect"] is not None: @@ -116,6 +140,7 @@ def wet_protection(self) -> Optional[bool]: return None @property + @sensor(name="Use Time", unit="s", icon="mdi:timer") def use_time(self) -> Optional[int]: """How long the device has been active in seconds. diff --git a/miio/integrations/dmaker/airfresh/airfresh_t2017.py b/miio/integrations/dmaker/airfresh/airfresh_t2017.py index 00c873cbf..684cf2b0d 100644 --- a/miio/integrations/dmaker/airfresh/airfresh_t2017.py +++ b/miio/integrations/dmaker/airfresh/airfresh_t2017.py @@ -7,6 +7,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -115,71 +116,96 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["power"] else "off" @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """Return True if device is on.""" return self.data["power"] @property + @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:fan") def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["mode"]) @property + @sensor("PM2.5", unit="μg/m³", device_class="pm25", icon="mdi:blur") def pm25(self) -> int: """Fine particulate patter (PM2.5).""" return self.data["pm25"] @property + @sensor( + "CO2", + unit="ppm", + device_class="carbon_dioxide", + icon="mdi:molecule-co2", + ) def co2(self) -> int: """Carbon dioxide.""" return self.data["co2"] @property + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> int: """Current temperature in degree celsions.""" return self.data["temperature_outside"] @property + @setting( + "Favorite Speed", + setter_name="set_favorite_speed", + min_value=0, + max_value=150, + icon="mdi:fan", + ) def favorite_speed(self) -> int: """Favorite speed.""" return self.data["favourite_speed"] @property + @sensor("Control Speed", icon="mdi:fan") def control_speed(self) -> int: """Control speed.""" return self.data["control_speed"] @property + @sensor("Dust Filter Life Remaining", unit="%", icon="mdi:filter") def dust_filter_life_remaining(self) -> Optional[int]: """Remaining dust filter life in percent.""" return self.data.get("filter_intermediate", self.data.get("filter_rate")) @property + @sensor("Dust Filter Life Remaining Days", unit="days", icon="mdi:filter") def dust_filter_life_remaining_days(self) -> Optional[int]: """Remaining dust filter life in days.""" return self.data.get("filter_inter_day", self.data.get("filter_day")) @property + @sensor("Upper Filter Life Remaining", unit="%", icon="mdi:filter") def upper_filter_life_remaining(self) -> Optional[int]: """Remaining upper filter life in percent.""" return self.data.get("filter_efficient") @property + @sensor("Upper Filter Life Remaining Days", unit="days", icon="mdi:filter") def upper_filter_life_remaining_days(self) -> Optional[int]: """Remaining upper filter life in days.""" return self.data.get("filter_effi_day") @property + @setting("PTC", setter_name="set_ptc", icon="mdi:radiator") def ptc(self) -> bool: """Return True if PTC is on.""" return self.data["ptc_on"] @property + @setting("PTC Level", setter_name="set_ptc_level", choices=PtcLevel, icon="mdi:radiator") def ptc_level(self) -> Optional[PtcLevel]: """PTC level.""" try: @@ -188,26 +214,36 @@ def ptc_level(self) -> Optional[PtcLevel]: return None @property + @sensor("PTC Status", icon="mdi:radiator") def ptc_status(self) -> bool: """Return true if PTC status is on.""" return self.data["ptc_status"] @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Return True if child lock is on.""" return self.data["child_lock"] @property + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """Return True if sound is on.""" return self.data["sound"] @property + @setting("Display", setter_name="set_display", icon="mdi:monitor") def display(self) -> bool: """Return True if the display is on.""" return self.data["display"] @property + @setting( + "Display Orientation", + setter_name="set_display_orientation", + choices=DisplayOrientation, + icon="mdi:monitor", + ) def display_orientation(self) -> Optional[DisplayOrientation]: """Display orientation.""" try: diff --git a/miio/integrations/dmaker/fan/fan.py b/miio/integrations/dmaker/fan/fan.py index 443796ec9..b5e3f241a 100644 --- a/miio/integrations/dmaker/fan/fan.py +++ b/miio/integrations/dmaker/fan/fan.py @@ -5,6 +5,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting class MoveDirection(enum.Enum): @@ -49,51 +50,80 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["power"] else "off" @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is currently on.""" return self.data["power"] @property + @setting( + "Mode", + setter_name="set_mode", + choices=OperationMode, + icon="mdi:fan", + ) def mode(self) -> OperationMode: """Operation mode.""" return OperationMode(self.data["mode"]) @property + @setting( + "Speed", + unit="%", + setter_name="set_speed", + min_value=0, + max_value=100, + icon="mdi:speedometer", + ) def speed(self) -> int: """Speed of the motor.""" return self.data["speed"] @property + @setting("Oscillate", setter_name="set_oscillate", icon="mdi:sync") def oscillate(self) -> bool: """True if oscillation is enabled.""" return self.data["roll_enable"] @property + @setting( + "Angle", + unit="°", + setter_name="set_angle", + min_value=30, + max_value=140, + icon="mdi:angle-acute", + ) def angle(self) -> int: """Oscillation angle.""" return self.data["roll_angle"] @property + @sensor("Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration") def delay_off_countdown(self) -> int: """Countdown until turning off in seconds.""" return self.data["time_off"] @property + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """True if LED is turned on, if available.""" return self.data["light"] @property + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["beep_sound"] @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """True if child lock is on.""" return self.data["child_lock"] diff --git a/miio/integrations/dmaker/fan/fan_miot.py b/miio/integrations/dmaker/fan/fan_miot.py index 33a072ffa..b4d3d8502 100644 --- a/miio/integrations/dmaker/fan/fan_miot.py +++ b/miio/integrations/dmaker/fan/fan_miot.py @@ -5,6 +5,7 @@ from miio import DeviceStatus, MiotDevice from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting class OperationMode(enum.Enum): @@ -189,16 +190,19 @@ def __init__(self, data: dict[str, Any], model: str) -> None: self.model = model @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["power"] else "off" @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is currently on.""" return self.data["power"] @property + @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:fan") def mode(self) -> OperationMode: """Operation mode.""" if self.model == MODEL_FAN_P45: @@ -206,36 +210,50 @@ def mode(self) -> OperationMode: return OperationMode[OperationModeMiot(self.data["mode"]).name] @property + @setting("Speed", setter_name="set_speed", min_value=0, max_value=100, icon="mdi:fan") def speed(self) -> int: """Speed of the motor.""" return self.data["fan_speed"] @property + @setting("Oscillate", setter_name="set_oscillate", icon="mdi:arrow-oscillating") def oscillate(self) -> bool: """True if oscillation is enabled.""" return self.data["swing_mode"] @property + @setting("Angle", setter_name="set_angle", icon="mdi:angle-acute") def angle(self) -> int: """Oscillation angle.""" return self.data["swing_mode_angle"] @property + @setting( + "Delay Off Countdown", + setter_name="delay_off", + unit="min", + min_value=0, + max_value=480, + icon="mdi:timer", + ) def delay_off_countdown(self) -> int: """Countdown until turning off in minutes.""" return self.data["power_off_time"] @property + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """True if LED is turned on, if available.""" return self.data["light"] @property + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["buzzer"] @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """True if child lock is on.""" return self.data["child_lock"] @@ -265,46 +283,62 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["power"] else "off" @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is currently on.""" return self.data["power"] @property + @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:fan") def mode(self) -> OperationMode: """Operation mode.""" return OperationMode[OperationModeMiot(self.data["mode"]).name] @property + @setting("Speed", setter_name="set_speed", min_value=1, max_value=3, icon="mdi:fan") def speed(self) -> int: """Speed of the motor.""" return self.data["fan_level"] @property + @setting("Oscillate", setter_name="set_oscillate", icon="mdi:arrow-oscillating") def oscillate(self) -> bool: """True if oscillation is enabled.""" return self.data["swing_mode"] @property + @setting( + "Delay Off Countdown", + setter_name="delay_off", + unit="min", + min_value=0, + max_value=480, + icon="mdi:timer", + ) def delay_off_countdown(self) -> int: """Countdown until turning off in minutes.""" return self.data["power_off_time"] @property + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """True if LED is turned on.""" return self.data["light"] @property + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["buzzer"] @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """True if child lock is on.""" return self.data["child_lock"] diff --git a/miio/integrations/dreame/vacuum/dreamevacuum_miot.py b/miio/integrations/dreame/vacuum/dreamevacuum_miot.py index de51a55cf..07fe38153 100644 --- a/miio/integrations/dreame/vacuum/dreamevacuum_miot.py +++ b/miio/integrations/dreame/vacuum/dreamevacuum_miot.py @@ -8,6 +8,7 @@ import click from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting from miio.miot_device import DeviceStatus as DeviceStatusContainer from miio.miot_device import MiotDevice, MiotMapping from miio.updater import OneShotServer @@ -304,120 +305,145 @@ class DreameVacuumStatus(DeviceStatusContainer): def __init__(self, data, model): self.data = data self.model = model - @property + + @sensor("Battery Level", unit="%", device_class="battery", icon="mdi:battery") def battery_level(self) -> str: return self.data["battery_level"] - @property + + @sensor("Brush Left Time", unit="h", icon="mdi:brush") def brush_left_time(self) -> str: return self.data["brush_left_time"] - @property + + @sensor("Side Brush Left Time", unit="h", icon="mdi:brush") def brush_left_time2(self) -> str: return self.data["brush_left_time2"] - @property + + @sensor("Side Brush Life Level", unit="%", icon="mdi:brush") def brush_life_level2(self) -> str: return self.data["brush_life_level2"] - @property + + @sensor("Brush Life Level", unit="%", icon="mdi:brush") def brush_life_level(self) -> str: return self.data["brush_life_level"] - @property + + @sensor("Filter Left Time", unit="h", icon="mdi:filter-outline") def filter_left_time(self) -> str: return self.data["filter_left_time"] - @property + + @sensor("Filter Life Level", unit="%", icon="mdi:filter-outline") def filter_life_level(self) -> str: return self.data["filter_life_level"] - @property + + @sensor("Device Fault", icon="mdi:alert-circle") def device_fault(self) -> Optional[FaultStatus]: try: return FaultStatus(self.data["device_fault"]) except ValueError: _LOGGER.error("Unknown FaultStatus (%s)", self.data["device_fault"]) return None - @property + + @sensor("Charging State", icon="mdi:battery-charging") def charging_state(self) -> Optional[ChargingState]: try: return ChargingState(self.data["charging_state"]) except ValueError: _LOGGER.error("Unknown ChargingStats (%s)", self.data["charging_state"]) return None - @property + + @sensor("Operating Mode", icon="mdi:robot-vacuum") def operating_mode(self) -> Optional[OperatingMode]: try: return OperatingMode(self.data["operating_mode"]) except ValueError: _LOGGER.error("Unknown OperatingMode (%s)", self.data["operating_mode"]) return None - @property + + @sensor("Device Status", icon="mdi:robot-vacuum") def device_status(self) -> Optional[DeviceStatus]: try: return DeviceStatus(self.data["device_status"]) except TypeError: _LOGGER.error("Unknown DeviceStatus (%s)", self.data["device_status"]) return None - @property + + @sensor("Timer Enable", icon="mdi:timer") def timer_enable(self) -> str: return self.data["timer_enable"] - @property + + @sensor("Start Time", icon="mdi:clock-start") def start_time(self) -> str: return self.data["start_time"] - @property + + @sensor("Stop Time", icon="mdi:clock-end") def stop_time(self) -> str: return self.data["stop_time"] - @property + + @sensor("Map View", icon="mdi:map") def map_view(self) -> str: return self.data["map_view"] - @property + + @setting("Volume", setter_name="set_sound_volume", unit="%", min_value=0, max_value=100, step=1, icon="mdi:volume-high") def volume(self) -> str: return self.data["volume"] - @property + + @sensor("Voice Package", icon="mdi:account-voice") def voice_package(self) -> str: return self.data["voice_package"] - @property + + @sensor("Timezone", icon="mdi:earth") def timezone(self) -> str: return self.data["timezone"] - @property + + @sensor("Cleaning Time", unit="min", icon="mdi:timer-outline") def cleaning_time(self) -> str: return self.data["cleaning_time"] - @property + + @sensor("Cleaning Area", unit="m²", icon="mdi:texture-box") def cleaning_area(self) -> str: return self.data["cleaning_area"] - @property + + @sensor("First Clean Time", icon="mdi:clock-outline") def first_clean_time(self) -> str: return self.data["first_clean_time"] - @property + + @sensor("Total Clean Time", unit="min", icon="mdi:timer-outline") def total_clean_time(self) -> str: return self.data["total_clean_time"] - @property + + @sensor("Total Clean Times", icon="mdi:counter") def total_clean_times(self) -> str: return self.data["total_clean_times"] - @property + + @sensor("Total Clean Area", unit="m²", icon="mdi:texture-box") def total_clean_area(self) -> str: return self.data["total_clean_area"] - @property + + @setting("Cleaning Mode", setter_name="set_fan_speed", icon="mdi:fan") def cleaning_mode(self): cleaning_mode = self.data["cleaning_mode"] cleaning_mode_enum_class = _get_cleaning_mode_enum_class(self.model) @@ -430,21 +456,25 @@ def cleaning_mode(self): except ValueError: _LOGGER.error(f"Unknown CleaningMode ({cleaning_mode})") return None - @property + + @sensor("Life Sieve", unit="%", icon="mdi:filter-outline") def life_sieve(self) -> Optional[str]: return self.data.get("life_sieve") - @property + + @sensor("Life Brush Side", unit="%", icon="mdi:brush") def life_brush_side(self) -> Optional[str]: return self.data.get("life_brush_side") - @property + + @sensor("Life Brush Main", unit="%", icon="mdi:brush") def life_brush_main(self) -> Optional[str]: return self.data.get("life_brush_main") # TODO: get/set water flow for Dreame 1C @property + @setting("Water Flow", setter_name="set_waterflow", choices=WaterFlow, icon="mdi:water") def water_flow(self) -> Optional[WaterFlow]: try: water_flow = self.data["water_flow"] @@ -455,8 +485,9 @@ def water_flow(self) -> Optional[WaterFlow]: except ValueError: _LOGGER.error("Unknown WaterFlow (%s)", self.data["water_flow"]) return None - @property + + @sensor("Water Box Carriage Attached", icon="mdi:cup-water") def is_water_box_carriage_attached(self) -> Optional[bool]: """Return True if water box carriage (mop) is installed, None if sensor not present.""" diff --git a/miio/integrations/huayi/light/huizuo.py b/miio/integrations/huayi/light/huizuo.py index 60811e67c..a43902cc2 100644 --- a/miio/integrations/huayi/light/huizuo.py +++ b/miio/integrations/huayi/light/huizuo.py @@ -11,6 +11,7 @@ from miio import DeviceStatus, MiotDevice, UnsupportedFeatureException from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -117,21 +118,40 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """Return True if device is on.""" return self.data["power"] @property + @setting( + "Brightness", + unit="%", + setter_name="set_brightness", + min_value=0, + max_value=100, + icon="mdi:brightness-6", + ) def brightness(self) -> int: """Return current brightness.""" return self.data["brightness"] @property + @setting( + "Color Temperature", + unit="K", + setter_name="set_color_temp", + min_value=3000, + max_value=6400, + icon="mdi:thermometer", + device_class="temperature", + ) def color_temp(self) -> int: """Return current color temperature.""" return self.data["color_temp"] @property + @sensor("Fan On", icon="mdi:fan") def is_fan_on(self) -> Optional[bool]: """Return True if Fan is on.""" if "fan_power" in self.data: @@ -139,6 +159,14 @@ def is_fan_on(self) -> Optional[bool]: return None @property + @setting( + "Fan Speed Level", + unit="%", + setter_name="set_fan_level", + min_value=0, + max_value=100, + icon="mdi:fan", + ) def fan_speed_level(self) -> Optional[int]: """Return current Fan speed level.""" if "fan_level" in self.data: @@ -146,6 +174,7 @@ def fan_speed_level(self) -> Optional[int]: return None @property + @sensor("Fan Reverse", icon="mdi:fan-chevron-down") def is_fan_reverse(self) -> Optional[bool]: """Return True if Fan reverse is on.""" if "fan_motor_reverse" in self.data: @@ -153,6 +182,7 @@ def is_fan_reverse(self) -> Optional[bool]: return None @property + @sensor("Fan Mode", icon="mdi:fan-auto") def fan_mode(self) -> Optional[int]: """Return 0 if 'Basic' and 1 if 'Natural wind'.""" if "fan_mode" in self.data: @@ -160,6 +190,7 @@ def fan_mode(self) -> Optional[int]: return None @property + @sensor("Heater On", icon="mdi:radiator") def is_heater_on(self) -> Optional[bool]: """Return True if Heater is on.""" if "heater_power" in self.data: @@ -167,6 +198,7 @@ def is_heater_on(self) -> Optional[bool]: return None @property + @sensor("Heater Fault Code", icon="mdi:alert-circle") def heater_fault_code(self) -> Optional[int]: """Return Heater's fault code. @@ -177,6 +209,13 @@ def heater_fault_code(self) -> Optional[int]: return None @property + @setting( + "Heat Level", + setter_name="set_heat_level", + min_value=1, + max_value=3, + icon="mdi:radiator", + ) def heat_level(self) -> Optional[int]: """Return Heater's heat level.""" if "heat_level" in self.data: diff --git a/miio/integrations/ksmb/walkingpad/walkingpad.py b/miio/integrations/ksmb/walkingpad/walkingpad.py index 290791c69..99cb704fa 100644 --- a/miio/integrations/ksmb/walkingpad/walkingpad.py +++ b/miio/integrations/ksmb/walkingpad/walkingpad.py @@ -7,6 +7,7 @@ from miio import Device, DeviceException, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -43,51 +44,85 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if the device is turned on.""" return self.power == "on" @property + @sensor("Walking Time", icon="mdi:timer", device_class="duration") def walking_time(self) -> timedelta: """Current walking duration in seconds.""" return timedelta(seconds=int(self.data["time"])) @property + @setting( + "Speed", + unit="km/h", + setter_name="set_speed", + min_value=0, + max_value=6, + icon="mdi:speedometer", + ) def speed(self) -> float: """Current speed.""" return float(self.data["sp"]) @property + @setting( + "Start Speed", + unit="km/h", + setter_name="set_start_speed", + min_value=0, + max_value=6, + icon="mdi:speedometer-slow", + ) def start_speed(self) -> float: """Current start speed.""" return self.data["start_speed"] @property + @setting( + "Mode", + setter_name="set_mode", + choices=OperationMode, + icon="mdi:run", + ) def mode(self) -> OperationMode: """Current mode.""" return OperationMode(self.data["mode"]) @property + @setting( + "Sensitivity", + setter_name="set_sensitivity", + choices=OperationSensitivity, + icon="mdi:tune", + ) def sensitivity(self) -> OperationSensitivity: """Current sensitivity.""" return OperationSensitivity(self.data["sensitivity"]) @property + @sensor("Step Count", icon="mdi:shoe-print") def step_count(self) -> int: """Current steps.""" return int(self.data["step"]) @property + @sensor("Distance", unit="m", icon="mdi:map-marker-distance") def distance(self) -> int: """Current distance in meters.""" return int(self.data["dist"]) @property + @sensor("Calories", unit="cal", icon="mdi:fire") def calories(self) -> int: """Current calories burnt.""" return int(self.data["cal"]) diff --git a/miio/integrations/leshow/fan/fan_leshow.py b/miio/integrations/leshow/fan/fan_leshow.py index bcbece3fa..eb20e17f4 100644 --- a/miio/integrations/leshow/fan/fan_leshow.py +++ b/miio/integrations/leshow/fan/fan_leshow.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -45,41 +46,71 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["power"] == 1 else "off" @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is turned on.""" return self.data["power"] == 1 @property + @setting( + "Mode", + setter_name="set_mode", + icon="mdi:fan", + choices=OperationMode, + ) def mode(self) -> OperationMode: """Operation mode.""" return OperationMode(self.data["mode"]) @property + @setting( + "Speed", + setter_name="set_speed", + icon="mdi:speedometer", + unit="%", + min_value=0, + max_value=100, + step=1, + ) def speed(self) -> int: """Speed of the fan in percent.""" return self.data["blow"] @property + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["sound"] == 1 @property + @setting("Oscillate", setter_name="set_oscillate", icon="mdi:rotate-3d-variant") def oscillate(self) -> bool: """True if oscillation is enabled.""" return self.data["yaw"] == 1 @property + @setting( + "Delay Off Countdown", + setter_name="delay_off", + icon="mdi:timer", + device_class="duration", + unit="min", + min_value=0, + max_value=540, + step=1, + ) def delay_off_countdown(self) -> int: """Countdown until turning off in minutes.""" return self.data["timer"] @property + @sensor("Error Detected", icon="mdi:alert-circle") def error_detected(self) -> bool: """True if a fault was detected.""" return self.data["fault"] == 1 diff --git a/miio/integrations/lumi/acpartner/airconditioningcompanion.py b/miio/integrations/lumi/acpartner/airconditioningcompanion.py index 520363f23..946a98c19 100644 --- a/miio/integrations/lumi/acpartner/airconditioningcompanion.py +++ b/miio/integrations/lumi/acpartner/airconditioningcompanion.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor _LOGGER = logging.getLogger(__name__) @@ -98,11 +99,13 @@ def __init__(self, data): self.state = data["model_and_state"][1] @property + @sensor("Load Power", unit="W", device_class="power", icon="mdi:flash") def load_power(self) -> int: """Current power load of the air conditioner.""" return int(self.data["model_and_state"][2]) @property + @sensor("Power Socket", icon="mdi:power-socket") def power_socket(self) -> Optional[str]: """Current socket power state.""" if "power_socket" in self.data and self.data["power_socket"] is not None: @@ -111,21 +114,25 @@ def power_socket(self) -> Optional[str]: return None @property + @sensor("Air Condition Model", icon="mdi:air-conditioner") def air_condition_model(self) -> bytes: """Model of the air conditioner.""" return bytes.fromhex(self.model) @property + @sensor("Model Format", icon="mdi:information-outline") def model_format(self) -> int: """Version number of the model format.""" return self.air_condition_model[0] @property + @sensor("Device Type", icon="mdi:devices") def device_type(self) -> int: """Device type identifier.""" return self.air_condition_model[1] @property + @sensor("Air Condition Brand", icon="mdi:tag") def air_condition_brand(self) -> int: """Brand of the air conditioner. @@ -134,6 +141,7 @@ def air_condition_brand(self) -> int: return int(self.air_condition_model[2:4].hex(), 16) @property + @sensor("Air Condition Remote", icon="mdi:remote") def air_condition_remote(self) -> int: """Remote id. @@ -149,6 +157,7 @@ def air_condition_remote(self) -> int: return int(self.air_condition_model[4:8].hex(), 16) @property + @sensor("State Format", icon="mdi:information-outline") def state_format(self) -> int: """Version number of the state format. @@ -157,15 +166,18 @@ def state_format(self) -> int: return int(self.air_condition_model[8]) @property + @sensor("Air Condition Configuration", icon="mdi:cog") def air_condition_configuration(self) -> int: return self.state[2:10] @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Current power state.""" return "on" if int(self.state[2:3]) == Power.On.value else "off" @property + @sensor("LED", icon="mdi:led-on") def led(self) -> Optional[bool]: """Current LED state.""" state = self.state[8:9] @@ -179,11 +191,13 @@ def led(self) -> Optional[bool]: return None @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if the device is turned on.""" return self.power == "on" @property + @sensor("Target Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def target_temperature(self) -> Optional[int]: """Target temperature.""" try: @@ -192,6 +206,7 @@ def target_temperature(self) -> Optional[int]: return None @property + @sensor("Swing Mode", icon="mdi:arrow-oscillating") def swing_mode(self) -> Optional[SwingMode]: """Current swing mode.""" try: @@ -201,6 +216,7 @@ def swing_mode(self) -> Optional[SwingMode]: return None @property + @sensor("Fan Speed", icon="mdi:fan") def fan_speed(self) -> Optional[FanSpeed]: """Current fan speed.""" try: @@ -210,6 +226,7 @@ def fan_speed(self) -> Optional[FanSpeed]: return None @property + @sensor("Mode", icon="mdi:air-conditioner") def mode(self) -> Optional[OperationMode]: """Current operation mode.""" try: diff --git a/miio/integrations/lumi/acpartner/airconditioningcompanionMCN.py b/miio/integrations/lumi/acpartner/airconditioningcompanionMCN.py index d463502b2..f394da802 100644 --- a/miio/integrations/lumi/acpartner/airconditioningcompanionMCN.py +++ b/miio/integrations/lumi/acpartner/airconditioningcompanionMCN.py @@ -5,6 +5,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -44,21 +45,25 @@ def __init__(self, data): self.data = data @property + @sensor("Load Power", unit="W", icon="mdi:flash", device_class="power") def load_power(self) -> int: """Current power load of the air conditioner.""" return int(self.data[-1]) @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Current power state.""" return self.data[0] @property + @setting("Power", setter_name="on", icon="mdi:power") def is_on(self) -> bool: """True if the device is turned on.""" return self.power == "on" @property + @setting("Mode", setter_name="send_command", icon="mdi:air-conditioner", choices=OperationMode) def mode(self) -> Optional[OperationMode]: """Current operation mode.""" try: @@ -68,6 +73,7 @@ def mode(self) -> Optional[OperationMode]: return None @property + @setting("Target Temperature", setter_name="send_command", unit="°C", icon="mdi:thermometer", device_class="temperature") def target_temperature(self) -> Optional[int]: """Target temperature.""" try: @@ -76,6 +82,7 @@ def target_temperature(self) -> Optional[int]: return None @property + @setting("Fan Speed", setter_name="send_command", icon="mdi:fan", choices=FanSpeed) def fan_speed(self) -> Optional[FanSpeed]: """Current fan speed.""" try: @@ -85,6 +92,7 @@ def fan_speed(self) -> Optional[FanSpeed]: return None @property + @setting("Swing Mode", setter_name="send_command", icon="mdi:arrow-oscillating", choices=SwingMode) def swing_mode(self) -> Optional[SwingMode]: """Current swing mode.""" try: diff --git a/miio/integrations/lumi/camera/aqaracamera.py b/miio/integrations/lumi/camera/aqaracamera.py index 1a16723b7..d0cc1bdae 100644 --- a/miio/integrations/lumi/camera/aqaracamera.py +++ b/miio/integrations/lumi/camera/aqaracamera.py @@ -17,6 +17,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -75,46 +76,59 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Type", icon="mdi:camera") def type(self) -> str: """TODO: Type of the camera? Name?""" return self.data["app_type"] @property + @sensor("Video Status", icon="mdi:video") def video_status(self) -> bool: """Video state.""" return bool(self.data["video_state"]) @property + @sensor("Is On", icon="mdi:video") def is_on(self) -> bool: """True if device is currently on.""" return self.video_status == 1 @property + @setting("Motion Detection", setter_name="md_on", icon="mdi:motion-sensor") def md(self) -> bool: """Motion detection state.""" return bool(self.data["md_status"]) @property + @setting( + "Motion Detection Sensitivity", + setter_name="md_sensitivity", + icon="mdi:motion-sensor", + ) def md_sensitivity(self): """Motion detection sensitivity.""" return self.data["mdsensitivity"] @property + @setting("IR Mode", setter_name="ir_on", icon="mdi:remote") def ir(self): """IR mode.""" return bool(self.data["ir_status"]) @property + @setting("LED", setter_name="led_on", icon="mdi:led-on") def led(self): """LED status.""" return bool(self.data["led_status"]) @property + @setting("Flipped", setter_name="flip_on", icon="mdi:flip-vertical") def flipped(self) -> bool: """TODO: If camera is flipped?""" return self.data["flip_state"] @property + @sensor("Camera Offsets", icon="mdi:camera-control") def offsets(self) -> CameraOffset: """Camera offset information.""" return CameraOffset( @@ -124,26 +138,31 @@ def offsets(self) -> CameraOffset: ) @property + @sensor("Channel ID", icon="mdi:access-point-network") def channel_id(self) -> int: """TODO: Zigbee channel?""" return self.data["channel_id"] @property + @setting("Fullstop", setter_name="fullstop_on", icon="mdi:alarm-light") def fullstop(self) -> bool: """Is alarm triggered by MD.""" return self.data["fullstop"] != 0 @property + @sensor("P2P ID", icon="mdi:identifier") def p2p_id(self) -> str: """P2P ID for video and audio.""" return self.data["p2p_id"] @property + @sensor("AV ID", icon="mdi:identifier") def av_id(self) -> str: """TODO: What is this? ID for the cloud?""" return self.data["avID"] @property + @sensor("AV Password", icon="mdi:lock") def av_password(self) -> str: """TODO: What is this? Password for the cloud?""" return self.data["avPass"] diff --git a/miio/integrations/lumi/curtain/curtain_youpin.py b/miio/integrations/lumi/curtain/curtain_youpin.py index bad091f7d..db6b96ad3 100644 --- a/miio/integrations/lumi/curtain/curtain_youpin.py +++ b/miio/integrations/lumi/curtain/curtain_youpin.py @@ -6,6 +6,7 @@ from miio import DeviceStatus, MiotDevice from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -73,46 +74,75 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Status", icon="mdi:curtains") def status(self) -> Status: """Device status.""" return Status(self.data["status"]) @property + @setting("Manual Enabled", setter_name="set_manual_enabled", icon="mdi:hand-back-left") def is_manual_enabled(self) -> bool: """True if manual controls are enabled.""" return bool(self.data["is_manual_enabled"]) @property + @setting( + "Polarity", + setter_name="set_polarity", + icon="mdi:swap-horizontal", + choices=Polarity, + ) def polarity(self) -> Polarity: """Motor rotation polarity.""" return Polarity(self.data["polarity"]) @property + @setting("Position Limited", setter_name="set_position_limit", icon="mdi:arrow-collapse-horizontal") def is_position_limited(self) -> bool: """Position limit.""" return bool(self.data["is_position_limited"]) @property + @setting("Night Tip Light", setter_name="set_night_tip_light", icon="mdi:lightbulb-night") def night_tip_light(self) -> bool: """Night tip light status.""" return bool(self.data["night_tip_light"]) @property + @sensor("Run Time", icon="mdi:timer-outline", device_class="duration", unit="s") def run_time(self) -> int: """Run time of the motor.""" return self.data["run_time"] @property + @sensor("Current Position", icon="mdi:curtains", unit="%") def current_position(self) -> int: """Current curtain position.""" return self.data["current_position"] @property + @setting( + "Target Position", + setter_name="set_target_position", + icon="mdi:curtains", + unit="%", + min_value=0, + max_value=100, + step=1, + ) def target_position(self) -> int: """Target curtain position.""" return self.data["target_position"] @property + @setting( + "Adjust Value", + setter_name="set_adjust_value", + icon="mdi:tune", + min_value=-100, + max_value=100, + step=1, + ) def adjust_value(self) -> int: """Adjust value.""" return self.data["adjust_value"] diff --git a/miio/integrations/mijia/vacuum/g1vacuum.py b/miio/integrations/mijia/vacuum/g1vacuum.py index 0e6cc6c56..6d5233236 100644 --- a/miio/integrations/mijia/vacuum/g1vacuum.py +++ b/miio/integrations/mijia/vacuum/g1vacuum.py @@ -5,6 +5,7 @@ import click from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting from miio.miot_device import DeviceStatus, MiotDevice _LOGGER = logging.getLogger(__name__) @@ -157,91 +158,108 @@ def __init__(self, data): ] """ self.data = data - @property + + @sensor("Battery", unit="%", device_class="battery", icon="mdi:battery") def battery(self) -> int: """Battery Level.""" return self.data["battery"] - @property + + @sensor("Charge State", icon="mdi:battery-charging") def charge_state(self) -> G1ChargeState: """Charging State.""" return G1ChargeState(self.data["charge_state"]) - @property + + @sensor("Error Code", icon="mdi:alert-circle") def error_code(self) -> int: """Error code as returned by the device.""" return int(self.data["error_code"]) - @property + + @sensor("Error", icon="mdi:alert-circle") def error(self) -> str: """Human readable error description, see also :func:`error_code`.""" try: return ERROR_CODES[self.error_code] except KeyError: return "Definition missing for error %s" % self.error_code - @property + + @sensor("State", icon="mdi:robot-vacuum") def state(self) -> G1State: """Vacuum Status.""" return G1State(self.data["state"]) - @property + + @setting("Fan Speed", setter_name="set_fan_speed", choices=G1FanSpeed, icon="mdi:fan") def fan_speed(self) -> G1FanSpeed: """Fan Speed.""" return G1FanSpeed(self.data["fan_speed"]) - @property + + @sensor("Operating Mode", icon="mdi:robot-vacuum") def operating_mode(self) -> G1VacuumMode: """Operating Mode.""" return G1VacuumMode(self.data["operating_mode"]) - @property + + @sensor("Mop State", icon="mdi:robot-vacuum-variant") def mop_state(self) -> G1MopState: """Mop State.""" return G1MopState(self.data["mop_state"]) - @property + + @sensor("Water Level", icon="mdi:water") def water_level(self) -> G1WaterLevel: """Water Level.""" return G1WaterLevel(self.data["water_level"]) - @property + + @sensor("Main Brush Life Level", unit="%", icon="mdi:brush") def main_brush_life_level(self) -> int: """Main Brush Life Level in %.""" return self.data["main_brush_life_level"] - @property + + @sensor("Main Brush Time Left", icon="mdi:brush") def main_brush_time_left(self) -> timedelta: """Main Brush Remaining Time in Minutes.""" return timedelta(minutes=self.data["main_brush_time_left"]) - @property + + @sensor("Side Brush Life Level", unit="%", icon="mdi:brush") def side_brush_life_level(self) -> int: """Side Brush Life Level in %.""" return self.data["side_brush_life_level"] - @property + + @sensor("Side Brush Time Left", icon="mdi:brush") def side_brush_time_left(self) -> timedelta: """Side Brush Remaining Time in Minutes.""" return timedelta(minutes=self.data["side_brush_time_left"]) - @property + + @sensor("Filter Life Level", unit="%", icon="mdi:filter-outline") def filter_life_level(self) -> int: """Filter Life Level in %.""" return self.data["filter_life_level"] - @property + + @sensor("Filter Time Left", icon="mdi:filter-outline") def filter_time_left(self) -> timedelta: """Filter remaining time.""" return timedelta(minutes=self.data["filter_time_left"]) - @property + + @sensor("Clean Area", unit="cm²", icon="mdi:texture-box") def clean_area(self) -> int: """Clean Area in cm2.""" return self.data["clean_area"] - @property + + @sensor("Clean Time", icon="mdi:timer-outline") def clean_time(self) -> timedelta: """Clean time.""" return timedelta(minutes=self.data["clean_time"]) @@ -261,18 +279,21 @@ class G1CleaningSummary(DeviceStatus): def __init__(self, data) -> None: self.data = data - @property + + @sensor("Total Clean Count", icon="mdi:counter") def total_clean_count(self) -> int: """Total Number of Cleanings.""" return self.data["total_clean_count"] - @property + + @sensor("Total Clean Area", unit="m²", icon="mdi:texture-box") def total_clean_area(self) -> int: """Total Area Cleaned in m2.""" return self.data["total_clean_area"] - @property + + @sensor("Total Clean Time", icon="mdi:timer-outline") def total_clean_time(self) -> timedelta: """Total Cleaning Time.""" return timedelta(hours=self.data["total_clean_area"]) diff --git a/miio/integrations/mmgg/petwaterdispenser/status.py b/miio/integrations/mmgg/petwaterdispenser/status.py index 2704bc281..29c092b9f 100644 --- a/miio/integrations/mmgg/petwaterdispenser/status.py +++ b/miio/integrations/mmgg/petwaterdispenser/status.py @@ -2,6 +2,7 @@ from datetime import timedelta from typing import Any +from miio.devicestatus import sensor, setting from miio.miot_device import DeviceStatus @@ -34,36 +35,48 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor(name="Sponge Filter Left Days", icon="mdi:filter") def sponge_filter_left_days(self) -> timedelta: """Filter life time remaining in days.""" return timedelta(days=self.data["filter_left_time"]) @property + @setting(name="Power", setter_name="on", icon="mdi:power") def is_on(self) -> bool: """True if device is on.""" return self.data["on"] @property + @setting( + name="Mode", + setter_name="set_mode", + icon="mdi:water-pump", + choices=OperatingMode, + ) def mode(self) -> OperatingMode: """OperatingMode.""" return OperatingMode(self.data["mode"]) @property + @setting(name="LED", setter_name="set_led", icon="mdi:led-outline") def is_led_on(self) -> bool: """True if enabled.""" return self.data["indicator_light"] @property + @sensor(name="Cotton Left Days", icon="mdi:filter") def cotton_left_days(self) -> timedelta: """Cotton filter life time remaining in days.""" return timedelta(days=self.data["cotton_left_time"]) @property + @sensor(name="Before Cleaning Days", icon="mdi:broom") def before_cleaning_days(self) -> timedelta: """Days before cleaning.""" return timedelta(days=self.data["remain_clean_time"]) @property + @sensor(name="No Water", icon="mdi:water-off") def is_no_water(self) -> bool: """True if there is no water left.""" if self.data["no_water_flag"]: @@ -71,31 +84,43 @@ def is_no_water(self) -> bool: return True @property + @sensor(name="No Water Duration", icon="mdi:water-off") def no_water_minutes(self) -> timedelta: """Minutes without water.""" return timedelta(minutes=self.data["no_water_time"]) @property + @sensor(name="Pump Blocked", icon="mdi:water-pump-off") def is_pump_blocked(self) -> bool: """True if pump is blocked.""" return self.data["pump_block_flag"] @property + @sensor(name="Lid Up", icon="mdi:archive-arrow-up") def is_lid_up(self) -> bool: """True if lid is up.""" return self.data["lid_up_flag"] @property + @setting( + name="Timezone", + setter_name="set_timezone", + icon="mdi:map-clock", + min_value=-12, + max_value=12, + ) def timezone(self) -> int: """Timezone from -12 to +12.""" return self.data["timezone"] @property + @setting(name="Location", setter_name="set_location", icon="mdi:map-marker") def location(self) -> str: """Device location string.""" return self.data["location"] @property + @sensor(name="Error Detected", icon="mdi:alert-circle") def is_error_detected(self) -> bool: """True if fault detected.""" return self.data["fault"] > 0 diff --git a/miio/integrations/nwt/dehumidifier/airdehumidifier.py b/miio/integrations/nwt/dehumidifier/airdehumidifier.py index d9db0e0ff..6a3412265 100644 --- a/miio/integrations/nwt/dehumidifier/airdehumidifier.py +++ b/miio/integrations/nwt/dehumidifier/airdehumidifier.py @@ -7,6 +7,7 @@ from miio import Device, DeviceError, DeviceException, DeviceInfo, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -63,16 +64,24 @@ def __init__(self, data: dict[str, Any], device_info: DeviceInfo) -> None: self.device_info = device_info @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["on_off"] @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is turned on.""" return self.power == "on" @property + @setting( + "Mode", + setter_name="set_mode", + choices=OperationMode, + icon="mdi:air-humidifier", + ) def mode(self) -> OperationMode: """Operation mode. @@ -81,6 +90,7 @@ def mode(self) -> OperationMode: return OperationMode(self.data["mode"]) @property + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> Optional[float]: """Current temperature, if available.""" if "temp" in self.data and self.data["temp"] is not None: @@ -88,26 +98,36 @@ def temperature(self) -> Optional[float]: return None @property + @sensor("Humidity", unit="%", device_class="humidity", icon="mdi:water-percent") def humidity(self) -> int: """Current humidity.""" return self.data["humidity"] @property + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["buzzer"] == "on" @property + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """LED brightness if available.""" return self.data["led"] == "on" @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Return True if child lock is on.""" return self.data["child_lock"] == "on" @property + @setting( + "Target Humidity", + setter_name="set_target_humidity", + unit="%", + icon="mdi:water-percent", + ) def target_humidity(self) -> Optional[int]: """Target humiditiy. @@ -118,6 +138,12 @@ def target_humidity(self) -> Optional[int]: return None @property + @setting( + "Fan Speed", + setter_name="set_fan_speed", + choices=FanSpeed, + icon="mdi:fan", + ) def fan_speed(self) -> Optional[FanSpeed]: """Current fan speed.""" if "fan_speed" in self.data and self.data["fan_speed"] is not None: @@ -125,26 +151,31 @@ def fan_speed(self) -> Optional[FanSpeed]: return None @property + @sensor("Tank Full", icon="mdi:cup-water") def tank_full(self) -> bool: """The remaining amount of water in percent.""" return self.data["tank_full"] == "on" @property + @sensor("Compressor Status", icon="mdi:air-conditioner") def compressor_status(self) -> bool: """Compressor status.""" return self.data["compressor_status"] == "on" @property + @sensor("Defrost Status", icon="mdi:snowflake-melt") def defrost_status(self) -> bool: """Defrost status.""" return self.data["defrost_status"] == "on" @property + @sensor("Fan St", icon="mdi:fan") def fan_st(self) -> int: """Fan st.""" return self.data["fan_st"] @property + @sensor("Alarm", icon="mdi:alarm-light") def alarm(self) -> str: """Alarm.""" return self.data["alarm"] diff --git a/miio/integrations/philips/light/ceil.py b/miio/integrations/philips/light/ceil.py index ed401dab7..6744d8dc0 100644 --- a/miio/integrations/philips/light/ceil.py +++ b/miio/integrations/philips/light/ceil.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -25,41 +26,77 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if the device is turned on.""" return self.power == "on" @property + @setting( + "Brightness", + setter_name="set_brightness", + icon="mdi:brightness-6", + unit="%", + min_value=1, + max_value=100, + step=1, + ) def brightness(self) -> int: """Current brightness.""" return self.data["bright"] @property + @setting( + "Scene", + setter_name="set_scene", + icon="mdi:palette", + min_value=1, + max_value=4, + step=1, + ) def scene(self) -> int: """Current fixed scene (brightness & colortemp).""" return self.data["snm"] @property + @setting( + "Delay Off Countdown", + setter_name="delay_off", + icon="mdi:timer", + device_class="duration", + unit="s", + ) def delay_off_countdown(self) -> int: """Countdown until turning off in seconds.""" return self.data["dv"] @property + @setting( + "Color Temperature", + setter_name="set_color_temperature", + icon="mdi:temperature-kelvin", + min_value=1, + max_value=100, + step=1, + ) def color_temperature(self) -> int: """Current color temperature.""" return self.data["cct"] @property + @sensor("Smart Night Light", icon="mdi:weather-night") def smart_night_light(self) -> bool: """Smart night mode state.""" return self.data["bl"] == 1 @property + @sensor("Automatic Color Temperature", icon="mdi:theme-light-dark") def automatic_color_temperature(self) -> bool: """Automatic color temperature state.""" return self.data["ac"] == 1 diff --git a/miio/integrations/philips/light/philips_bulb.py b/miio/integrations/philips/light/philips_bulb.py index 66a38f12b..d305dc227 100644 --- a/miio/integrations/philips/light/philips_bulb.py +++ b/miio/integrations/philips/light/philips_bulb.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -37,14 +38,17 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: return self.data["power"] @property + @setting("Power", setter_name="on", icon="mdi:power") def is_on(self) -> bool: return self.power == "on" @property + @setting("Brightness", setter_name="set_brightness", unit="%", icon="mdi:brightness-6") def brightness(self) -> Optional[int]: if "bright" in self.data: return self.data["bright"] @@ -53,18 +57,21 @@ def brightness(self) -> Optional[int]: return None @property + @setting("Color Temperature", setter_name="set_color_temperature", icon="mdi:palette") def color_temperature(self) -> Optional[int]: if "cct" in self.data: return self.data["cct"] return None @property + @setting("Scene", setter_name="set_scene", icon="mdi:palette") def scene(self) -> Optional[int]: if "snm" in self.data: return self.data["snm"] return None @property + @sensor("Delay Off Countdown", unit="s", icon="mdi:timer-outline", device_class="duration") def delay_off_countdown(self) -> int: return self.data["dv"] diff --git a/miio/integrations/philips/light/philips_eyecare.py b/miio/integrations/philips/light/philips_eyecare.py index 1d34ca0e8..f29faf680 100644 --- a/miio/integrations/philips/light/philips_eyecare.py +++ b/miio/integrations/philips/light/philips_eyecare.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -20,51 +21,81 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if the device is turned on.""" return self.power == "on" @property + @setting( + "Brightness", + unit="%", + setter_name="set_brightness", + min_value=1, + max_value=100, + icon="mdi:brightness-6", + ) def brightness(self) -> int: """Current brightness of the primary light.""" return self.data["bright"] @property + @setting("Eye Fatigue Reminder", setter_name="reminder_on", icon="mdi:eye") def reminder(self) -> bool: """Indicates the eye fatigue notification is enabled or not.""" return self.data["notifystatus"] == "on" @property + @setting("Ambient Light", setter_name="ambient_on", icon="mdi:lightbulb-outline") def ambient(self) -> bool: """True if the ambient light (second light source) is on.""" return self.data["ambstatus"] == "on" @property + @setting( + "Ambient Brightness", + unit="%", + setter_name="set_ambient_brightness", + min_value=1, + max_value=100, + icon="mdi:brightness-6", + ) def ambient_brightness(self) -> int: """Brightness of the ambient light.""" return self.data["ambvalue"] @property + @setting("Eyecare Mode", setter_name="eyecare_on", icon="mdi:eye-check") def eyecare(self) -> bool: """True if the eyecare mode is on.""" return self.data["eyecare"] == "on" @property + @setting( + "Scene", + setter_name="set_scene", + min_value=1, + max_value=4, + icon="mdi:palette", + ) def scene(self) -> int: """Current fixed scene.""" return self.data["scene_num"] @property + @setting("Smart Night Light", setter_name="smart_night_light_on", icon="mdi:weather-night") def smart_night_light(self) -> bool: """True if the smart night light mode is on.""" return self.data["bls"] == "on" @property + @sensor("Delay Off Countdown", unit="min", icon="mdi:timer-sand", device_class="duration") def delay_off_countdown(self) -> int: """Countdown until turning off in minutes.""" return self.data["dvalue"] diff --git a/miio/integrations/philips/light/philips_moonlight.py b/miio/integrations/philips/light/philips_moonlight.py index d5e90bfcf..8d724cf00 100644 --- a/miio/integrations/philips/light/philips_moonlight.py +++ b/miio/integrations/philips/light/philips_moonlight.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor, setting from miio.utils import int_to_rgb _LOGGER = logging.getLogger(__name__) @@ -23,31 +24,57 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor(name="Power", icon="mdi:power") def power(self) -> str: return self.data["pow"] @property + @setting(name="Power", setter_name="on", icon="mdi:power") def is_on(self) -> bool: return self.power == "on" @property + @setting( + name="Brightness", + setter_name="set_brightness", + unit="%", + icon="mdi:brightness-6", + min_value=1, + max_value=100, + ) def brightness(self) -> int: return self.data["bri"] @property + @setting( + name="Color Temperature", + setter_name="set_color_temperature", + icon="mdi:thermometer", + min_value=1, + max_value=100, + ) def color_temperature(self) -> int: return self.data["cct"] @property + @setting(name="RGB", setter_name="set_rgb", icon="mdi:palette") def rgb(self) -> tuple[int, int, int]: """Return color in RGB.""" return int_to_rgb(int(self.data["rgb"])) @property + @setting( + name="Scene", + setter_name="set_scene", + icon="mdi:palette-swatch", + min_value=1, + max_value=6, + ) def scene(self) -> int: return self.data["snm"] @property + @sensor(name="Sleep Assistant", icon="mdi:sleep") def sleep_assistant(self) -> int: """Example values: @@ -59,24 +86,29 @@ def sleep_assistant(self) -> int: return self.data["sta"] @property + @sensor(name="Sleep Off Time", unit="s", icon="mdi:timer-off") def sleep_off_time(self) -> int: return self.data["spr"] @property + @sensor(name="Total Assistant Sleep Time", unit="s", icon="mdi:timer") def total_assistant_sleep_time(self) -> int: return self.data["spt"] @property + @sensor(name="Brand Sleep", icon="mdi:sleep") def brand_sleep(self) -> bool: # sp_sleep_open? return self.data["ms"] == 1 @property + @sensor(name="Brand", icon="mdi:watch") def brand(self) -> bool: # sp_xm_bracelet? return self.data["mb"] == 1 @property + @sensor(name="Wake Up Time", icon="mdi:alarm") def wake_up_time(self) -> list[int]: # Example: [weekdays?, hour, minute] return self.data["wkp"] diff --git a/miio/integrations/philips/light/philips_rwread.py b/miio/integrations/philips/light/philips_rwread.py index 5d3f5d4ca..b0dc5091a 100644 --- a/miio/integrations/philips/light/philips_rwread.py +++ b/miio/integrations/philips/light/philips_rwread.py @@ -7,6 +7,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -35,41 +36,75 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if the device is turned on.""" return self.power == "on" @property + @setting( + "Brightness", + setter_name="set_brightness", + icon="mdi:brightness-6", + unit="%", + min_value=1, + max_value=100, + step=1, + ) def brightness(self) -> int: """Current brightness.""" return self.data["bright"] @property + @setting( + "Delay Off Countdown", + setter_name="delay_off", + icon="mdi:timer", + device_class="duration", + unit="s", + ) def delay_off_countdown(self) -> int: """Countdown until turning off in seconds.""" return self.data["dv"] @property + @setting( + "Scene", + setter_name="set_scene", + icon="mdi:palette", + min_value=1, + max_value=4, + step=1, + ) def scene(self) -> int: """Current fixed scene.""" return self.data["snm"] @property + @setting("Motion Detection", setter_name="set_motion_detection", icon="mdi:motion-sensor") def motion_detection(self) -> bool: """True if motion detection is enabled.""" return self.data["flm"] == 1 @property + @setting( + "Motion Detection Sensitivity", + setter_name="set_motion_detection_sensitivity", + icon="mdi:signal-cellular-2", + choices=MotionDetectionSensitivity, + ) def motion_detection_sensitivity(self) -> MotionDetectionSensitivity: """The sensitivity of the motion detection.""" return MotionDetectionSensitivity(self.data["flmv"]) @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """True if child lock is enabled.""" return self.data["chl"] == 1 diff --git a/miio/integrations/pwzn/relay/pwzn_relay.py b/miio/integrations/pwzn/relay/pwzn_relay.py index 889cf3556..47b7e28da 100644 --- a/miio/integrations/pwzn/relay/pwzn_relay.py +++ b/miio/integrations/pwzn/relay/pwzn_relay.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor _LOGGER = logging.getLogger(__name__) @@ -70,6 +71,7 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Relay State", icon="mdi:electric-switch") def relay_state(self) -> Optional[int]: """Current relay state.""" if "relay_status" in self.data: @@ -77,6 +79,7 @@ def relay_state(self) -> Optional[int]: return None @property + @sensor("Relay Names", icon="mdi:rename-box") def relay_names(self) -> dict[int, str]: def _extract_index_from_key(name) -> int: """extract the index from the variable.""" @@ -89,6 +92,7 @@ def _extract_index_from_key(name) -> int: } @property + @sensor("Active Relay Count", icon="mdi:counter") def on_count(self) -> Optional[int]: """Number of on relay.""" if "on_count" in self.data: diff --git a/miio/integrations/roidmi/vacuum/roidmivacuum_miot.py b/miio/integrations/roidmi/vacuum/roidmivacuum_miot.py index b99f0bf5c..301a149b8 100644 --- a/miio/integrations/roidmi/vacuum/roidmivacuum_miot.py +++ b/miio/integrations/roidmi/vacuum/roidmivacuum_miot.py @@ -9,6 +9,7 @@ import click from miio.click_common import EnumType, command +from miio.devicestatus import sensor, setting from miio.integrations.roborock.vacuum.vacuumcontainers import ( # TODO: remove roborock import DNDStatus, ) @@ -222,26 +223,30 @@ def __init__(self, data): ] """ self.data = data - @property + + @sensor("Battery", unit="%", device_class="battery", icon="mdi:battery") def battery(self) -> int: """Remaining battery in percentage.""" return self.data["battery_level"] - @property + + @sensor("Error Code", icon="mdi:alert-circle") def error_code(self) -> int: """Error code as returned by the device.""" return int(self.data["error_code"]) - @property + + @sensor("Error", icon="mdi:alert-circle") def error(self) -> str: """Human readable error description, see also :func:`error_code`.""" try: return error_codes[self.error_code] except KeyError: return "Definition missing for error %s" % self.error_code - @property + + @sensor("Charging State", icon="mdi:battery-charging") def charging_state(self) -> ChargingState: """Charging state (Charging/Discharging)""" try: @@ -249,8 +254,9 @@ def charging_state(self) -> ChargingState: except ValueError: _LOGGER.error("Unknown ChargingStats (%s)", self.data["charging_state"]) return ChargingState.Unknown - @property + + @sensor("Sweep Mode", icon="mdi:robot-vacuum") def sweep_mode(self) -> SweepMode: """Sweep mode point/area/total etc.""" try: @@ -258,8 +264,9 @@ def sweep_mode(self) -> SweepMode: except ValueError: _LOGGER.error("Unknown SweepMode (%s)", self.data["sweep_mode"]) return SweepMode.Unknown - @property + + @setting("Fan Speed", setter_name="set_fanspeed", choices=FanSpeed, icon="mdi:fan") def fan_speed(self) -> FanSpeed: """Current fan speed.""" try: @@ -267,8 +274,9 @@ def fan_speed(self) -> FanSpeed: except ValueError: _LOGGER.error("Unknown FanSpeed (%s)", self.data["fanspeed_mode"]) return FanSpeed.Unknown - @property + + @setting("Sweep Type", setter_name="set_sweep_type", choices=SweepType, icon="mdi:robot-vacuum") def sweep_type(self) -> SweepType: """Current sweep type sweep/mop/sweep&mop.""" try: @@ -276,8 +284,9 @@ def sweep_type(self) -> SweepType: except ValueError: _LOGGER.error("Unknown SweepType (%s)", self.data["sweep_type"]) return SweepType.Unknown - @property + + @setting("Path Mode", setter_name="set_path_mode", choices=PathMode, icon="mdi:map-marker-path") def path_mode(self) -> PathMode: """Current path-mode: normal/y-mopping etc.""" try: @@ -285,21 +294,24 @@ def path_mode(self) -> PathMode: except ValueError: _LOGGER.error("Unknown PathMode (%s)", self.data["path_mode"]) return PathMode.Unknown - @property + + @sensor("Mop Attached", icon="mdi:robot-vacuum-variant") def is_mop_attached(self) -> bool: """Return True if mop is attached.""" return self.data["mop_present"] - @property + + @setting("Dust Collection Frequency", setter_name="set_dust_collection_frequency", min_value=0, max_value=3, step=1, icon="mdi:delete-variant") def dust_collection_frequency(self) -> int: """Frequency for emptying the dust bin. Example: 2 means the dust bin is emptied every second cleaning. """ return self.data["work_station_freq"] - @property + + @setting("Timing", setter_name="set_timing", icon="mdi:clock-outline") def timing(self) -> str: """Repeated cleaning. @@ -335,8 +347,9 @@ def timing(self) -> str: tz/tzs= time-zone """ return self.data["timing"] - @property + + @setting("Carpet Mode", setter_name="set_carpet_mode", icon="mdi:rug") def carpet_mode(self) -> bool: """Auto boost on carpet.""" return self.data["auto_boost"] @@ -361,13 +374,15 @@ def _seconds_to_components(val): end_minute=end[1], ) ) - @property + + @sensor("DND Status", icon="mdi:minus-circle") def dnd_status(self) -> DNDStatus: """Returns do-not-disturb status.""" return self._parse_forbid_mode(self.data["forbid_mode"]) - @property + + @setting("Water Level", setter_name="set_water_level", choices=WaterLevel, icon="mdi:water") def water_level(self) -> WaterLevel: """Get current water level.""" try: @@ -375,57 +390,67 @@ def water_level(self) -> WaterLevel: except ValueError: _LOGGER.error("Unknown WaterLevel (%s)", self.data["water_level"]) return WaterLevel.Unknown - @property + + @setting("Double Clean", setter_name="set_double_clean", icon="mdi:refresh") def double_clean(self) -> bool: """Is double clean enabled.""" return self.data["double_clean"] - @property + + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """Return True if led/display on vaccum is on.""" return self.data["led_switch"] - @property + + @setting("Lidar Collision Sensor", setter_name="set_lidar_collision_sensor", icon="mdi:radar") def is_lidar_collision_sensor(self) -> bool: """When ON, the robot will use lidar as the main detection sensor to help reduce collisions.""" return self.data["lidar_collision"] - @property + + @sensor("Station Key", icon="mdi:gesture-tap-button") def station_key(self) -> bool: """When ON: long press the display will turn on dust collection.""" return self.data["station_key"] - @property + + @setting("Station LED", setter_name="set_station_led", icon="mdi:led-on") def station_led(self) -> bool: """Return if station display is on.""" return self.data["station_led"] - @property + + @sensor("Current Audio", icon="mdi:account-voice") def current_audio(self) -> str: """Current voice setting. E.g. 'girl_en' """ return self.data["current_audio"] - @property + + @sensor("Clean Time", icon="mdi:timer-outline") def clean_time(self) -> timedelta: """Time used for cleaning (if finished, shows how long it took).""" return timedelta(seconds=self.data["clean_time_sec"]) - @property + + @sensor("Clean Area", unit="m²", icon="mdi:texture-box") def clean_area(self) -> int: """Cleaned area in m2.""" return self.data["clean_area"] - @property + + @sensor("State Code", icon="mdi:robot-vacuum") def state_code(self) -> int: """State code as returned by the device.""" return int(self.data["state"]) - @property + + @sensor("State", icon="mdi:robot-vacuum") def state(self) -> RoidmiState: """Human readable state description, see also :func:`state_code`.""" try: @@ -433,28 +458,33 @@ def state(self) -> RoidmiState: except ValueError: _LOGGER.error("Unknown RoidmiState (%s)", self.state_code) return RoidmiState.Unknown - @property + + @setting("Volume", setter_name="set_sound_volume", unit="%", min_value=0, max_value=100, step=1, icon="mdi:volume-high") def volume(self) -> int: """Return device sound volumen level.""" return self.data["volume"] - @property + + @setting("Muted", setter_name="set_sound_muted", icon="mdi:volume-off") def is_muted(self) -> bool: """True if device is muted.""" return bool(self.data["mute"]) - @property + + @sensor("Is Paused", icon="mdi:pause-circle") def is_paused(self) -> bool: """Return True if vacuum is paused.""" return self.state in [RoidmiState.Paused, RoidmiState.FindChargerPause] - @property + + @sensor("Is On", icon="mdi:robot-vacuum") def is_on(self) -> bool: """True if device is currently cleaning in any mode.""" return self.state == RoidmiState.Sweeping - @property + + @sensor("Got Error", icon="mdi:alert-circle") def got_error(self) -> bool: """True if an error has occurred.""" return self.error_code != 0 @@ -465,18 +495,21 @@ class RoidmiCleaningSummary(DeviceStatus): def __init__(self, data) -> None: self.data = data - @property + + @sensor("Total Duration", icon="mdi:timer-outline") def total_duration(self) -> timedelta: """Total cleaning duration.""" return timedelta(seconds=self.data["total_clean_time_sec"]) - @property + + @sensor("Total Area", unit="m²", icon="mdi:texture-box") def total_area(self) -> int: """Total cleaned area.""" return self.data["total_clean_areas"] - @property + + @sensor("Count", icon="mdi:counter") def count(self) -> int: """Number of cleaning runs.""" return self.data["clean_counts"] @@ -498,49 +531,57 @@ def _calcUsageTime( remaning_fraction = remaning_level / 100.0 original_total = renaning_time / remaning_fraction return original_total * (1 - remaning_fraction) - @property + + @sensor("Filter Usage", icon="mdi:filter-outline") def filter(self) -> timedelta: """Filter usage time.""" return self._calcUsageTime(self.filter_left, self.data["filter_life_level"]) - @property + + @sensor("Filter Left", icon="mdi:filter-outline") def filter_left(self) -> timedelta: """How long until the filter should be changed.""" return timedelta(minutes=self.data["filter_left_minutes"]) - @property + + @sensor("Main Brush Usage", icon="mdi:brush") def main_brush(self) -> timedelta: """Main brush usage time.""" return self._calcUsageTime( self.main_brush_left, self.data["main_brush_life_level"] ) - @property + + @sensor("Main Brush Left", icon="mdi:brush") def main_brush_left(self) -> timedelta: """How long until the main brush should be changed.""" return timedelta(minutes=self.data["main_brush_left_minutes"]) - @property + + @sensor("Side Brush Usage", icon="mdi:brush") def side_brush(self) -> timedelta: """Main brush usage time.""" return self._calcUsageTime( self.side_brush_left, self.data["side_brushes_life_level"] ) - @property + + @sensor("Side Brush Left", icon="mdi:brush") def side_brush_left(self) -> timedelta: """How long until the side brushes should be changed.""" return timedelta(minutes=self.data["side_brushes_left_minutes"]) - @property + + @sensor("Sensor Dirty", icon="mdi:eye-off") def sensor_dirty(self) -> timedelta: """Return time since last sensor clean.""" return self._calcUsageTime( self.sensor_dirty_left, self.data["sensor_dirty_remaning_level"] ) - @property + + @sensor("Sensor Dirty Left", icon="mdi:eye-off") def sensor_dirty_left(self) -> timedelta: """How long until the sensors should be cleaned.""" return timedelta(minutes=self.data["sensor_dirty_time_left_minutes"]) diff --git a/miio/integrations/shuii/humidifier/airhumidifier_jsq.py b/miio/integrations/shuii/humidifier/airhumidifier_jsq.py index 80ccb5a21..6a4cdd0d0 100644 --- a/miio/integrations/shuii/humidifier/airhumidifier_jsq.py +++ b/miio/integrations/shuii/humidifier/airhumidifier_jsq.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -59,16 +60,24 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor(name="Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["power"] == 1 else "off" @property + @setting(name="Power", setter_name="on", icon="mdi:power") def is_on(self) -> bool: """True if device is turned on.""" return self.power == "on" @property + @setting( + name="Mode", + setter_name="set_mode", + icon="mdi:fan", + choices=OperationMode, + ) def mode(self) -> OperationMode: """Operation mode. @@ -84,21 +93,30 @@ def mode(self) -> OperationMode: return mode @property + @sensor(name="Temperature", unit="C", device_class="temperature") def temperature(self) -> int: """Current temperature in degree celsius.""" return self.data["temperature"] @property + @sensor(name="Humidity", unit="%", device_class="humidity") def humidity(self) -> int: """Current humidity in percent.""" return self.data["humidity"] @property + @setting(name="Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["buzzer"] == 1 @property + @setting( + name="LED Brightness", + setter_name="set_led_brightness", + icon="mdi:brightness-6", + choices=LedBrightness, + ) def led_brightness(self) -> LedBrightness: """Buttons illumination Brightness level.""" try: @@ -110,26 +128,31 @@ def led_brightness(self) -> LedBrightness: return brightness @property + @setting(name="LED", setter_name="set_led", icon="mdi:led-outline") def led(self) -> bool: """True if LED is turned on.""" return self.led_brightness is not LedBrightness.Off @property + @setting(name="Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Return True if child lock is on.""" return self.data["child_lock"] == 1 @property + @sensor(name="No Water", icon="mdi:water-off") def no_water(self) -> bool: """True if the water tank is empty.""" return self.data["no_water"] == 1 @property + @sensor(name="Lid Opened", icon="mdi:cup-water") def lid_opened(self) -> bool: """True if the water tank is detached.""" return self.data["lid_opened"] == 1 @property + @sensor(name="Use Time", unit="s", icon="mdi:timer") def use_time(self) -> Optional[int]: """How long the device has been active in seconds. diff --git a/miio/integrations/tinymu/toiletlid/toiletlid.py b/miio/integrations/tinymu/toiletlid/toiletlid.py index 2584feba9..4069e1f25 100644 --- a/miio/integrations/tinymu/toiletlid/toiletlid.py +++ b/miio/integrations/tinymu/toiletlid/toiletlid.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -41,30 +42,36 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Work State", icon="mdi:state-machine") def work_state(self) -> int: """Device state code.""" return self.data["work_state"] @property + @sensor("Work Mode", icon="mdi:toilet") def work_mode(self) -> ToiletlidOperatingMode: """Device working mode.""" return ToiletlidOperatingMode((self.work_state - 1) // 16) @property + @sensor("Power", icon="mdi:power") def is_on(self) -> bool: return self.work_state != 1 @property + @sensor("Filter Life Remaining", icon="mdi:filter") def filter_use_percentage(self) -> str: """Filter percentage of remaining life.""" return "{}%".format(self.data["filter_use_flux"]) @property + @sensor("Filter Remaining Time", unit="d", icon="mdi:filter-outline", device_class="duration") def filter_remaining_time(self) -> int: """Filter remaining life days.""" return self.data["filter_use_time"] @property + @setting("Ambient Light", setter_name="set_ambient_light", icon="mdi:lightbulb-variant", choices=AmbientLightColor) def ambient_light(self) -> str: """Ambient light color.""" return self.data["ambient_light"] diff --git a/miio/integrations/viomi/viomidishwasher/viomidishwasher.py b/miio/integrations/viomi/viomidishwasher/viomidishwasher.py index 8d28fb1d9..539ca5283 100644 --- a/miio/integrations/viomi/viomidishwasher/viomidishwasher.py +++ b/miio/integrations/viomi/viomidishwasher/viomidishwasher.py @@ -8,6 +8,7 @@ from miio.click_common import EnumType, command, format_output from miio.device import Device, DeviceStatus +from miio.devicestatus import sensor, setting from miio.exceptions import DeviceException _LOGGER = logging.getLogger(__name__) @@ -103,6 +104,7 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @setting(name="Child Lock", setter_name="child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Returns the child lock status of the device.""" value = self.data["child_lock"] @@ -112,6 +114,12 @@ def child_lock(self) -> bool: raise DeviceException(f"{value} is not a valid child lock status.") @property + @setting( + name="Program", + setter_name="start", + icon="mdi:dishwasher", + choices=Program, + ) def program(self) -> Program: """Returns the current selected program of the device.""" program = self.data["program"] @@ -122,12 +130,14 @@ def program(self) -> Program: return Program.Unknown @property + @sensor(name="Door Open", icon="mdi:door-open") def door_open(self) -> bool: """Returns True if the door is open.""" return bool(self.data["run_status"] & (1 << 7)) @property + @sensor(name="System Status Raw", icon="mdi:information-outline") def system_status_raw(self) -> int: """Returns the raw status number of the device. @@ -139,12 +149,14 @@ def system_status_raw(self) -> int: return self.data["run_status"] @property + @sensor(name="Status", icon="mdi:dishwasher") def status(self) -> MachineStatus: """Returns the machine status of the device.""" return MachineStatus(self.data["wash_status"]) @property + @sensor(name="Temperature", unit="C", device_class="temperature") def temperature(self) -> int: """Returns the temperature in degree Celsius as determined by the NTC thermistor.""" @@ -152,6 +164,7 @@ def temperature(self) -> int: return self.data["wash_temp"] @property + @setting(name="Power", setter_name="on", icon="mdi:power") def power(self) -> bool: """Returns the power status of the device.""" @@ -162,6 +175,7 @@ def power(self) -> bool: raise DeviceException(f"{value} is not a valid power status.") @property + @sensor(name="Time Left", icon="mdi:timer") def time_left(self) -> timedelta: """Returns the timedelta in seconds of time left of the current program. @@ -174,6 +188,7 @@ def time_left(self) -> timedelta: raise DeviceException(f"{value} is not a valid integer for time_left.") @property + @sensor(name="Schedule", icon="mdi:calendar-clock") def schedule(self) -> Optional[datetime]: """Returns a datetime when the scheduled program should be finished. @@ -189,6 +204,7 @@ def schedule(self) -> Optional[datetime]: ) @property + @setting(name="Air Refresh Interval", setter_name="airrefresh", icon="mdi:fan") def air_refresh_interval(self) -> int: """Returns an integer on how often the air in the device should be refreshed. @@ -203,6 +219,7 @@ def air_refresh_interval(self) -> int: raise DeviceException(f"{value} is not a valid integer for freshdry_interval.") @property + @sensor(name="Program Progress", icon="mdi:progress-check") def program_progress(self) -> ProgramStatus: """Returns the program status of the running program.""" value = self.data["wash_process"] @@ -213,6 +230,7 @@ def program_progress(self) -> ProgramStatus: return ProgramStatus.Unknown @property + @sensor(name="Errors", icon="mdi:alert-circle") def errors(self) -> list[SystemStatus]: """Returns list of errors if detected in the system.""" diff --git a/miio/integrations/xiaomi/aircondition/airconditioner_miot.py b/miio/integrations/xiaomi/aircondition/airconditioner_miot.py index 6eaf34c42..4875ccdd9 100644 --- a/miio/integrations/xiaomi/aircondition/airconditioner_miot.py +++ b/miio/integrations/xiaomi/aircondition/airconditioner_miot.py @@ -7,6 +7,7 @@ from miio import DeviceStatus, MiotDevice from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -78,23 +79,27 @@ def __init__(self, status: str): Only write 1 or 0 to it would start or abort the auto clean mode. """ self.status = [int(value) for value in status.split(",")] - @property + + @sensor("Cleaning", icon="mdi:broom") def cleaning(self) -> bool: return bool(self.status[0]) - @property + + @sensor("Progress", unit="%", icon="mdi:progress-check") def progress(self) -> int: return int(self.status[1]) - @property + + @sensor("Stage", icon="mdi:broom") def stage(self) -> str: try: return CLEANING_STAGES[self.status[2]] except KeyError: return "Unknown stage" - @property + + @sensor("Cancellable", icon="mdi:cancel") def cancellable(self) -> bool: return bool(self.status[3]) @@ -138,20 +143,24 @@ def __init__(self, status): Also, if the countdown minutes set to 0, the timer would be disabled. """ self.status = [int(value) for value in status.split(",")] - @property + + @sensor("Enabled", icon="mdi:timer") def enabled(self) -> bool: return bool(self.status[0]) - @property + + @sensor("Countdown", icon="mdi:timer-outline") def countdown(self) -> timedelta: return timedelta(minutes=self.status[1]) - @property + + @sensor("Power On", icon="mdi:power") def power_on(self) -> bool: return bool(self.status[2]) - @property + + @sensor("Time Left", icon="mdi:timer-outline") def time_left(self) -> timedelta: return timedelta(minutes=self.status[3]) @@ -184,93 +193,111 @@ def __init__(self, data: dict[str, Any]) -> None: """ self.data = data - @property + + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if the device is turned on.""" return self.data["power"] - @property + + @sensor("Power", icon="mdi:power") def power(self) -> str: """Current power state.""" return "on" if self.is_on else "off" - @property + + @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:air-conditioner") def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["mode"]) - @property + + @setting("Target Temperature", setter_name="set_target_temperature", unit="°C", min_value=16, max_value=31, step=1, device_class="temperature", icon="mdi:thermometer") def target_temperature(self) -> float: """Target temperature in Celsius.""" return self.data["target_temperature"] - @property + + @setting("ECO", setter_name="set_eco", icon="mdi:leaf") def eco(self) -> bool: """True if ECO mode is on.""" return self.data["eco"] - @property + + @setting("Heater", setter_name="set_heater", icon="mdi:radiator") def heater(self) -> bool: """True if aux heat mode is on.""" return self.data["heater"] - @property + + @setting("Dryer", setter_name="set_dryer", icon="mdi:water-off") def dryer(self) -> bool: """True if aux dryer mode is on.""" return self.data["dryer"] - @property + + @setting("Sleep Mode", setter_name="set_sleep_mode", icon="mdi:sleep") def sleep_mode(self) -> bool: """True if sleep mode is on.""" return self.data["sleep_mode"] - @property + + @setting("Fan Speed", setter_name="set_fan_speed", choices=FanSpeed, icon="mdi:fan") def fan_speed(self) -> FanSpeed: """Current Fan speed.""" return FanSpeed(self.data["fan_speed"]) - @property + + @setting("Vertical Swing", setter_name="set_vertical_swing", icon="mdi:arrow-up-down") def vertical_swing(self) -> bool: """True if vertical swing is on.""" return self.data["vertical_swing"] - @property + + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> float: """Current ambient temperature in Celsius.""" return self.data["temperature"] - @property + + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is on.""" return self.data["buzzer"] - @property + + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """True if LED is on.""" return self.data["led"] - @property + + @sensor("Electricity", unit="kWh", device_class="energy", icon="mdi:flash") def electricity(self) -> float: """Power consumption accumulation in kWh.""" return self.data["electricity"] - @property + + @sensor("Clean", icon="mdi:broom") def clean(self) -> CleaningStatus: """Auto clean mode indicator.""" return CleaningStatus(self.data["clean"]) - @property + + @sensor("Total Running Duration", icon="mdi:timer-outline") def total_running_duration(self) -> timedelta: """Total running duration in hours.""" return timedelta(hours=self.data["running_duration"]) - @property + + @setting("Fan Speed Percent", setter_name="set_fan_speed_percent", unit="%", min_value=1, max_value=101, step=1, icon="mdi:fan") def fan_speed_percent(self) -> int: """Current fan speed in percent.""" return self.data["fan_speed_percent"] - @property + + @sensor("Timer", icon="mdi:timer") def timer(self) -> TimerStatus: """Countdown timer indicator.""" return TimerStatus(self.data["timer"]) diff --git a/miio/integrations/xiaomi/repeater/wifirepeater.py b/miio/integrations/xiaomi/repeater/wifirepeater.py index 43dad1ffd..5813817e3 100644 --- a/miio/integrations/xiaomi/repeater/wifirepeater.py +++ b/miio/integrations/xiaomi/repeater/wifirepeater.py @@ -4,6 +4,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor _LOGGER = logging.getLogger(__name__) @@ -25,11 +26,13 @@ def __init__(self, data): self.data = data @property + @sensor("Access Policy", icon="mdi:security") def access_policy(self) -> int: """Access policy of the associated stations.""" return self.data["sta"]["access_policy"] @property + @sensor("Associated Stations", icon="mdi:devices") def associated_stations(self) -> dict: """List of associated stations.""" return self.data["mat"] @@ -51,14 +54,17 @@ def __init__(self, data): self.data = data @property + @sensor("SSID", icon="mdi:wifi") def ssid(self) -> str: return self.data["ssid"] @property + @sensor("Password", icon="mdi:lock") def password(self) -> str: return self.data["pwd"] @property + @sensor("SSID Hidden", icon="mdi:wifi-off") def ssid_hidden(self) -> bool: return self.data["hidden"] == 1 diff --git a/miio/integrations/xiaomi/wifispeaker/wifispeaker.py b/miio/integrations/xiaomi/wifispeaker/wifispeaker.py index e37d11078..d2aeda925 100644 --- a/miio/integrations/xiaomi/wifispeaker/wifispeaker.py +++ b/miio/integrations/xiaomi/wifispeaker/wifispeaker.py @@ -5,6 +5,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor _LOGGER = logging.getLogger(__name__) @@ -46,46 +47,56 @@ def __init__(self, data): self.data = data @property + @sensor("Device Name", icon="mdi:speaker") def device_name(self) -> str: """Name of the device.""" return self.data["DeviceName"] @property + @sensor("Channel", icon="mdi:radio") def channel(self) -> str: """Name of the channel.""" return self.data["channel_title"] @property + @sensor("State", icon="mdi:play-circle") def state(self) -> PlayState: """State of the device, e.g. PLAYING.""" return PlayState(self.data["current_state"]) @property + @sensor("Hardware Version", icon="mdi:information-outline") def hardware_version(self) -> str: + """Hardware version.""" return self.data["hardware_version"] @property - def play_mode(self): + @sensor("Play Mode", icon="mdi:repeat") + def play_mode(self) -> str: """Play mode such as REPEAT_ALL.""" # note: this can be enumized when all values are known return self.data["play_mode"] @property + @sensor("Track Artist", icon="mdi:account-music") def track_artist(self) -> str: """Artist of the current track.""" return self.data["track_artist"] @property + @sensor("Track Title", icon="mdi:music-note") def track_title(self) -> str: """Title of the current track.""" return self.data["track_title"] @property + @sensor("Track Duration", icon="mdi:timer-music", device_class="duration") def track_duration(self) -> str: """Total duration of the current track.""" return self.data["track_duration"] @property + @sensor("Transport Channel", icon="mdi:radio") def transport_channel(self) -> TransportChannel: """Transport channel, e.g. PLAYLIST.""" return TransportChannel(self.data["transport_channel"]) diff --git a/miio/integrations/yeelight/dual_switch/yeelight_dual_switch.py b/miio/integrations/yeelight/dual_switch/yeelight_dual_switch.py index 84e118745..019073b38 100644 --- a/miio/integrations/yeelight/dual_switch/yeelight_dual_switch.py +++ b/miio/integrations/yeelight/dual_switch/yeelight_dual_switch.py @@ -4,6 +4,7 @@ import click from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting from miio.miot_device import DeviceStatus, MiotDevice, MiotMapping @@ -61,46 +62,73 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Switch 1 State", icon="mdi:toggle-switch") def switch_1_state(self) -> bool: """First switch state.""" return bool(self.data["switch_1_state"]) @property + @setting("Switch 1 Default State", setter_name="set_default_state", icon="mdi:toggle-switch-outline") def switch_1_default_state(self) -> bool: """First switch default state.""" return bool(self.data["switch_1_default_state"]) @property + @setting( + "Switch 1 Off Delay", + setter_name="set_switch_off_delay", + icon="mdi:timer", + device_class="duration", + unit="s", + min_value=-1, + max_value=43200, + step=1, + ) def switch_1_off_delay(self) -> int: """First switch off delay.""" return self.data["switch_1_off_delay"] @property + @sensor("Switch 2 State", icon="mdi:toggle-switch") def switch_2_state(self) -> bool: """Second switch state.""" return bool(self.data["switch_2_state"]) @property + @setting("Switch 2 Default State", setter_name="set_default_state", icon="mdi:toggle-switch-outline") def switch_2_default_state(self) -> bool: """Second switch default state.""" return bool(self.data["switch_2_default_state"]) @property + @setting( + "Switch 2 Off Delay", + setter_name="set_switch_off_delay", + icon="mdi:timer", + device_class="duration", + unit="s", + min_value=-1, + max_value=43200, + step=1, + ) def switch_2_off_delay(self) -> int: """Second switch off delay.""" return self.data["switch_2_off_delay"] @property + @setting("Interlock", setter_name="set_interlock", icon="mdi:lock-outline") def interlock(self) -> bool: """Interlock.""" return bool(self.data["interlock"]) @property + @setting("Flex Mode", setter_name="set_flex_mode", icon="mdi:shuffle-variant") def flex_mode(self) -> int: """Flex mode.""" return self.data["flex_mode"] @property + @sensor("RC List", icon="mdi:remote") def rc_list(self) -> str: """List of paired remote controls.""" return self.data["rc_list"] diff --git a/miio/integrations/yunmi/waterpurifier/waterpurifier.py b/miio/integrations/yunmi/waterpurifier/waterpurifier.py index a6e435d8e..3125cbb80 100644 --- a/miio/integrations/yunmi/waterpurifier/waterpurifier.py +++ b/miio/integrations/yunmi/waterpurifier/waterpurifier.py @@ -3,6 +3,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor _LOGGER = logging.getLogger(__name__) @@ -14,78 +15,96 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: return self.data["power"] @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: return self.power == "on" @property + @sensor("Mode", icon="mdi:water") def mode(self) -> str: """Current operation mode.""" return self.data["mode"] @property + @sensor("TDS", unit="ppm", icon="mdi:water-opacity") def tds(self) -> str: return self.data["tds"] @property + @sensor("Filter Life Remaining", unit="%", icon="mdi:filter") def filter_life_remaining(self) -> int: """Time until the filter should be changed.""" return self.data["filter1_life"] @property + @sensor("Filter State", icon="mdi:filter") def filter_state(self) -> str: return self.data["filter1_state"] @property + @sensor("Filter 2 Life Remaining", unit="%", icon="mdi:filter") def filter2_life_remaining(self) -> int: """Time until the filter should be changed.""" return self.data["filter_life"] @property + @sensor("Filter 2 State", icon="mdi:filter") def filter2_state(self) -> str: return self.data["filter_state"] @property + @sensor("Life", icon="mdi:clock-outline") def life(self) -> str: return self.data["life"] @property + @sensor("State", icon="mdi:information-outline") def state(self) -> str: return self.data["state"] @property + @sensor("Level", icon="mdi:water") def level(self) -> str: return self.data["level"] @property + @sensor("Volume", icon="mdi:cup-water") def volume(self) -> str: return self.data["volume"] @property + @sensor("Filter", icon="mdi:filter") def filter(self) -> str: return self.data["filter"] @property + @sensor("Usage", icon="mdi:chart-line") def usage(self) -> str: return self.data["usage"] @property + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> str: return self.data["temperature"] @property + @sensor("UV Filter Life Remaining", unit="%", icon="mdi:filter") def uv_filter_life_remaining(self) -> int: """Time until the filter should be changed.""" return self.data["uv_life"] @property + @sensor("UV Filter State", icon="mdi:filter") def uv_filter_state(self) -> str: return self.data["uv_state"] @property + @sensor("Valve", icon="mdi:valve") def valve(self) -> str: return self.data["elecval_state"] diff --git a/miio/integrations/yunmi/waterpurifier/waterpurifier_yunmi.py b/miio/integrations/yunmi/waterpurifier/waterpurifier_yunmi.py index 8009c6300..aca37d56c 100644 --- a/miio/integrations/yunmi/waterpurifier/waterpurifier_yunmi.py +++ b/miio/integrations/yunmi/waterpurifier/waterpurifier_yunmi.py @@ -4,6 +4,7 @@ from miio import Device, DeviceStatus from miio.click_common import command, format_output +from miio.devicestatus import sensor _LOGGER = logging.getLogger(__name__) @@ -93,8 +94,9 @@ def __init__(self, operation_status: int): for i in range(0, len(ERROR_DESCRIPTION)) if (1 << i) & operation_status ] - @property + + @sensor("Errors", icon="mdi:alert-circle") def errors(self) -> list: return self.err_list @@ -117,123 +119,147 @@ def __init__(self, data: dict[str, Any]) -> None: 'filter3_flow_used': 1440, 'filter3_life_used': 3313} """ self.data = data - @property + + @sensor("Operation Status", icon="mdi:information-outline") def operation_status(self) -> OperationStatus: """Current operation status.""" return OperationStatus(self.data["run_status"]) - @property + + @sensor("Filter 1 Life Total", icon="mdi:filter-outline") def filter1_life_total(self) -> timedelta: """Filter1 total available time in hours.""" return timedelta(hours=self.data["f1_totaltime"]) - @property + + @sensor("Filter 1 Life Used", icon="mdi:filter-outline") def filter1_life_used(self) -> timedelta: """Filter1 used time in hours.""" return timedelta(hours=self.data["f1_usedtime"]) - @property + + @sensor("Filter 1 Life Remaining", icon="mdi:filter-outline") def filter1_life_remaining(self) -> timedelta: """Filter1 remaining time in hours.""" return self.filter1_life_total - self.filter1_life_used - @property + + @sensor("Filter 1 Flow Total", unit="L", icon="mdi:water") def filter1_flow_total(self) -> int: """Filter1 total available flow in Metric Liter (L).""" return self.data["f1_totalflow"] - @property + + @sensor("Filter 1 Flow Used", unit="L", icon="mdi:water") def filter1_flow_used(self) -> int: """Filter1 used flow in Metric Liter (L).""" return self.data["f1_usedflow"] - @property + + @sensor("Filter 1 Flow Remaining", unit="L", icon="mdi:water") def filter1_flow_remaining(self) -> int: """Filter1 remaining flow in Metric Liter (L).""" return self.filter1_flow_total - self.filter1_flow_used - @property + + @sensor("Filter 2 Life Total", icon="mdi:filter-outline") def filter2_life_total(self) -> timedelta: """Filter2 total available time in hours.""" return timedelta(hours=self.data["f2_totaltime"]) - @property + + @sensor("Filter 2 Life Used", icon="mdi:filter-outline") def filter2_life_used(self) -> timedelta: """Filter2 used time in hours.""" return timedelta(hours=self.data["f2_usedtime"]) - @property + + @sensor("Filter 2 Life Remaining", icon="mdi:filter-outline") def filter2_life_remaining(self) -> timedelta: """Filter2 remaining time in hours.""" return self.filter2_life_total - self.filter2_life_used - @property + + @sensor("Filter 2 Flow Total", unit="L", icon="mdi:water") def filter2_flow_total(self) -> int: """Filter2 total available flow in Metric Liter (L).""" return self.data["f2_totalflow"] - @property + + @sensor("Filter 2 Flow Used", unit="L", icon="mdi:water") def filter2_flow_used(self) -> int: """Filter2 used flow in Metric Liter (L).""" return self.data["f2_usedflow"] - @property + + @sensor("Filter 2 Flow Remaining", unit="L", icon="mdi:water") def filter2_flow_remaining(self) -> int: """Filter2 remaining flow in Metric Liter (L).""" return self.filter2_flow_total - self.filter2_flow_used - @property + + @sensor("Filter 3 Life Total", icon="mdi:filter-outline") def filter3_life_total(self) -> timedelta: """Filter3 total available time in hours.""" return timedelta(hours=self.data["f3_totaltime"]) - @property + + @sensor("Filter 3 Life Used", icon="mdi:filter-outline") def filter3_life_used(self) -> timedelta: """Filter3 used time in hours.""" return timedelta(hours=self.data["f3_usedtime"]) - @property + + @sensor("Filter 3 Life Remaining", icon="mdi:filter-outline") def filter3_life_remaining(self) -> timedelta: """Filter3 remaining time in hours.""" return self.filter3_life_total - self.filter3_life_used - @property + + @sensor("Filter 3 Flow Total", unit="L", icon="mdi:water") def filter3_flow_total(self) -> int: """Filter3 total available flow in Metric Liter (L).""" return self.data["f3_totalflow"] - @property + + @sensor("Filter 3 Flow Used", unit="L", icon="mdi:water") def filter3_flow_used(self) -> int: """Filter3 used flow in Metric Liter (L).""" return self.data["f3_usedflow"] - @property + + @sensor("Filter 3 Flow Remaining", unit="L", icon="mdi:water") def filter3_flow_remaining(self) -> int: """Filter1 remaining flow in Metric Liter (L).""" return self.filter3_flow_total - self.filter3_flow_used - @property + + @sensor("TDS In", unit="ppm", icon="mdi:water-check") def tds_in(self) -> int: """TDS value of input water.""" return self.data["tds_in"] - @property + + @sensor("TDS Out", unit="ppm", icon="mdi:water-check") def tds_out(self) -> int: """TDS value of output water.""" return self.data["tds_out"] - @property + + @sensor("Rinse", icon="mdi:water-sync") def rinse(self) -> bool: """True if the device is rinsing.""" return self.data["rinse"] - @property + + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> int: """Current water temperature in Celsius.""" return self.data["temperature"] - @property + + @sensor("TDS Warning Threshold", unit="ppm", icon="mdi:water-alert") def tds_warn_thd(self) -> int: """TDS warning threshold.""" return self.data["tds_warn_thd"] diff --git a/miio/integrations/zhimi/airpurifier/airfresh.py b/miio/integrations/zhimi/airpurifier/airfresh.py index f74bf5a69..ca0aa90a3 100644 --- a/miio/integrations/zhimi/airpurifier/airfresh.py +++ b/miio/integrations/zhimi/airpurifier/airfresh.py @@ -7,6 +7,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -90,46 +91,54 @@ def __init__(self, data: dict[str, Any], model: str) -> None: self.data = data self.model = model - @property + + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] - @property + + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """Return True if device is on.""" return self.power == "on" - @property + + @sensor("AQI", unit="μg/m³", icon="mdi:air-filter") def aqi(self) -> int: """Air quality index.""" return self.data["aqi"] - @property + + @sensor("Average AQI", unit="μg/m³", icon="mdi:air-filter") def average_aqi(self) -> int: """Average of the air quality index.""" return self.data["average_aqi"] - @property + + @sensor("CO2", unit="ppm", icon="mdi:molecule-co2", device_class="carbon_dioxide") def co2(self) -> int: """Carbon dioxide.""" return self.data["co2"] - @property + + @sensor("Humidity", unit="%", device_class="humidity", icon="mdi:water-percent") def humidity(self) -> int: """Current humidity.""" return self.data["humidity"] - @property + + @setting("PTC", setter_name="set_ptc", icon="mdi:radiator") def ptc(self) -> Optional[bool]: """Return True if PTC is on.""" if self.data["ptc_state"] is not None: return self.data["ptc_state"] == "on" return None - @property + + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> Optional[float]: """Current temperature, if available.""" if self.data["temp_dec"] is not None: @@ -139,26 +148,30 @@ def temperature(self) -> Optional[float]: return self.data["temp_dec"] / 10.0 return None - @property + + @sensor("NTC Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def ntc_temperature(self) -> Optional[float]: """Current ntc temperature, if available.""" if self.data["ntcT"] is not None: return self.data["ntcT"] return None - @property + + @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:fan") def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["mode"]) - @property + + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """Return True if LED is on.""" return self.data["led"] == "on" - @property + + @setting("LED Brightness", setter_name="set_led_brightness", choices=LedBrightness, icon="mdi:brightness-6") def led_brightness(self) -> Optional[LedBrightness]: """Brightness of the LED.""" if self.data["led_level"] is not None: @@ -171,41 +184,48 @@ def led_brightness(self) -> Optional[LedBrightness]: return None return None - @property + + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> Optional[bool]: """Return True if buzzer is on.""" if self.data["buzzer"] is not None: return self.data["buzzer"] == "on" return None - @property + + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Return True if child lock is on.""" return self.data["child_lock"] == "on" - @property + + @sensor("Filter Life Remaining", unit="%", icon="mdi:filter-outline") def filter_life_remaining(self) -> int: """Time until the filter should be changed.""" return self.data["filter_life"] - @property + + @sensor("Filter Hours Used", unit="h", icon="mdi:filter-outline") def filter_hours_used(self) -> int: """How long the filter has been in use.""" return self.data["f1_hour_used"] - @property + + @sensor("Use Time", unit="s", icon="mdi:timer-sand") def use_time(self) -> int: """How long the device has been active in seconds.""" return self.data["use_time"] - @property + + @sensor("Motor Speed", unit="rpm", icon="mdi:fan") def motor_speed(self) -> int: """Speed of the motor.""" return self.data["motor1_speed"] - @property + + @setting("Extra Features", setter_name="set_extra_features", icon="mdi:star") def extra_features(self) -> Optional[int]: return self.data["app_extra"] diff --git a/miio/integrations/zhimi/airpurifier/airpurifier.py b/miio/integrations/zhimi/airpurifier/airpurifier.py index e30853dd4..e9c1589b9 100644 --- a/miio/integrations/zhimi/airpurifier/airpurifier.py +++ b/miio/integrations/zhimi/airpurifier/airpurifier.py @@ -7,6 +7,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting from .airfilter_util import FilterType, FilterTypeUtil @@ -128,46 +129,54 @@ def __init__(self, data: dict[str, Any]) -> None: self.filter_type_util = FilterTypeUtil() self.data = data - @property + + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] - @property + + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """Return True if device is on.""" return self.power == "on" - @property + + @sensor("AQI", unit="μg/m³", icon="mdi:air-filter") def aqi(self) -> int: """Air quality index.""" return self.data["aqi"] - @property + + @sensor("Average AQI", unit="μg/m³", icon="mdi:air-filter") def average_aqi(self) -> int: """Average of the air quality index.""" return self.data["average_aqi"] - @property + + @sensor("Humidity", unit="%", device_class="humidity", icon="mdi:water-percent") def humidity(self) -> int: """Current humidity.""" return self.data["humidity"] - @property + + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> Optional[float]: """Current temperature, if available.""" if self.data["temp_dec"] is not None: return self.data["temp_dec"] / 10.0 return None - @property + + @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:air-purifier") def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["mode"]) - @property + + @sensor("Sleep Mode", icon="mdi:sleep") def sleep_mode(self) -> Optional[SleepMode]: """Operation mode of the sleep state. @@ -177,13 +186,15 @@ def sleep_mode(self) -> Optional[SleepMode]: return SleepMode(self.data["sleep_mode"]) return None - @property + + @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """Return True if LED is on.""" return self.data["led"] == "on" - @property + + @setting("LED Brightness", setter_name="set_led_brightness", choices=LedBrightness, icon="mdi:brightness-6") def led_brightness(self) -> Optional[LedBrightness]: """Brightness of the LED.""" if self.data["led_b"] is not None: @@ -193,119 +204,140 @@ def led_brightness(self) -> Optional[LedBrightness]: return None return None - @property + + @sensor("Illuminance", unit="lx", device_class="illuminance", icon="mdi:brightness-5") def illuminance(self) -> Optional[int]: """Environment illuminance level in lux [0-200]. Sensor value is updated only when device is turned on. """ return self.data["bright"] - @property + + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> Optional[bool]: """Return True if buzzer is on.""" if self.data["buzzer"] is not None: return self.data["buzzer"] == "on" return None - @property + + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Return True if child lock is on.""" return self.data["child_lock"] == "on" - @property + + @setting("Favorite Level", setter_name="set_favorite_level", min_value=0, max_value=17, step=1, icon="mdi:star") def favorite_level(self) -> int: """Return favorite level, which is used if the mode is ``favorite``.""" # Favorite level used when the mode is `favorite`. return self.data["favorite_level"] - @property + + @sensor("Filter Life Remaining", unit="%", icon="mdi:filter-outline") def filter_life_remaining(self) -> int: """Time until the filter should be changed.""" return self.data["filter1_life"] - @property + + @sensor("Filter Hours Used", unit="h", icon="mdi:filter-outline") def filter_hours_used(self) -> int: """How long the filter has been in use.""" return self.data["f1_hour_used"] - @property + + @sensor("Use Time", unit="s", icon="mdi:timer-sand") def use_time(self) -> int: """How long the device has been active in seconds.""" return self.data["use_time"] - @property + + @sensor("Purify Volume", unit="m³", icon="mdi:air-purifier") def purify_volume(self) -> int: """The volume of purified air in cubic meter.""" return self.data["purify_volume"] - @property + + @sensor("Motor Speed", unit="rpm", icon="mdi:fan") def motor_speed(self) -> int: """Speed of the motor.""" return self.data["motor1_speed"] - @property + + @sensor("Motor 2 Speed", unit="rpm", icon="mdi:fan") def motor2_speed(self) -> Optional[int]: """Speed of the 2nd motor.""" return self.data["motor2_speed"] - @property + + @setting("Volume", setter_name="set_volume", unit="%", min_value=0, max_value=100, step=1, icon="mdi:volume-high") def volume(self) -> Optional[int]: """Volume of sound notifications [0-100].""" return self.data["volume"] - @property + + @sensor("Filter RFID Product ID", icon="mdi:barcode") def filter_rfid_product_id(self) -> Optional[str]: """RFID product ID of installed filter.""" return self.data["rfid_product_id"] - @property + + @sensor("Filter RFID Tag", icon="mdi:barcode") def filter_rfid_tag(self) -> Optional[str]: """RFID tag ID of installed filter.""" return self.data["rfid_tag"] - @property + + @sensor("Filter Type", icon="mdi:filter-outline") def filter_type(self) -> Optional[FilterType]: """Type of installed filter.""" return self.filter_type_util.determine_filter_type( self.filter_rfid_tag, self.filter_rfid_product_id ) - @property + + @setting("Learn Mode", setter_name="set_learn_mode", icon="mdi:school") def learn_mode(self) -> bool: """Return True if Learn Mode is enabled.""" return self.data["act_sleep"] == "single" - @property + + @sensor("Sleep Time", unit="s", icon="mdi:sleep") def sleep_time(self) -> Optional[int]: return self.data["sleep_time"] - @property + + @sensor("Sleep Mode Learn Count", icon="mdi:counter") def sleep_mode_learn_count(self) -> Optional[int]: return self.data["sleep_data_num"] - @property + + @setting("Extra Features", setter_name="set_extra_features", icon="mdi:star") def extra_features(self) -> Optional[int]: return self.data["app_extra"] - @property + + @sensor("Turbo Mode Supported", icon="mdi:rocket") def turbo_mode_supported(self) -> Optional[bool]: if self.data["app_extra"] is not None: return self.data["app_extra"] == 1 return None - @property + + @setting("Auto Detect", setter_name="set_auto_detect", icon="mdi:eye") def auto_detect(self) -> Optional[bool]: """Return True if auto detect is enabled.""" if self.data["act_det"] is not None: return self.data["act_det"] == "on" return None - @property + + @sensor("Button Pressed", icon="mdi:gesture-tap-button") def button_pressed(self) -> Optional[str]: """Last pressed button.""" return self.data["button_pressed"] diff --git a/miio/integrations/zhimi/fan/zhimi_miot.py b/miio/integrations/zhimi/fan/zhimi_miot.py index 6f7f15749..4ea3b20bb 100644 --- a/miio/integrations/zhimi/fan/zhimi_miot.py +++ b/miio/integrations/zhimi/fan/zhimi_miot.py @@ -5,6 +5,7 @@ from miio import DeviceException, DeviceStatus, MiotDevice from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting from miio.utils import deprecated @@ -80,18 +81,21 @@ def __init__(self, data: dict[str, Any]) -> None: {'code': 0, 'did': 'temperature', 'piid': 7, 'siid': 7, 'value': 26.4}, """ self.data = data - @property + + @setting("Ionizer", setter_name="set_ionizer", icon="mdi:atom") def ionizer(self) -> bool: """True if negative ions generation is enabled.""" return self.data["anion"] - @property + + @sensor("Battery Supported", icon="mdi:battery") def battery_supported(self) -> bool: """True if battery is supported.""" return self.data["battery_supported"] - @property + + @sensor("Buttons Pressed", icon="mdi:gesture-tap-button") def buttons_pressed(self) -> str: """What buttons on the fan are pressed now.""" code = self.data["buttons_pressed"] @@ -102,18 +106,21 @@ def buttons_pressed(self) -> str: if code == 2: return "Swing" return "Unknown" - @property + + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["buzzer"] - @property + + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """True if child lock if on.""" return self.data["child_lock"] - @property + + @setting("Fan Level", setter_name="set_fan_speed", min_value=1, max_value=4, step=1, icon="mdi:fan") def fan_level(self) -> int: """Fan level (1-4).""" return self.data["fan_level"] @@ -123,63 +130,75 @@ def fan_level(self) -> int: def fan_speed(self) -> int: """Fan speed (1-100).""" return self.speed - @property + + @setting("Speed", setter_name="set_speed", min_value=1, max_value=100, step=1, unit="%", icon="mdi:fan") def speed(self) -> int: """Fan speed (1-100).""" return self.data["fan_speed"] - @property + + @sensor("Humidity", unit="%", device_class="humidity", icon="mdi:water-percent") def humidity(self) -> int: """Air humidity in percent.""" return self.data["humidity"] - @property + + @setting("LED Brightness", setter_name="set_led_brightness", min_value=0, max_value=100, step=1, unit="%", icon="mdi:brightness-6") def led_brightness(self) -> int: """LED brightness (1-100).""" return self.data["light"] - @property + + @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:fan") def mode(self) -> OperationMode: """Operation mode (normal or nature).""" return OperationMode[OperationModeFanZA5(self.data["mode"]).name] - @property + + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["power"] else "off" - @property + + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is currently on.""" return self.data["power"] - @property + + @setting("Delay Off Countdown", setter_name="delay_off", unit="s", icon="mdi:timer-off-outline") def delay_off_countdown(self) -> int: """Countdown until turning off in minutes.""" return self.data["power_off_time"] - @property + + @sensor("Power Supply Attached", icon="mdi:power-plug") def powersupply_attached(self) -> bool: """True is power supply is attached.""" return self.data["powersupply_attached"] - @property + + @sensor("Speed RPM", unit="rpm", icon="mdi:fan") def speed_rpm(self) -> int: """Fan rotations per minute.""" return self.data["speed_rpm"] - @property + + @setting("Oscillate", setter_name="set_oscillate", icon="mdi:arrow-left-right") def oscillate(self) -> bool: """True if oscillation is enabled.""" return self.data["swing_mode"] - @property + + @setting("Angle", setter_name="set_angle", icon="mdi:angle-acute") def angle(self) -> int: """Oscillation angle.""" return self.data["swing_mode_angle"] - @property + + @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") def temperature(self) -> Any: """Air temperature (degree celsius).""" return self.data["temperature"] diff --git a/miio/integrations/zhimi/heater/heater.py b/miio/integrations/zhimi/heater/heater.py index e42ff6e93..60c1e96c6 100644 --- a/miio/integrations/zhimi/heater/heater.py +++ b/miio/integrations/zhimi/heater/heater.py @@ -6,6 +6,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) @@ -57,16 +58,19 @@ def __init__(self, data: dict[str, Any]) -> None: self.data = data @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is currently on.""" return self.power == "on" @property + @sensor("Humidity", unit="%", icon="mdi:water-percent", device_class="humidity") def humidity(self) -> Optional[int]: """Current humidity.""" if ( @@ -78,36 +82,56 @@ def humidity(self) -> Optional[int]: return None @property + @sensor("Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature") def temperature(self) -> float: """Current temperature.""" return self.data["temperature"] @property + @setting( + "Target Temperature", + unit="°C", + setter_name="set_target_temperature", + min_value=16, + max_value=32, + device_class="temperature", + icon="mdi:thermometer", + ) def target_temperature(self) -> int: """Target temperature.""" return self.data["target_temperature"] @property + @setting( + "Brightness", + setter_name="set_brightness", + choices=Brightness, + icon="mdi:brightness-6", + ) def brightness(self) -> Brightness: """Display brightness.""" return Brightness(self.data["brightness"]) @property + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["buzzer"] in ["on", 1, 2] @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """True if child lock is on.""" return self.data["child_lock"] == "on" @property + @sensor("Use Time", unit="s", icon="mdi:timer", device_class="duration") def use_time(self) -> int: """How long the device has been active in seconds.""" return self.data["use_time"] @property + @sensor("Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration") def delay_off_countdown(self) -> Optional[int]: """Countdown until turning off in seconds.""" if "poweroff_time" in self.data and self.data["poweroff_time"] is not None: diff --git a/miio/integrations/zhimi/heater/heater_miot.py b/miio/integrations/zhimi/heater/heater_miot.py index 7d6104754..15f66a239 100644 --- a/miio/integrations/zhimi/heater/heater_miot.py +++ b/miio/integrations/zhimi/heater/heater_miot.py @@ -6,6 +6,7 @@ from miio import DeviceStatus, MiotDevice from miio.click_common import EnumType, command, format_output +from miio.devicestatus import sensor, setting _LOGGER = logging.getLogger(__name__) _MAPPINGS = { @@ -121,46 +122,68 @@ def __init__(self, data: dict[str, Any], model: str) -> None: self.model = model @property + @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.is_on else "off" @property + @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is currently on.""" return self.data["power"] @property + @setting( + "Target Temperature", + unit="°C", + setter_name="set_target_temperature", + min_value=18, + max_value=28, + device_class="temperature", + icon="mdi:thermometer", + ) def target_temperature(self) -> int: """Target temperature.""" return self.data["target_temperature"] @property + @sensor("Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration") def delay_off_countdown(self) -> int: """Countdown until turning off in seconds.""" return self.data["countdown_time"] @property + @sensor("Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature") def temperature(self) -> float: """Current temperature.""" return self.data["temperature"] @property + @sensor("Relative Humidity", unit="%", icon="mdi:water-percent", device_class="humidity") def relative_humidity(self) -> Optional[int]: """Current relative humidity.""" return self.data.get("relative_humidity") @property + @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """True if child lock is on, False otherwise.""" return self.data["child_lock"] is True @property + @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on, False otherwise.""" return self.data["buzzer"] is True @property + @setting( + "LED Brightness", + setter_name="set_led_brightness", + choices=LedBrightness, + icon="mdi:brightness-6", + ) def led_brightness(self) -> LedBrightness: """LED indicator brightness.""" value = self.data["led_brightness"] diff --git a/miio/integrations/zimi/clock/alarmclock.py b/miio/integrations/zimi/clock/alarmclock.py index ac12ce7c3..004458ed9 100644 --- a/miio/integrations/zimi/clock/alarmclock.py +++ b/miio/integrations/zimi/clock/alarmclock.py @@ -5,6 +5,7 @@ from miio import Device, DeviceStatus from miio.click_common import EnumType, command +from miio.devicestatus import sensor class HourlySystem(enum.Enum): @@ -36,15 +37,18 @@ def __init__(self, data): self._end = data[2] @property + @sensor("Enabled", icon="mdi:alarm") def enabled(self) -> bool: return self._enabled @property - def start(self): + @sensor("Start Time", icon="mdi:clock-start") + def start(self) -> str: return self._start @property - def end(self): + @sensor("End Time", icon="mdi:clock-end") + def end(self) -> str: return self._end From 6a0016303a01094d04819909f9f645dc7adba630 Mon Sep 17 00:00:00 2001 From: Andrzej Pomirski Date: Fri, 27 Mar 2026 02:03:15 +0100 Subject: [PATCH 2/2] Fix decorator formatting with ruff format The decorator order was @property then @sensor/@setting (correct), but blank lines were misplaced between them. Run ruff format on all changed files to fix spacing. --- .../cgllc/airmonitor/airqualitymonitor.py | 4 +- .../airmonitor/airqualitymonitor_miot.py | 4 +- .../chuangmi/camera/chuangmi_camera.py | 8 +- .../chuangmi/plug/chuangmi_plug.py | 4 +- miio/integrations/chunmi/cooker/cooker.py | 86 ++++++------ .../deerma/humidifier/airhumidifier_jsqs.py | 6 +- .../deerma/humidifier/airhumidifier_mjjsq.py | 4 +- .../dmaker/airfresh/airfresh_t2017.py | 8 +- miio/integrations/dmaker/fan/fan.py | 4 +- miio/integrations/dmaker/fan/fan_miot.py | 4 +- .../dreame/vacuum/dreamevacuum_miot.py | 72 +++++----- .../acpartner/airconditioningcompanion.py | 7 +- .../acpartner/airconditioningcompanionMCN.py | 22 ++- .../lumi/curtain/curtain_youpin.py | 14 +- miio/integrations/mijia/vacuum/g1vacuum.py | 44 +++--- .../nwt/dehumidifier/airdehumidifier.py | 4 +- .../philips/light/philips_bulb.py | 15 ++- .../philips/light/philips_eyecare.py | 13 +- .../philips/light/philips_rwread.py | 4 +- .../roidmi/vacuum/roidmivacuum_miot.py | 126 +++++++++++------- .../tinymu/toiletlid/toiletlid.py | 14 +- .../aircondition/airconditioner_miot.py | 88 +++++++----- .../xiaomi/repeater/wifirepeater.py | 2 +- .../dual_switch/yeelight_dual_switch.py | 12 +- .../yunmi/waterpurifier/waterpurifier.py | 4 +- .../waterpurifier/waterpurifier_yunmi.py | 54 ++++---- .../zhimi/airpurifier/airfresh.py | 53 +++++--- .../zhimi/airpurifier/airpurifier.py | 100 +++++++++----- miio/integrations/zhimi/fan/zhimi_miot.py | 76 +++++++---- miio/integrations/zhimi/heater/heater.py | 8 +- miio/integrations/zhimi/heater/heater_miot.py | 12 +- 31 files changed, 561 insertions(+), 315 deletions(-) diff --git a/miio/integrations/cgllc/airmonitor/airqualitymonitor.py b/miio/integrations/cgllc/airmonitor/airqualitymonitor.py index e616efb67..0dffc89be 100644 --- a/miio/integrations/cgllc/airmonitor/airqualitymonitor.py +++ b/miio/integrations/cgllc/airmonitor/airqualitymonitor.py @@ -159,7 +159,9 @@ def pm25(self) -> Optional[float]: return self.data.get("pm25") @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> Optional[float]: """Return temperature value (-10...50°C).""" return self.data.get("temperature") diff --git a/miio/integrations/cgllc/airmonitor/airqualitymonitor_miot.py b/miio/integrations/cgllc/airmonitor/airqualitymonitor_miot.py index a734a3bb2..fccfaa4fb 100644 --- a/miio/integrations/cgllc/airmonitor/airqualitymonitor_miot.py +++ b/miio/integrations/cgllc/airmonitor/airqualitymonitor_miot.py @@ -126,7 +126,9 @@ def pm10(self) -> int: return self.data["pm10"] @property - @sensor("Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature") + @sensor( + "Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature" + ) def temperature(self) -> float: """Return temperature value (-30...100°C).""" return self.data["temperature"] diff --git a/miio/integrations/chuangmi/camera/chuangmi_camera.py b/miio/integrations/chuangmi/camera/chuangmi_camera.py index c85deae8c..3a1e70d09 100644 --- a/miio/integrations/chuangmi/camera/chuangmi_camera.py +++ b/miio/integrations/chuangmi/camera/chuangmi_camera.py @@ -98,7 +98,9 @@ def power(self) -> bool: return self.data["power"] == "on" @property - @setting(name="Motion Record", setter_name="motion_record_on", icon="mdi:motion-sensor") + @setting( + name="Motion Record", setter_name="motion_record_on", icon="mdi:motion-sensor" + ) def motion_record(self) -> bool: """Motion record status.""" return self.data["motion_record"] == "on" @@ -122,7 +124,9 @@ def flip(self) -> bool: return self.data["flip"] == "on" @property - @setting(name="Improve Program", setter_name="improve_program_on", icon="mdi:chart-line") + @setting( + name="Improve Program", setter_name="improve_program_on", icon="mdi:chart-line" + ) def improve_program(self) -> bool: """Customer experience improvement program status.""" return self.data["improve_program"] == "on" diff --git a/miio/integrations/chuangmi/plug/chuangmi_plug.py b/miio/integrations/chuangmi/plug/chuangmi_plug.py index 087a2144e..2dc04ceac 100644 --- a/miio/integrations/chuangmi/plug/chuangmi_plug.py +++ b/miio/integrations/chuangmi/plug/chuangmi_plug.py @@ -63,7 +63,9 @@ def is_on(self) -> bool: return self.power @property - @sensor("Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature") + @sensor( + "Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature" + ) def temperature(self) -> int: return self.data["temperature"] diff --git a/miio/integrations/chunmi/cooker/cooker.py b/miio/integrations/chunmi/cooker/cooker.py index df19cf177..911eae448 100644 --- a/miio/integrations/chunmi/cooker/cooker.py +++ b/miio/integrations/chunmi/cooker/cooker.py @@ -125,13 +125,13 @@ def __init__(self, data: str): self.data = [int(data[i : i + 2], 16) for i in range(0, len(data), 2)] else: self.data = [] - @property + @property @sensor("Temperatures", unit="°C", icon="mdi:thermometer") def temperatures(self) -> list[int]: return self.data - @property + @property @sensor("Raw", icon="mdi:code-string") def raw(self) -> str: return "".join([f"{value:02x}" for value in self.data]) @@ -166,33 +166,33 @@ def __init__(self, custom: str): Octet 11-16 (01 00 00 00 1d 1f): Meaning unknown """ self.custom = [int(custom[i : i + 2], 16) for i in range(0, len(custom), 2)] - @property + @property @sensor("Jingzhu Appointment", icon="mdi:clock-outline") def jingzhu_appointment(self) -> time: return time(hour=self.custom[0], minute=self.custom[1]) - @property + @property @sensor("Kuaizhu Appointment", icon="mdi:clock-outline") def kuaizhu_appointment(self) -> time: return time(hour=self.custom[2], minute=self.custom[3]) - @property + @property @sensor("Zhuzhou Appointment", icon="mdi:clock-outline") def zhuzhou_appointment(self) -> time: return time(hour=self.custom[4], minute=self.custom[5]) - @property + @property @sensor("Zhuzhou Cooking", icon="mdi:clock-outline") def zhuzhou_cooking(self) -> time: return time(hour=self.custom[6], minute=self.custom[7]) - @property + @property @sensor("Favorite Appointment", icon="mdi:clock-outline") def favorite_appointment(self) -> time: return time(hour=self.custom[8], minute=self.custom[9]) - @property + @property @sensor("Favorite Cooking", icon="mdi:clock-outline") def favorite_cooking(self) -> time: return time(hour=self.custom[10], minute=self.custom[11]) @@ -215,8 +215,8 @@ def __init__(self, stage: str): Octet 5 (ff): Meaning unknown. """ self.stage = stage - @property + @property @sensor("State", icon="mdi:pot-steam") def state(self) -> int: """ @@ -226,18 +226,18 @@ def state(self) -> int: 12: Cooking finished """ return int(self.stage[0:2], 16) - @property + @property @sensor("Rice ID", icon="mdi:rice") def rice_id(self) -> int: return int(self.stage[2:6], 16) - @property + @property @sensor("Taste", icon="mdi:food-variant") def taste(self) -> int: return int(self.stage[6:8], 16) - @property + @property @sensor("Taste Phase", icon="mdi:food-variant") def taste_phase(self) -> int: phase = int(self.taste / 33) @@ -245,24 +245,24 @@ def taste_phase(self) -> int: if phase > 2: return 2 return phase - @property + @property @sensor("Name", icon="mdi:label") def name(self) -> str: try: return COOKING_STAGES[self.state]["name"] except KeyError: return "Unknown stage" - @property + @property @sensor("Description", icon="mdi:text") def description(self) -> str: try: return COOKING_STAGES[self.state]["description"] except KeyError: return "" - @property + @property @sensor("Raw", icon="mdi:code-string") def raw(self) -> str: return self.stage @@ -284,8 +284,8 @@ def __init__(self, timeouts: Optional[str] = None): self.timeouts = [ int(timeouts[i : i + 2], 16) for i in range(0, len(timeouts), 2) ] - @property + @property @sensor("LED Off Timeout", unit="s", icon="mdi:led-off") def led_off(self) -> int: return self.timeouts[0] @@ -293,8 +293,8 @@ def led_off(self) -> int: @led_off.setter def led_off(self, delay: int): self.timeouts[0] = delay - @property + @property @sensor("Lid Open Timeout", unit="s", icon="mdi:pot-steam-outline") def lid_open(self) -> int: return self.timeouts[1] @@ -302,8 +302,8 @@ def lid_open(self) -> int: @lid_open.setter def lid_open(self, timeout: int): self.timeouts[1] = timeout - @property + @property @sensor("Lid Open Warning Timeout", unit="s", icon="mdi:alert") def lid_open_warning(self) -> int: return self.timeouts[2] @@ -342,8 +342,8 @@ def __init__(self, settings: Optional[str] = None): self._settings = [ int(settings[i : i + 2], 16) for i in range(0, len(settings), 2) ] - @property + @property @sensor("Pressure Supported", icon="mdi:gauge") def pressure_supported(self) -> bool: return self._settings[0] & 1 != 0 @@ -354,8 +354,8 @@ def pressure_supported(self, supported: bool): self._settings[0] |= 1 else: self._settings[0] &= 254 - @property + @property @sensor("LED On", icon="mdi:led-on") def led_on(self) -> bool: return self._settings[0] & 2 != 0 @@ -366,8 +366,8 @@ def led_on(self, on: bool): self._settings[0] |= 2 else: self._settings[0] &= 253 - @property + @property @sensor("Auto Keep Warm", icon="mdi:pot-steam") def auto_keep_warm(self) -> bool: return self._settings[0] & 4 != 0 @@ -378,8 +378,8 @@ def auto_keep_warm(self, keep_warm: bool): self._settings[0] |= 4 else: self._settings[0] &= 251 - @property + @property @sensor("Lid Open Warning", icon="mdi:alert") def lid_open_warning(self) -> bool: return self._settings[0] & 8 != 0 @@ -390,8 +390,8 @@ def lid_open_warning(self, alarm: bool): self._settings[0] |= 8 else: self._settings[0] &= 247 - @property + @property @sensor("Lid Open Warning Delayed", icon="mdi:alert-outline") def lid_open_warning_delayed(self) -> bool: return self._settings[0] & 16 != 0 @@ -402,8 +402,8 @@ def lid_open_warning_delayed(self, alarm: bool): self._settings[0] |= 16 else: self._settings[0] &= 239 - @property + @property @sensor("Jingzhu Auto Keep Warm", icon="mdi:pot-steam") def jingzhu_auto_keep_warm(self) -> bool: return self._settings[1] & 1 != 0 @@ -414,8 +414,8 @@ def jingzhu_auto_keep_warm(self, auto_keep_warm: bool): self._settings[1] |= 1 else: self._settings[1] &= 254 - @property + @property @sensor("Kuaizhu Auto Keep Warm", icon="mdi:pot-steam") def kuaizhu_auto_keep_warm(self) -> bool: return self._settings[1] & 2 != 0 @@ -426,8 +426,8 @@ def kuaizhu_auto_keep_warm(self, auto_keep_warm: bool): self._settings[1] |= 2 else: self._settings[1] &= 253 - @property + @property @sensor("Zhuzhou Auto Keep Warm", icon="mdi:pot-steam") def zhuzhou_auto_keep_warm(self) -> bool: return self._settings[1] & 4 != 0 @@ -438,8 +438,8 @@ def zhuzhou_auto_keep_warm(self, auto_keep_warm: bool): self._settings[1] |= 4 else: self._settings[1] &= 251 - @property + @property @sensor("Favorite Auto Keep Warm", icon="mdi:pot-steam") def favorite_auto_keep_warm(self) -> bool: return self._settings[1] & 8 != 0 @@ -497,20 +497,20 @@ def __init__(self, data): meal is ready: ['autokeepwarm', '0001', '1000000000', '031e0b23031e', '1', '750', '60', '0207', '05040f', '00030017', '0100', 'ffffffffffff011effff01000000535d'] """ self.data = data - @property + @property @sensor("Mode", icon="mdi:pot-steam") def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["func"]) - @property + @property @sensor("Menu", icon="mdi:book-open-variant") def menu(self) -> int: """Selected recipe id.""" return int(self.data["menu"], 16) - @property + @property @sensor("Stage", icon="mdi:pot-steam") def stage(self) -> Optional[CookingStage]: """Current stage if cooking.""" @@ -519,9 +519,11 @@ def stage(self) -> Optional[CookingStage]: return CookingStage(stage) return None - @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @property + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> Optional[int]: """Current temperature, if idle. @@ -532,8 +534,8 @@ def temperature(self) -> Optional[int]: return int(value) return None - @property + @property @sensor("Start Time", icon="mdi:clock-start") def start_time(self) -> Optional[time]: """Start time of cooking? @@ -546,14 +548,14 @@ def start_time(self) -> Optional[time]: return time(hour=int(value[4:6], 16), minute=int(value[6:8], 16)) return None - @property + @property @sensor("Remaining", unit="min", icon="mdi:timer-outline") def remaining(self) -> int: """Remaining minutes of the cooking process.""" return int(self.data["t_func"]) - @property + @property @sensor("Cooking Delayed", unit="min", icon="mdi:timer-sand") def cooking_delayed(self) -> Optional[int]: """Wait n minutes before cooking / scheduled cooking.""" @@ -563,38 +565,38 @@ def cooking_delayed(self) -> Optional[int]: return delay return None - @property + @property @sensor("Duration", unit="min", icon="mdi:timer-outline") def duration(self) -> int: """Duration of the cooking process.""" return int(self.data["t_cook"]) - @property + @property @sensor("Cooker Settings", icon="mdi:cog") def cooker_settings(self) -> CookerSettings: """Settings of the cooker.""" return CookerSettings(self.data["setting"]) - @property + @property @sensor("Interaction Timeouts", icon="mdi:timer-cog-outline") def interaction_timeouts(self) -> InteractionTimeouts: """Interaction timeouts.""" return InteractionTimeouts(self.data["delay"]) - @property + @property @sensor("Hardware Version", icon="mdi:chip") def hardware_version(self) -> int: """Hardware version.""" return int(self.data["version"][0:4], 16) - @property + @property @sensor("Firmware Version", icon="mdi:update") def firmware_version(self) -> int: """Firmware version.""" return int(self.data["version"][4:8], 16) - @property + @property @sensor("Favorite", icon="mdi:star") def favorite(self) -> int: """Favored recipe id. @@ -602,8 +604,8 @@ def favorite(self) -> int: Can be compared with the menu property. """ return int(self.data["favorite"], 16) - @property + @property @sensor("Custom", icon="mdi:tune") def custom(self) -> Optional[CookerCustomizations]: custom = self.data["custom"] diff --git a/miio/integrations/deerma/humidifier/airhumidifier_jsqs.py b/miio/integrations/deerma/humidifier/airhumidifier_jsqs.py index fe029b289..1be2c00ee 100644 --- a/miio/integrations/deerma/humidifier/airhumidifier_jsqs.py +++ b/miio/integrations/deerma/humidifier/airhumidifier_jsqs.py @@ -163,7 +163,11 @@ def water_shortage_fault(self) -> Optional[bool]: return self.data.get("water_shortage_fault") @property - @setting(name="Overwet Protect", setter_name="set_overwet_protect", icon="mdi:shield-check") + @setting( + name="Overwet Protect", + setter_name="set_overwet_protect", + icon="mdi:shield-check", + ) def overwet_protect(self) -> Optional[bool]: """Return True if overwet mode is active.""" return self.data.get("overwet_protect") diff --git a/miio/integrations/deerma/humidifier/airhumidifier_mjjsq.py b/miio/integrations/deerma/humidifier/airhumidifier_mjjsq.py index 2a59fd8a3..82b0e90b2 100644 --- a/miio/integrations/deerma/humidifier/airhumidifier_mjjsq.py +++ b/miio/integrations/deerma/humidifier/airhumidifier_mjjsq.py @@ -131,7 +131,9 @@ def water_tank_detached(self) -> bool: return self.data["watertankstatus"] == 0 @property - @setting(name="Wet Protection", setter_name="set_wet_protection", icon="mdi:shield-check") + @setting( + name="Wet Protection", setter_name="set_wet_protection", icon="mdi:shield-check" + ) def wet_protection(self) -> Optional[bool]: """True if wet protection is enabled.""" if self.data["wet_and_protect"] is not None: diff --git a/miio/integrations/dmaker/airfresh/airfresh_t2017.py b/miio/integrations/dmaker/airfresh/airfresh_t2017.py index 684cf2b0d..2dcc063a6 100644 --- a/miio/integrations/dmaker/airfresh/airfresh_t2017.py +++ b/miio/integrations/dmaker/airfresh/airfresh_t2017.py @@ -151,7 +151,9 @@ def co2(self) -> int: return self.data["co2"] @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> int: """Current temperature in degree celsions.""" return self.data["temperature_outside"] @@ -205,7 +207,9 @@ def ptc(self) -> bool: return self.data["ptc_on"] @property - @setting("PTC Level", setter_name="set_ptc_level", choices=PtcLevel, icon="mdi:radiator") + @setting( + "PTC Level", setter_name="set_ptc_level", choices=PtcLevel, icon="mdi:radiator" + ) def ptc_level(self) -> Optional[PtcLevel]: """PTC level.""" try: diff --git a/miio/integrations/dmaker/fan/fan.py b/miio/integrations/dmaker/fan/fan.py index b5e3f241a..4027fa012 100644 --- a/miio/integrations/dmaker/fan/fan.py +++ b/miio/integrations/dmaker/fan/fan.py @@ -105,7 +105,9 @@ def angle(self) -> int: return self.data["roll_angle"] @property - @sensor("Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration") + @sensor( + "Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration" + ) def delay_off_countdown(self) -> int: """Countdown until turning off in seconds.""" return self.data["time_off"] diff --git a/miio/integrations/dmaker/fan/fan_miot.py b/miio/integrations/dmaker/fan/fan_miot.py index b4d3d8502..af13bf7c4 100644 --- a/miio/integrations/dmaker/fan/fan_miot.py +++ b/miio/integrations/dmaker/fan/fan_miot.py @@ -210,7 +210,9 @@ def mode(self) -> OperationMode: return OperationMode[OperationModeMiot(self.data["mode"]).name] @property - @setting("Speed", setter_name="set_speed", min_value=0, max_value=100, icon="mdi:fan") + @setting( + "Speed", setter_name="set_speed", min_value=0, max_value=100, icon="mdi:fan" + ) def speed(self) -> int: """Speed of the motor.""" return self.data["fan_speed"] diff --git a/miio/integrations/dreame/vacuum/dreamevacuum_miot.py b/miio/integrations/dreame/vacuum/dreamevacuum_miot.py index 07fe38153..bba90e9ba 100644 --- a/miio/integrations/dreame/vacuum/dreamevacuum_miot.py +++ b/miio/integrations/dreame/vacuum/dreamevacuum_miot.py @@ -305,43 +305,43 @@ class DreameVacuumStatus(DeviceStatusContainer): def __init__(self, data, model): self.data = data self.model = model - @property + @property @sensor("Battery Level", unit="%", device_class="battery", icon="mdi:battery") def battery_level(self) -> str: return self.data["battery_level"] - @property + @property @sensor("Brush Left Time", unit="h", icon="mdi:brush") def brush_left_time(self) -> str: return self.data["brush_left_time"] - @property + @property @sensor("Side Brush Left Time", unit="h", icon="mdi:brush") def brush_left_time2(self) -> str: return self.data["brush_left_time2"] - @property + @property @sensor("Side Brush Life Level", unit="%", icon="mdi:brush") def brush_life_level2(self) -> str: return self.data["brush_life_level2"] - @property + @property @sensor("Brush Life Level", unit="%", icon="mdi:brush") def brush_life_level(self) -> str: return self.data["brush_life_level"] - @property + @property @sensor("Filter Left Time", unit="h", icon="mdi:filter-outline") def filter_left_time(self) -> str: return self.data["filter_left_time"] - @property + @property @sensor("Filter Life Level", unit="%", icon="mdi:filter-outline") def filter_life_level(self) -> str: return self.data["filter_life_level"] - @property + @property @sensor("Device Fault", icon="mdi:alert-circle") def device_fault(self) -> Optional[FaultStatus]: try: @@ -349,8 +349,8 @@ def device_fault(self) -> Optional[FaultStatus]: except ValueError: _LOGGER.error("Unknown FaultStatus (%s)", self.data["device_fault"]) return None - @property + @property @sensor("Charging State", icon="mdi:battery-charging") def charging_state(self) -> Optional[ChargingState]: try: @@ -358,8 +358,8 @@ def charging_state(self) -> Optional[ChargingState]: except ValueError: _LOGGER.error("Unknown ChargingStats (%s)", self.data["charging_state"]) return None - @property + @property @sensor("Operating Mode", icon="mdi:robot-vacuum") def operating_mode(self) -> Optional[OperatingMode]: try: @@ -367,8 +367,8 @@ def operating_mode(self) -> Optional[OperatingMode]: except ValueError: _LOGGER.error("Unknown OperatingMode (%s)", self.data["operating_mode"]) return None - @property + @property @sensor("Device Status", icon="mdi:robot-vacuum") def device_status(self) -> Optional[DeviceStatus]: try: @@ -376,73 +376,81 @@ def device_status(self) -> Optional[DeviceStatus]: except TypeError: _LOGGER.error("Unknown DeviceStatus (%s)", self.data["device_status"]) return None - @property + @property @sensor("Timer Enable", icon="mdi:timer") def timer_enable(self) -> str: return self.data["timer_enable"] - @property + @property @sensor("Start Time", icon="mdi:clock-start") def start_time(self) -> str: return self.data["start_time"] - @property + @property @sensor("Stop Time", icon="mdi:clock-end") def stop_time(self) -> str: return self.data["stop_time"] - @property + @property @sensor("Map View", icon="mdi:map") def map_view(self) -> str: return self.data["map_view"] - @property - @setting("Volume", setter_name="set_sound_volume", unit="%", min_value=0, max_value=100, step=1, icon="mdi:volume-high") + @property + @setting( + "Volume", + setter_name="set_sound_volume", + unit="%", + min_value=0, + max_value=100, + step=1, + icon="mdi:volume-high", + ) def volume(self) -> str: return self.data["volume"] - @property + @property @sensor("Voice Package", icon="mdi:account-voice") def voice_package(self) -> str: return self.data["voice_package"] - @property + @property @sensor("Timezone", icon="mdi:earth") def timezone(self) -> str: return self.data["timezone"] - @property + @property @sensor("Cleaning Time", unit="min", icon="mdi:timer-outline") def cleaning_time(self) -> str: return self.data["cleaning_time"] - @property + @property @sensor("Cleaning Area", unit="m²", icon="mdi:texture-box") def cleaning_area(self) -> str: return self.data["cleaning_area"] - @property + @property @sensor("First Clean Time", icon="mdi:clock-outline") def first_clean_time(self) -> str: return self.data["first_clean_time"] - @property + @property @sensor("Total Clean Time", unit="min", icon="mdi:timer-outline") def total_clean_time(self) -> str: return self.data["total_clean_time"] - @property + @property @sensor("Total Clean Times", icon="mdi:counter") def total_clean_times(self) -> str: return self.data["total_clean_times"] - @property + @property @sensor("Total Clean Area", unit="m²", icon="mdi:texture-box") def total_clean_area(self) -> str: return self.data["total_clean_area"] - @property + @property @setting("Cleaning Mode", setter_name="set_fan_speed", icon="mdi:fan") def cleaning_mode(self): cleaning_mode = self.data["cleaning_mode"] @@ -456,25 +464,27 @@ def cleaning_mode(self): except ValueError: _LOGGER.error(f"Unknown CleaningMode ({cleaning_mode})") return None - @property + @property @sensor("Life Sieve", unit="%", icon="mdi:filter-outline") def life_sieve(self) -> Optional[str]: return self.data.get("life_sieve") - @property + @property @sensor("Life Brush Side", unit="%", icon="mdi:brush") def life_brush_side(self) -> Optional[str]: return self.data.get("life_brush_side") - @property + @property @sensor("Life Brush Main", unit="%", icon="mdi:brush") def life_brush_main(self) -> Optional[str]: return self.data.get("life_brush_main") # TODO: get/set water flow for Dreame 1C @property - @setting("Water Flow", setter_name="set_waterflow", choices=WaterFlow, icon="mdi:water") + @setting( + "Water Flow", setter_name="set_waterflow", choices=WaterFlow, icon="mdi:water" + ) def water_flow(self) -> Optional[WaterFlow]: try: water_flow = self.data["water_flow"] @@ -485,8 +495,8 @@ def water_flow(self) -> Optional[WaterFlow]: except ValueError: _LOGGER.error("Unknown WaterFlow (%s)", self.data["water_flow"]) return None - @property + @property @sensor("Water Box Carriage Attached", icon="mdi:cup-water") def is_water_box_carriage_attached(self) -> Optional[bool]: """Return True if water box carriage (mop) is installed, None if sensor not diff --git a/miio/integrations/lumi/acpartner/airconditioningcompanion.py b/miio/integrations/lumi/acpartner/airconditioningcompanion.py index 946a98c19..837a52957 100644 --- a/miio/integrations/lumi/acpartner/airconditioningcompanion.py +++ b/miio/integrations/lumi/acpartner/airconditioningcompanion.py @@ -197,7 +197,12 @@ def is_on(self) -> bool: return self.power == "on" @property - @sensor("Target Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @sensor( + "Target Temperature", + unit="°C", + device_class="temperature", + icon="mdi:thermometer", + ) def target_temperature(self) -> Optional[int]: """Target temperature.""" try: diff --git a/miio/integrations/lumi/acpartner/airconditioningcompanionMCN.py b/miio/integrations/lumi/acpartner/airconditioningcompanionMCN.py index f394da802..2960dda5b 100644 --- a/miio/integrations/lumi/acpartner/airconditioningcompanionMCN.py +++ b/miio/integrations/lumi/acpartner/airconditioningcompanionMCN.py @@ -63,7 +63,12 @@ def is_on(self) -> bool: return self.power == "on" @property - @setting("Mode", setter_name="send_command", icon="mdi:air-conditioner", choices=OperationMode) + @setting( + "Mode", + setter_name="send_command", + icon="mdi:air-conditioner", + choices=OperationMode, + ) def mode(self) -> Optional[OperationMode]: """Current operation mode.""" try: @@ -73,7 +78,13 @@ def mode(self) -> Optional[OperationMode]: return None @property - @setting("Target Temperature", setter_name="send_command", unit="°C", icon="mdi:thermometer", device_class="temperature") + @setting( + "Target Temperature", + setter_name="send_command", + unit="°C", + icon="mdi:thermometer", + device_class="temperature", + ) def target_temperature(self) -> Optional[int]: """Target temperature.""" try: @@ -92,7 +103,12 @@ def fan_speed(self) -> Optional[FanSpeed]: return None @property - @setting("Swing Mode", setter_name="send_command", icon="mdi:arrow-oscillating", choices=SwingMode) + @setting( + "Swing Mode", + setter_name="send_command", + icon="mdi:arrow-oscillating", + choices=SwingMode, + ) def swing_mode(self) -> Optional[SwingMode]: """Current swing mode.""" try: diff --git a/miio/integrations/lumi/curtain/curtain_youpin.py b/miio/integrations/lumi/curtain/curtain_youpin.py index db6b96ad3..25742b1a5 100644 --- a/miio/integrations/lumi/curtain/curtain_youpin.py +++ b/miio/integrations/lumi/curtain/curtain_youpin.py @@ -80,7 +80,9 @@ def status(self) -> Status: return Status(self.data["status"]) @property - @setting("Manual Enabled", setter_name="set_manual_enabled", icon="mdi:hand-back-left") + @setting( + "Manual Enabled", setter_name="set_manual_enabled", icon="mdi:hand-back-left" + ) def is_manual_enabled(self) -> bool: """True if manual controls are enabled.""" return bool(self.data["is_manual_enabled"]) @@ -97,13 +99,19 @@ def polarity(self) -> Polarity: return Polarity(self.data["polarity"]) @property - @setting("Position Limited", setter_name="set_position_limit", icon="mdi:arrow-collapse-horizontal") + @setting( + "Position Limited", + setter_name="set_position_limit", + icon="mdi:arrow-collapse-horizontal", + ) def is_position_limited(self) -> bool: """Position limit.""" return bool(self.data["is_position_limited"]) @property - @setting("Night Tip Light", setter_name="set_night_tip_light", icon="mdi:lightbulb-night") + @setting( + "Night Tip Light", setter_name="set_night_tip_light", icon="mdi:lightbulb-night" + ) def night_tip_light(self) -> bool: """Night tip light status.""" return bool(self.data["night_tip_light"]) diff --git a/miio/integrations/mijia/vacuum/g1vacuum.py b/miio/integrations/mijia/vacuum/g1vacuum.py index 6d5233236..df7bdaf4d 100644 --- a/miio/integrations/mijia/vacuum/g1vacuum.py +++ b/miio/integrations/mijia/vacuum/g1vacuum.py @@ -158,26 +158,26 @@ def __init__(self, data): ] """ self.data = data - @property + @property @sensor("Battery", unit="%", device_class="battery", icon="mdi:battery") def battery(self) -> int: """Battery Level.""" return self.data["battery"] - @property + @property @sensor("Charge State", icon="mdi:battery-charging") def charge_state(self) -> G1ChargeState: """Charging State.""" return G1ChargeState(self.data["charge_state"]) - @property + @property @sensor("Error Code", icon="mdi:alert-circle") def error_code(self) -> int: """Error code as returned by the device.""" return int(self.data["error_code"]) - @property + @property @sensor("Error", icon="mdi:alert-circle") def error(self) -> str: """Human readable error description, see also :func:`error_code`.""" @@ -185,80 +185,82 @@ def error(self) -> str: return ERROR_CODES[self.error_code] except KeyError: return "Definition missing for error %s" % self.error_code - @property + @property @sensor("State", icon="mdi:robot-vacuum") def state(self) -> G1State: """Vacuum Status.""" return G1State(self.data["state"]) - @property - @setting("Fan Speed", setter_name="set_fan_speed", choices=G1FanSpeed, icon="mdi:fan") + @property + @setting( + "Fan Speed", setter_name="set_fan_speed", choices=G1FanSpeed, icon="mdi:fan" + ) def fan_speed(self) -> G1FanSpeed: """Fan Speed.""" return G1FanSpeed(self.data["fan_speed"]) - @property + @property @sensor("Operating Mode", icon="mdi:robot-vacuum") def operating_mode(self) -> G1VacuumMode: """Operating Mode.""" return G1VacuumMode(self.data["operating_mode"]) - @property + @property @sensor("Mop State", icon="mdi:robot-vacuum-variant") def mop_state(self) -> G1MopState: """Mop State.""" return G1MopState(self.data["mop_state"]) - @property + @property @sensor("Water Level", icon="mdi:water") def water_level(self) -> G1WaterLevel: """Water Level.""" return G1WaterLevel(self.data["water_level"]) - @property + @property @sensor("Main Brush Life Level", unit="%", icon="mdi:brush") def main_brush_life_level(self) -> int: """Main Brush Life Level in %.""" return self.data["main_brush_life_level"] - @property + @property @sensor("Main Brush Time Left", icon="mdi:brush") def main_brush_time_left(self) -> timedelta: """Main Brush Remaining Time in Minutes.""" return timedelta(minutes=self.data["main_brush_time_left"]) - @property + @property @sensor("Side Brush Life Level", unit="%", icon="mdi:brush") def side_brush_life_level(self) -> int: """Side Brush Life Level in %.""" return self.data["side_brush_life_level"] - @property + @property @sensor("Side Brush Time Left", icon="mdi:brush") def side_brush_time_left(self) -> timedelta: """Side Brush Remaining Time in Minutes.""" return timedelta(minutes=self.data["side_brush_time_left"]) - @property + @property @sensor("Filter Life Level", unit="%", icon="mdi:filter-outline") def filter_life_level(self) -> int: """Filter Life Level in %.""" return self.data["filter_life_level"] - @property + @property @sensor("Filter Time Left", icon="mdi:filter-outline") def filter_time_left(self) -> timedelta: """Filter remaining time.""" return timedelta(minutes=self.data["filter_time_left"]) - @property + @property @sensor("Clean Area", unit="cm²", icon="mdi:texture-box") def clean_area(self) -> int: """Clean Area in cm2.""" return self.data["clean_area"] - @property + @property @sensor("Clean Time", icon="mdi:timer-outline") def clean_time(self) -> timedelta: """Clean time.""" @@ -279,20 +281,20 @@ class G1CleaningSummary(DeviceStatus): def __init__(self, data) -> None: self.data = data - @property + @property @sensor("Total Clean Count", icon="mdi:counter") def total_clean_count(self) -> int: """Total Number of Cleanings.""" return self.data["total_clean_count"] - @property + @property @sensor("Total Clean Area", unit="m²", icon="mdi:texture-box") def total_clean_area(self) -> int: """Total Area Cleaned in m2.""" return self.data["total_clean_area"] - @property + @property @sensor("Total Clean Time", icon="mdi:timer-outline") def total_clean_time(self) -> timedelta: """Total Cleaning Time.""" diff --git a/miio/integrations/nwt/dehumidifier/airdehumidifier.py b/miio/integrations/nwt/dehumidifier/airdehumidifier.py index 6a3412265..cfc3470a4 100644 --- a/miio/integrations/nwt/dehumidifier/airdehumidifier.py +++ b/miio/integrations/nwt/dehumidifier/airdehumidifier.py @@ -90,7 +90,9 @@ def mode(self) -> OperationMode: return OperationMode(self.data["mode"]) @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> Optional[float]: """Current temperature, if available.""" if "temp" in self.data and self.data["temp"] is not None: diff --git a/miio/integrations/philips/light/philips_bulb.py b/miio/integrations/philips/light/philips_bulb.py index d305dc227..57aa4af82 100644 --- a/miio/integrations/philips/light/philips_bulb.py +++ b/miio/integrations/philips/light/philips_bulb.py @@ -48,7 +48,9 @@ def is_on(self) -> bool: return self.power == "on" @property - @setting("Brightness", setter_name="set_brightness", unit="%", icon="mdi:brightness-6") + @setting( + "Brightness", setter_name="set_brightness", unit="%", icon="mdi:brightness-6" + ) def brightness(self) -> Optional[int]: if "bright" in self.data: return self.data["bright"] @@ -57,7 +59,9 @@ def brightness(self) -> Optional[int]: return None @property - @setting("Color Temperature", setter_name="set_color_temperature", icon="mdi:palette") + @setting( + "Color Temperature", setter_name="set_color_temperature", icon="mdi:palette" + ) def color_temperature(self) -> Optional[int]: if "cct" in self.data: return self.data["cct"] @@ -71,7 +75,12 @@ def scene(self) -> Optional[int]: return None @property - @sensor("Delay Off Countdown", unit="s", icon="mdi:timer-outline", device_class="duration") + @sensor( + "Delay Off Countdown", + unit="s", + icon="mdi:timer-outline", + device_class="duration", + ) def delay_off_countdown(self) -> int: return self.data["dv"] diff --git a/miio/integrations/philips/light/philips_eyecare.py b/miio/integrations/philips/light/philips_eyecare.py index f29faf680..45595f6ba 100644 --- a/miio/integrations/philips/light/philips_eyecare.py +++ b/miio/integrations/philips/light/philips_eyecare.py @@ -89,13 +89,22 @@ def scene(self) -> int: return self.data["scene_num"] @property - @setting("Smart Night Light", setter_name="smart_night_light_on", icon="mdi:weather-night") + @setting( + "Smart Night Light", + setter_name="smart_night_light_on", + icon="mdi:weather-night", + ) def smart_night_light(self) -> bool: """True if the smart night light mode is on.""" return self.data["bls"] == "on" @property - @sensor("Delay Off Countdown", unit="min", icon="mdi:timer-sand", device_class="duration") + @sensor( + "Delay Off Countdown", + unit="min", + icon="mdi:timer-sand", + device_class="duration", + ) def delay_off_countdown(self) -> int: """Countdown until turning off in minutes.""" return self.data["dvalue"] diff --git a/miio/integrations/philips/light/philips_rwread.py b/miio/integrations/philips/light/philips_rwread.py index b0dc5091a..86f29d249 100644 --- a/miio/integrations/philips/light/philips_rwread.py +++ b/miio/integrations/philips/light/philips_rwread.py @@ -87,7 +87,9 @@ def scene(self) -> int: return self.data["snm"] @property - @setting("Motion Detection", setter_name="set_motion_detection", icon="mdi:motion-sensor") + @setting( + "Motion Detection", setter_name="set_motion_detection", icon="mdi:motion-sensor" + ) def motion_detection(self) -> bool: """True if motion detection is enabled.""" return self.data["flm"] == 1 diff --git a/miio/integrations/roidmi/vacuum/roidmivacuum_miot.py b/miio/integrations/roidmi/vacuum/roidmivacuum_miot.py index 301a149b8..6cdac0d2a 100644 --- a/miio/integrations/roidmi/vacuum/roidmivacuum_miot.py +++ b/miio/integrations/roidmi/vacuum/roidmivacuum_miot.py @@ -223,20 +223,20 @@ def __init__(self, data): ] """ self.data = data - @property + @property @sensor("Battery", unit="%", device_class="battery", icon="mdi:battery") def battery(self) -> int: """Remaining battery in percentage.""" return self.data["battery_level"] - @property + @property @sensor("Error Code", icon="mdi:alert-circle") def error_code(self) -> int: """Error code as returned by the device.""" return int(self.data["error_code"]) - @property + @property @sensor("Error", icon="mdi:alert-circle") def error(self) -> str: """Human readable error description, see also :func:`error_code`.""" @@ -244,8 +244,8 @@ def error(self) -> str: return error_codes[self.error_code] except KeyError: return "Definition missing for error %s" % self.error_code - @property + @property @sensor("Charging State", icon="mdi:battery-charging") def charging_state(self) -> ChargingState: """Charging state (Charging/Discharging)""" @@ -254,8 +254,8 @@ def charging_state(self) -> ChargingState: except ValueError: _LOGGER.error("Unknown ChargingStats (%s)", self.data["charging_state"]) return ChargingState.Unknown - @property + @property @sensor("Sweep Mode", icon="mdi:robot-vacuum") def sweep_mode(self) -> SweepMode: """Sweep mode point/area/total etc.""" @@ -264,8 +264,8 @@ def sweep_mode(self) -> SweepMode: except ValueError: _LOGGER.error("Unknown SweepMode (%s)", self.data["sweep_mode"]) return SweepMode.Unknown - @property + @property @setting("Fan Speed", setter_name="set_fanspeed", choices=FanSpeed, icon="mdi:fan") def fan_speed(self) -> FanSpeed: """Current fan speed.""" @@ -274,9 +274,14 @@ def fan_speed(self) -> FanSpeed: except ValueError: _LOGGER.error("Unknown FanSpeed (%s)", self.data["fanspeed_mode"]) return FanSpeed.Unknown - @property - @setting("Sweep Type", setter_name="set_sweep_type", choices=SweepType, icon="mdi:robot-vacuum") + @property + @setting( + "Sweep Type", + setter_name="set_sweep_type", + choices=SweepType, + icon="mdi:robot-vacuum", + ) def sweep_type(self) -> SweepType: """Current sweep type sweep/mop/sweep&mop.""" try: @@ -284,9 +289,14 @@ def sweep_type(self) -> SweepType: except ValueError: _LOGGER.error("Unknown SweepType (%s)", self.data["sweep_type"]) return SweepType.Unknown - @property - @setting("Path Mode", setter_name="set_path_mode", choices=PathMode, icon="mdi:map-marker-path") + @property + @setting( + "Path Mode", + setter_name="set_path_mode", + choices=PathMode, + icon="mdi:map-marker-path", + ) def path_mode(self) -> PathMode: """Current path-mode: normal/y-mopping etc.""" try: @@ -294,23 +304,30 @@ def path_mode(self) -> PathMode: except ValueError: _LOGGER.error("Unknown PathMode (%s)", self.data["path_mode"]) return PathMode.Unknown - @property + @property @sensor("Mop Attached", icon="mdi:robot-vacuum-variant") def is_mop_attached(self) -> bool: """Return True if mop is attached.""" return self.data["mop_present"] - @property - @setting("Dust Collection Frequency", setter_name="set_dust_collection_frequency", min_value=0, max_value=3, step=1, icon="mdi:delete-variant") + @property + @setting( + "Dust Collection Frequency", + setter_name="set_dust_collection_frequency", + min_value=0, + max_value=3, + step=1, + icon="mdi:delete-variant", + ) def dust_collection_frequency(self) -> int: """Frequency for emptying the dust bin. Example: 2 means the dust bin is emptied every second cleaning. """ return self.data["work_station_freq"] - @property + @property @setting("Timing", setter_name="set_timing", icon="mdi:clock-outline") def timing(self) -> str: """Repeated cleaning. @@ -347,8 +364,8 @@ def timing(self) -> str: tz/tzs= time-zone """ return self.data["timing"] - @property + @property @setting("Carpet Mode", setter_name="set_carpet_mode", icon="mdi:rug") def carpet_mode(self) -> bool: """Auto boost on carpet.""" @@ -374,15 +391,20 @@ def _seconds_to_components(val): end_minute=end[1], ) ) - @property + @property @sensor("DND Status", icon="mdi:minus-circle") def dnd_status(self) -> DNDStatus: """Returns do-not-disturb status.""" return self._parse_forbid_mode(self.data["forbid_mode"]) - @property - @setting("Water Level", setter_name="set_water_level", choices=WaterLevel, icon="mdi:water") + @property + @setting( + "Water Level", + setter_name="set_water_level", + choices=WaterLevel, + icon="mdi:water", + ) def water_level(self) -> WaterLevel: """Get current water level.""" try: @@ -390,39 +412,43 @@ def water_level(self) -> WaterLevel: except ValueError: _LOGGER.error("Unknown WaterLevel (%s)", self.data["water_level"]) return WaterLevel.Unknown - @property + @property @setting("Double Clean", setter_name="set_double_clean", icon="mdi:refresh") def double_clean(self) -> bool: """Is double clean enabled.""" return self.data["double_clean"] - @property + @property @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """Return True if led/display on vaccum is on.""" return self.data["led_switch"] - @property - @setting("Lidar Collision Sensor", setter_name="set_lidar_collision_sensor", icon="mdi:radar") + @property + @setting( + "Lidar Collision Sensor", + setter_name="set_lidar_collision_sensor", + icon="mdi:radar", + ) def is_lidar_collision_sensor(self) -> bool: """When ON, the robot will use lidar as the main detection sensor to help reduce collisions.""" return self.data["lidar_collision"] - @property + @property @sensor("Station Key", icon="mdi:gesture-tap-button") def station_key(self) -> bool: """When ON: long press the display will turn on dust collection.""" return self.data["station_key"] - @property + @property @setting("Station LED", setter_name="set_station_led", icon="mdi:led-on") def station_led(self) -> bool: """Return if station display is on.""" return self.data["station_led"] - @property + @property @sensor("Current Audio", icon="mdi:account-voice") def current_audio(self) -> str: """Current voice setting. @@ -430,26 +456,26 @@ def current_audio(self) -> str: E.g. 'girl_en' """ return self.data["current_audio"] - @property + @property @sensor("Clean Time", icon="mdi:timer-outline") def clean_time(self) -> timedelta: """Time used for cleaning (if finished, shows how long it took).""" return timedelta(seconds=self.data["clean_time_sec"]) - @property + @property @sensor("Clean Area", unit="m²", icon="mdi:texture-box") def clean_area(self) -> int: """Cleaned area in m2.""" return self.data["clean_area"] - @property + @property @sensor("State Code", icon="mdi:robot-vacuum") def state_code(self) -> int: """State code as returned by the device.""" return int(self.data["state"]) - @property + @property @sensor("State", icon="mdi:robot-vacuum") def state(self) -> RoidmiState: """Human readable state description, see also :func:`state_code`.""" @@ -458,32 +484,40 @@ def state(self) -> RoidmiState: except ValueError: _LOGGER.error("Unknown RoidmiState (%s)", self.state_code) return RoidmiState.Unknown - @property - @setting("Volume", setter_name="set_sound_volume", unit="%", min_value=0, max_value=100, step=1, icon="mdi:volume-high") + @property + @setting( + "Volume", + setter_name="set_sound_volume", + unit="%", + min_value=0, + max_value=100, + step=1, + icon="mdi:volume-high", + ) def volume(self) -> int: """Return device sound volumen level.""" return self.data["volume"] - @property + @property @setting("Muted", setter_name="set_sound_muted", icon="mdi:volume-off") def is_muted(self) -> bool: """True if device is muted.""" return bool(self.data["mute"]) - @property + @property @sensor("Is Paused", icon="mdi:pause-circle") def is_paused(self) -> bool: """Return True if vacuum is paused.""" return self.state in [RoidmiState.Paused, RoidmiState.FindChargerPause] - @property + @property @sensor("Is On", icon="mdi:robot-vacuum") def is_on(self) -> bool: """True if device is currently cleaning in any mode.""" return self.state == RoidmiState.Sweeping - @property + @property @sensor("Got Error", icon="mdi:alert-circle") def got_error(self) -> bool: """True if an error has occurred.""" @@ -495,20 +529,20 @@ class RoidmiCleaningSummary(DeviceStatus): def __init__(self, data) -> None: self.data = data - @property + @property @sensor("Total Duration", icon="mdi:timer-outline") def total_duration(self) -> timedelta: """Total cleaning duration.""" return timedelta(seconds=self.data["total_clean_time_sec"]) - @property + @property @sensor("Total Area", unit="m²", icon="mdi:texture-box") def total_area(self) -> int: """Total cleaned area.""" return self.data["total_clean_areas"] - @property + @property @sensor("Count", icon="mdi:counter") def count(self) -> int: """Number of cleaning runs.""" @@ -531,56 +565,56 @@ def _calcUsageTime( remaning_fraction = remaning_level / 100.0 original_total = renaning_time / remaning_fraction return original_total * (1 - remaning_fraction) - @property + @property @sensor("Filter Usage", icon="mdi:filter-outline") def filter(self) -> timedelta: """Filter usage time.""" return self._calcUsageTime(self.filter_left, self.data["filter_life_level"]) - @property + @property @sensor("Filter Left", icon="mdi:filter-outline") def filter_left(self) -> timedelta: """How long until the filter should be changed.""" return timedelta(minutes=self.data["filter_left_minutes"]) - @property + @property @sensor("Main Brush Usage", icon="mdi:brush") def main_brush(self) -> timedelta: """Main brush usage time.""" return self._calcUsageTime( self.main_brush_left, self.data["main_brush_life_level"] ) - @property + @property @sensor("Main Brush Left", icon="mdi:brush") def main_brush_left(self) -> timedelta: """How long until the main brush should be changed.""" return timedelta(minutes=self.data["main_brush_left_minutes"]) - @property + @property @sensor("Side Brush Usage", icon="mdi:brush") def side_brush(self) -> timedelta: """Main brush usage time.""" return self._calcUsageTime( self.side_brush_left, self.data["side_brushes_life_level"] ) - @property + @property @sensor("Side Brush Left", icon="mdi:brush") def side_brush_left(self) -> timedelta: """How long until the side brushes should be changed.""" return timedelta(minutes=self.data["side_brushes_left_minutes"]) - @property + @property @sensor("Sensor Dirty", icon="mdi:eye-off") def sensor_dirty(self) -> timedelta: """Return time since last sensor clean.""" return self._calcUsageTime( self.sensor_dirty_left, self.data["sensor_dirty_remaning_level"] ) - @property + @property @sensor("Sensor Dirty Left", icon="mdi:eye-off") def sensor_dirty_left(self) -> timedelta: """How long until the sensors should be cleaned.""" diff --git a/miio/integrations/tinymu/toiletlid/toiletlid.py b/miio/integrations/tinymu/toiletlid/toiletlid.py index 4069e1f25..74d94f7cd 100644 --- a/miio/integrations/tinymu/toiletlid/toiletlid.py +++ b/miio/integrations/tinymu/toiletlid/toiletlid.py @@ -65,13 +65,23 @@ def filter_use_percentage(self) -> str: return "{}%".format(self.data["filter_use_flux"]) @property - @sensor("Filter Remaining Time", unit="d", icon="mdi:filter-outline", device_class="duration") + @sensor( + "Filter Remaining Time", + unit="d", + icon="mdi:filter-outline", + device_class="duration", + ) def filter_remaining_time(self) -> int: """Filter remaining life days.""" return self.data["filter_use_time"] @property - @setting("Ambient Light", setter_name="set_ambient_light", icon="mdi:lightbulb-variant", choices=AmbientLightColor) + @setting( + "Ambient Light", + setter_name="set_ambient_light", + icon="mdi:lightbulb-variant", + choices=AmbientLightColor, + ) def ambient_light(self) -> str: """Ambient light color.""" return self.data["ambient_light"] diff --git a/miio/integrations/xiaomi/aircondition/airconditioner_miot.py b/miio/integrations/xiaomi/aircondition/airconditioner_miot.py index 4875ccdd9..3666c29e4 100644 --- a/miio/integrations/xiaomi/aircondition/airconditioner_miot.py +++ b/miio/integrations/xiaomi/aircondition/airconditioner_miot.py @@ -79,26 +79,26 @@ def __init__(self, status: str): Only write 1 or 0 to it would start or abort the auto clean mode. """ self.status = [int(value) for value in status.split(",")] - @property + @property @sensor("Cleaning", icon="mdi:broom") def cleaning(self) -> bool: return bool(self.status[0]) - @property + @property @sensor("Progress", unit="%", icon="mdi:progress-check") def progress(self) -> int: return int(self.status[1]) - @property + @property @sensor("Stage", icon="mdi:broom") def stage(self) -> str: try: return CLEANING_STAGES[self.status[2]] except KeyError: return "Unknown stage" - @property + @property @sensor("Cancellable", icon="mdi:cancel") def cancellable(self) -> bool: return bool(self.status[3]) @@ -143,23 +143,23 @@ def __init__(self, status): Also, if the countdown minutes set to 0, the timer would be disabled. """ self.status = [int(value) for value in status.split(",")] - @property + @property @sensor("Enabled", icon="mdi:timer") def enabled(self) -> bool: return bool(self.status[0]) - @property + @property @sensor("Countdown", icon="mdi:timer-outline") def countdown(self) -> timedelta: return timedelta(minutes=self.status[1]) - @property + @property @sensor("Power On", icon="mdi:power") def power_on(self) -> bool: return bool(self.status[2]) - @property + @property @sensor("Time Left", icon="mdi:timer-outline") def time_left(self) -> timedelta: return timedelta(minutes=self.status[3]) @@ -193,110 +193,136 @@ def __init__(self, data: dict[str, Any]) -> None: """ self.data = data - @property + @property @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if the device is turned on.""" return self.data["power"] - @property + @property @sensor("Power", icon="mdi:power") def power(self) -> str: """Current power state.""" return "on" if self.is_on else "off" - @property - @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:air-conditioner") + @property + @setting( + "Mode", + setter_name="set_mode", + choices=OperationMode, + icon="mdi:air-conditioner", + ) def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["mode"]) - @property - @setting("Target Temperature", setter_name="set_target_temperature", unit="°C", min_value=16, max_value=31, step=1, device_class="temperature", icon="mdi:thermometer") + @property + @setting( + "Target Temperature", + setter_name="set_target_temperature", + unit="°C", + min_value=16, + max_value=31, + step=1, + device_class="temperature", + icon="mdi:thermometer", + ) def target_temperature(self) -> float: """Target temperature in Celsius.""" return self.data["target_temperature"] - @property + @property @setting("ECO", setter_name="set_eco", icon="mdi:leaf") def eco(self) -> bool: """True if ECO mode is on.""" return self.data["eco"] - @property + @property @setting("Heater", setter_name="set_heater", icon="mdi:radiator") def heater(self) -> bool: """True if aux heat mode is on.""" return self.data["heater"] - @property + @property @setting("Dryer", setter_name="set_dryer", icon="mdi:water-off") def dryer(self) -> bool: """True if aux dryer mode is on.""" return self.data["dryer"] - @property + @property @setting("Sleep Mode", setter_name="set_sleep_mode", icon="mdi:sleep") def sleep_mode(self) -> bool: """True if sleep mode is on.""" return self.data["sleep_mode"] - @property + @property @setting("Fan Speed", setter_name="set_fan_speed", choices=FanSpeed, icon="mdi:fan") def fan_speed(self) -> FanSpeed: """Current Fan speed.""" return FanSpeed(self.data["fan_speed"]) - @property - @setting("Vertical Swing", setter_name="set_vertical_swing", icon="mdi:arrow-up-down") + @property + @setting( + "Vertical Swing", setter_name="set_vertical_swing", icon="mdi:arrow-up-down" + ) def vertical_swing(self) -> bool: """True if vertical swing is on.""" return self.data["vertical_swing"] - @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @property + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> float: """Current ambient temperature in Celsius.""" return self.data["temperature"] - @property + @property @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is on.""" return self.data["buzzer"] - @property + @property @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """True if LED is on.""" return self.data["led"] - @property + @property @sensor("Electricity", unit="kWh", device_class="energy", icon="mdi:flash") def electricity(self) -> float: """Power consumption accumulation in kWh.""" return self.data["electricity"] - @property + @property @sensor("Clean", icon="mdi:broom") def clean(self) -> CleaningStatus: """Auto clean mode indicator.""" return CleaningStatus(self.data["clean"]) - @property + @property @sensor("Total Running Duration", icon="mdi:timer-outline") def total_running_duration(self) -> timedelta: """Total running duration in hours.""" return timedelta(hours=self.data["running_duration"]) - @property - @setting("Fan Speed Percent", setter_name="set_fan_speed_percent", unit="%", min_value=1, max_value=101, step=1, icon="mdi:fan") + @property + @setting( + "Fan Speed Percent", + setter_name="set_fan_speed_percent", + unit="%", + min_value=1, + max_value=101, + step=1, + icon="mdi:fan", + ) def fan_speed_percent(self) -> int: """Current fan speed in percent.""" return self.data["fan_speed_percent"] - @property + @property @sensor("Timer", icon="mdi:timer") def timer(self) -> TimerStatus: """Countdown timer indicator.""" diff --git a/miio/integrations/xiaomi/repeater/wifirepeater.py b/miio/integrations/xiaomi/repeater/wifirepeater.py index 5813817e3..ba8346cae 100644 --- a/miio/integrations/xiaomi/repeater/wifirepeater.py +++ b/miio/integrations/xiaomi/repeater/wifirepeater.py @@ -38,7 +38,7 @@ def associated_stations(self) -> dict: return self.data["mat"] def __repr__(self) -> str: - s = "" % ( + s = "" % ( self.access_policy, len(self.associated_stations), ) diff --git a/miio/integrations/yeelight/dual_switch/yeelight_dual_switch.py b/miio/integrations/yeelight/dual_switch/yeelight_dual_switch.py index 019073b38..056249383 100644 --- a/miio/integrations/yeelight/dual_switch/yeelight_dual_switch.py +++ b/miio/integrations/yeelight/dual_switch/yeelight_dual_switch.py @@ -68,7 +68,11 @@ def switch_1_state(self) -> bool: return bool(self.data["switch_1_state"]) @property - @setting("Switch 1 Default State", setter_name="set_default_state", icon="mdi:toggle-switch-outline") + @setting( + "Switch 1 Default State", + setter_name="set_default_state", + icon="mdi:toggle-switch-outline", + ) def switch_1_default_state(self) -> bool: """First switch default state.""" return bool(self.data["switch_1_default_state"]) @@ -95,7 +99,11 @@ def switch_2_state(self) -> bool: return bool(self.data["switch_2_state"]) @property - @setting("Switch 2 Default State", setter_name="set_default_state", icon="mdi:toggle-switch-outline") + @setting( + "Switch 2 Default State", + setter_name="set_default_state", + icon="mdi:toggle-switch-outline", + ) def switch_2_default_state(self) -> bool: """Second switch default state.""" return bool(self.data["switch_2_default_state"]) diff --git a/miio/integrations/yunmi/waterpurifier/waterpurifier.py b/miio/integrations/yunmi/waterpurifier/waterpurifier.py index 3125cbb80..521a186ba 100644 --- a/miio/integrations/yunmi/waterpurifier/waterpurifier.py +++ b/miio/integrations/yunmi/waterpurifier/waterpurifier.py @@ -88,7 +88,9 @@ def usage(self) -> str: return self.data["usage"] @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> str: return self.data["temperature"] diff --git a/miio/integrations/yunmi/waterpurifier/waterpurifier_yunmi.py b/miio/integrations/yunmi/waterpurifier/waterpurifier_yunmi.py index aca37d56c..52a712821 100644 --- a/miio/integrations/yunmi/waterpurifier/waterpurifier_yunmi.py +++ b/miio/integrations/yunmi/waterpurifier/waterpurifier_yunmi.py @@ -94,8 +94,8 @@ def __init__(self, operation_status: int): for i in range(0, len(ERROR_DESCRIPTION)) if (1 << i) & operation_status ] - @property + @property @sensor("Errors", icon="mdi:alert-circle") def errors(self) -> list: return self.err_list @@ -119,146 +119,148 @@ def __init__(self, data: dict[str, Any]) -> None: 'filter3_flow_used': 1440, 'filter3_life_used': 3313} """ self.data = data - @property + @property @sensor("Operation Status", icon="mdi:information-outline") def operation_status(self) -> OperationStatus: """Current operation status.""" return OperationStatus(self.data["run_status"]) - @property + @property @sensor("Filter 1 Life Total", icon="mdi:filter-outline") def filter1_life_total(self) -> timedelta: """Filter1 total available time in hours.""" return timedelta(hours=self.data["f1_totaltime"]) - @property + @property @sensor("Filter 1 Life Used", icon="mdi:filter-outline") def filter1_life_used(self) -> timedelta: """Filter1 used time in hours.""" return timedelta(hours=self.data["f1_usedtime"]) - @property + @property @sensor("Filter 1 Life Remaining", icon="mdi:filter-outline") def filter1_life_remaining(self) -> timedelta: """Filter1 remaining time in hours.""" return self.filter1_life_total - self.filter1_life_used - @property + @property @sensor("Filter 1 Flow Total", unit="L", icon="mdi:water") def filter1_flow_total(self) -> int: """Filter1 total available flow in Metric Liter (L).""" return self.data["f1_totalflow"] - @property + @property @sensor("Filter 1 Flow Used", unit="L", icon="mdi:water") def filter1_flow_used(self) -> int: """Filter1 used flow in Metric Liter (L).""" return self.data["f1_usedflow"] - @property + @property @sensor("Filter 1 Flow Remaining", unit="L", icon="mdi:water") def filter1_flow_remaining(self) -> int: """Filter1 remaining flow in Metric Liter (L).""" return self.filter1_flow_total - self.filter1_flow_used - @property + @property @sensor("Filter 2 Life Total", icon="mdi:filter-outline") def filter2_life_total(self) -> timedelta: """Filter2 total available time in hours.""" return timedelta(hours=self.data["f2_totaltime"]) - @property + @property @sensor("Filter 2 Life Used", icon="mdi:filter-outline") def filter2_life_used(self) -> timedelta: """Filter2 used time in hours.""" return timedelta(hours=self.data["f2_usedtime"]) - @property + @property @sensor("Filter 2 Life Remaining", icon="mdi:filter-outline") def filter2_life_remaining(self) -> timedelta: """Filter2 remaining time in hours.""" return self.filter2_life_total - self.filter2_life_used - @property + @property @sensor("Filter 2 Flow Total", unit="L", icon="mdi:water") def filter2_flow_total(self) -> int: """Filter2 total available flow in Metric Liter (L).""" return self.data["f2_totalflow"] - @property + @property @sensor("Filter 2 Flow Used", unit="L", icon="mdi:water") def filter2_flow_used(self) -> int: """Filter2 used flow in Metric Liter (L).""" return self.data["f2_usedflow"] - @property + @property @sensor("Filter 2 Flow Remaining", unit="L", icon="mdi:water") def filter2_flow_remaining(self) -> int: """Filter2 remaining flow in Metric Liter (L).""" return self.filter2_flow_total - self.filter2_flow_used - @property + @property @sensor("Filter 3 Life Total", icon="mdi:filter-outline") def filter3_life_total(self) -> timedelta: """Filter3 total available time in hours.""" return timedelta(hours=self.data["f3_totaltime"]) - @property + @property @sensor("Filter 3 Life Used", icon="mdi:filter-outline") def filter3_life_used(self) -> timedelta: """Filter3 used time in hours.""" return timedelta(hours=self.data["f3_usedtime"]) - @property + @property @sensor("Filter 3 Life Remaining", icon="mdi:filter-outline") def filter3_life_remaining(self) -> timedelta: """Filter3 remaining time in hours.""" return self.filter3_life_total - self.filter3_life_used - @property + @property @sensor("Filter 3 Flow Total", unit="L", icon="mdi:water") def filter3_flow_total(self) -> int: """Filter3 total available flow in Metric Liter (L).""" return self.data["f3_totalflow"] - @property + @property @sensor("Filter 3 Flow Used", unit="L", icon="mdi:water") def filter3_flow_used(self) -> int: """Filter3 used flow in Metric Liter (L).""" return self.data["f3_usedflow"] - @property + @property @sensor("Filter 3 Flow Remaining", unit="L", icon="mdi:water") def filter3_flow_remaining(self) -> int: """Filter1 remaining flow in Metric Liter (L).""" return self.filter3_flow_total - self.filter3_flow_used - @property + @property @sensor("TDS In", unit="ppm", icon="mdi:water-check") def tds_in(self) -> int: """TDS value of input water.""" return self.data["tds_in"] - @property + @property @sensor("TDS Out", unit="ppm", icon="mdi:water-check") def tds_out(self) -> int: """TDS value of output water.""" return self.data["tds_out"] - @property + @property @sensor("Rinse", icon="mdi:water-sync") def rinse(self) -> bool: """True if the device is rinsing.""" return self.data["rinse"] - @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @property + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> int: """Current water temperature in Celsius.""" return self.data["temperature"] - @property + @property @sensor("TDS Warning Threshold", unit="ppm", icon="mdi:water-alert") def tds_warn_thd(self) -> int: """TDS warning threshold.""" diff --git a/miio/integrations/zhimi/airpurifier/airfresh.py b/miio/integrations/zhimi/airpurifier/airfresh.py index ca0aa90a3..189aeb362 100644 --- a/miio/integrations/zhimi/airpurifier/airfresh.py +++ b/miio/integrations/zhimi/airpurifier/airfresh.py @@ -91,44 +91,44 @@ def __init__(self, data: dict[str, Any], model: str) -> None: self.data = data self.model = model - @property + @property @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] - @property + @property @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """Return True if device is on.""" return self.power == "on" - @property + @property @sensor("AQI", unit="μg/m³", icon="mdi:air-filter") def aqi(self) -> int: """Air quality index.""" return self.data["aqi"] - @property + @property @sensor("Average AQI", unit="μg/m³", icon="mdi:air-filter") def average_aqi(self) -> int: """Average of the air quality index.""" return self.data["average_aqi"] - @property + @property @sensor("CO2", unit="ppm", icon="mdi:molecule-co2", device_class="carbon_dioxide") def co2(self) -> int: """Carbon dioxide.""" return self.data["co2"] - @property + @property @sensor("Humidity", unit="%", device_class="humidity", icon="mdi:water-percent") def humidity(self) -> int: """Current humidity.""" return self.data["humidity"] - @property + @property @setting("PTC", setter_name="set_ptc", icon="mdi:radiator") def ptc(self) -> Optional[bool]: """Return True if PTC is on.""" @@ -136,9 +136,11 @@ def ptc(self) -> Optional[bool]: return self.data["ptc_state"] == "on" return None - @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @property + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> Optional[float]: """Current temperature, if available.""" if self.data["temp_dec"] is not None: @@ -148,30 +150,37 @@ def temperature(self) -> Optional[float]: return self.data["temp_dec"] / 10.0 return None - @property - @sensor("NTC Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @property + @sensor( + "NTC Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def ntc_temperature(self) -> Optional[float]: """Current ntc temperature, if available.""" if self.data["ntcT"] is not None: return self.data["ntcT"] return None - @property + @property @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:fan") def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["mode"]) - @property + @property @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """Return True if LED is on.""" return self.data["led"] == "on" - @property - @setting("LED Brightness", setter_name="set_led_brightness", choices=LedBrightness, icon="mdi:brightness-6") + @property + @setting( + "LED Brightness", + setter_name="set_led_brightness", + choices=LedBrightness, + icon="mdi:brightness-6", + ) def led_brightness(self) -> Optional[LedBrightness]: """Brightness of the LED.""" if self.data["led_level"] is not None: @@ -184,8 +193,8 @@ def led_brightness(self) -> Optional[LedBrightness]: return None return None - @property + @property @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> Optional[bool]: """Return True if buzzer is on.""" @@ -193,38 +202,38 @@ def buzzer(self) -> Optional[bool]: return self.data["buzzer"] == "on" return None - @property + @property @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Return True if child lock is on.""" return self.data["child_lock"] == "on" - @property + @property @sensor("Filter Life Remaining", unit="%", icon="mdi:filter-outline") def filter_life_remaining(self) -> int: """Time until the filter should be changed.""" return self.data["filter_life"] - @property + @property @sensor("Filter Hours Used", unit="h", icon="mdi:filter-outline") def filter_hours_used(self) -> int: """How long the filter has been in use.""" return self.data["f1_hour_used"] - @property + @property @sensor("Use Time", unit="s", icon="mdi:timer-sand") def use_time(self) -> int: """How long the device has been active in seconds.""" return self.data["use_time"] - @property + @property @sensor("Motor Speed", unit="rpm", icon="mdi:fan") def motor_speed(self) -> int: """Speed of the motor.""" return self.data["motor1_speed"] - @property + @property @setting("Extra Features", setter_name="set_extra_features", icon="mdi:star") def extra_features(self) -> Optional[int]: return self.data["app_extra"] diff --git a/miio/integrations/zhimi/airpurifier/airpurifier.py b/miio/integrations/zhimi/airpurifier/airpurifier.py index e9c1589b9..2250bb279 100644 --- a/miio/integrations/zhimi/airpurifier/airpurifier.py +++ b/miio/integrations/zhimi/airpurifier/airpurifier.py @@ -129,53 +129,57 @@ def __init__(self, data: dict[str, Any]) -> None: self.filter_type_util = FilterTypeUtil() self.data = data - @property + @property @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return self.data["power"] - @property + @property @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """Return True if device is on.""" return self.power == "on" - @property + @property @sensor("AQI", unit="μg/m³", icon="mdi:air-filter") def aqi(self) -> int: """Air quality index.""" return self.data["aqi"] - @property + @property @sensor("Average AQI", unit="μg/m³", icon="mdi:air-filter") def average_aqi(self) -> int: """Average of the air quality index.""" return self.data["average_aqi"] - @property + @property @sensor("Humidity", unit="%", device_class="humidity", icon="mdi:water-percent") def humidity(self) -> int: """Current humidity.""" return self.data["humidity"] - @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @property + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> Optional[float]: """Current temperature, if available.""" if self.data["temp_dec"] is not None: return self.data["temp_dec"] / 10.0 return None - @property - @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:air-purifier") + @property + @setting( + "Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:air-purifier" + ) def mode(self) -> OperationMode: """Current operation mode.""" return OperationMode(self.data["mode"]) - @property + @property @sensor("Sleep Mode", icon="mdi:sleep") def sleep_mode(self) -> Optional[SleepMode]: """Operation mode of the sleep state. @@ -186,15 +190,20 @@ def sleep_mode(self) -> Optional[SleepMode]: return SleepMode(self.data["sleep_mode"]) return None - @property + @property @setting("LED", setter_name="set_led", icon="mdi:led-on") def led(self) -> bool: """Return True if LED is on.""" return self.data["led"] == "on" - @property - @setting("LED Brightness", setter_name="set_led_brightness", choices=LedBrightness, icon="mdi:brightness-6") + @property + @setting( + "LED Brightness", + setter_name="set_led_brightness", + choices=LedBrightness, + icon="mdi:brightness-6", + ) def led_brightness(self) -> Optional[LedBrightness]: """Brightness of the LED.""" if self.data["led_b"] is not None: @@ -204,17 +213,19 @@ def led_brightness(self) -> Optional[LedBrightness]: return None return None - @property - @sensor("Illuminance", unit="lx", device_class="illuminance", icon="mdi:brightness-5") + @property + @sensor( + "Illuminance", unit="lx", device_class="illuminance", icon="mdi:brightness-5" + ) def illuminance(self) -> Optional[int]: """Environment illuminance level in lux [0-200]. Sensor value is updated only when device is turned on. """ return self.data["bright"] - @property + @property @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> Optional[bool]: """Return True if buzzer is on.""" @@ -222,112 +233,127 @@ def buzzer(self) -> Optional[bool]: return self.data["buzzer"] == "on" return None - @property + @property @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """Return True if child lock is on.""" return self.data["child_lock"] == "on" - @property - @setting("Favorite Level", setter_name="set_favorite_level", min_value=0, max_value=17, step=1, icon="mdi:star") + @property + @setting( + "Favorite Level", + setter_name="set_favorite_level", + min_value=0, + max_value=17, + step=1, + icon="mdi:star", + ) def favorite_level(self) -> int: """Return favorite level, which is used if the mode is ``favorite``.""" # Favorite level used when the mode is `favorite`. return self.data["favorite_level"] - @property + @property @sensor("Filter Life Remaining", unit="%", icon="mdi:filter-outline") def filter_life_remaining(self) -> int: """Time until the filter should be changed.""" return self.data["filter1_life"] - @property + @property @sensor("Filter Hours Used", unit="h", icon="mdi:filter-outline") def filter_hours_used(self) -> int: """How long the filter has been in use.""" return self.data["f1_hour_used"] - @property + @property @sensor("Use Time", unit="s", icon="mdi:timer-sand") def use_time(self) -> int: """How long the device has been active in seconds.""" return self.data["use_time"] - @property + @property @sensor("Purify Volume", unit="m³", icon="mdi:air-purifier") def purify_volume(self) -> int: """The volume of purified air in cubic meter.""" return self.data["purify_volume"] - @property + @property @sensor("Motor Speed", unit="rpm", icon="mdi:fan") def motor_speed(self) -> int: """Speed of the motor.""" return self.data["motor1_speed"] - @property + @property @sensor("Motor 2 Speed", unit="rpm", icon="mdi:fan") def motor2_speed(self) -> Optional[int]: """Speed of the 2nd motor.""" return self.data["motor2_speed"] - @property - @setting("Volume", setter_name="set_volume", unit="%", min_value=0, max_value=100, step=1, icon="mdi:volume-high") + @property + @setting( + "Volume", + setter_name="set_volume", + unit="%", + min_value=0, + max_value=100, + step=1, + icon="mdi:volume-high", + ) def volume(self) -> Optional[int]: """Volume of sound notifications [0-100].""" return self.data["volume"] - @property + @property @sensor("Filter RFID Product ID", icon="mdi:barcode") def filter_rfid_product_id(self) -> Optional[str]: """RFID product ID of installed filter.""" return self.data["rfid_product_id"] - @property + @property @sensor("Filter RFID Tag", icon="mdi:barcode") def filter_rfid_tag(self) -> Optional[str]: """RFID tag ID of installed filter.""" return self.data["rfid_tag"] - @property + @property @sensor("Filter Type", icon="mdi:filter-outline") def filter_type(self) -> Optional[FilterType]: """Type of installed filter.""" return self.filter_type_util.determine_filter_type( self.filter_rfid_tag, self.filter_rfid_product_id ) - @property + @property @setting("Learn Mode", setter_name="set_learn_mode", icon="mdi:school") def learn_mode(self) -> bool: """Return True if Learn Mode is enabled.""" return self.data["act_sleep"] == "single" - @property + @property @sensor("Sleep Time", unit="s", icon="mdi:sleep") def sleep_time(self) -> Optional[int]: return self.data["sleep_time"] - @property + @property @sensor("Sleep Mode Learn Count", icon="mdi:counter") def sleep_mode_learn_count(self) -> Optional[int]: return self.data["sleep_data_num"] - @property + @property @setting("Extra Features", setter_name="set_extra_features", icon="mdi:star") def extra_features(self) -> Optional[int]: return self.data["app_extra"] - @property + @property @sensor("Turbo Mode Supported", icon="mdi:rocket") def turbo_mode_supported(self) -> Optional[bool]: if self.data["app_extra"] is not None: return self.data["app_extra"] == 1 return None - @property + @property @setting("Auto Detect", setter_name="set_auto_detect", icon="mdi:eye") def auto_detect(self) -> Optional[bool]: """Return True if auto detect is enabled.""" @@ -335,8 +361,8 @@ def auto_detect(self) -> Optional[bool]: return self.data["act_det"] == "on" return None - @property + @property @sensor("Button Pressed", icon="mdi:gesture-tap-button") def button_pressed(self) -> Optional[str]: """Last pressed button.""" diff --git a/miio/integrations/zhimi/fan/zhimi_miot.py b/miio/integrations/zhimi/fan/zhimi_miot.py index 4ea3b20bb..1257d8096 100644 --- a/miio/integrations/zhimi/fan/zhimi_miot.py +++ b/miio/integrations/zhimi/fan/zhimi_miot.py @@ -81,20 +81,20 @@ def __init__(self, data: dict[str, Any]) -> None: {'code': 0, 'did': 'temperature', 'piid': 7, 'siid': 7, 'value': 26.4}, """ self.data = data - @property + @property @setting("Ionizer", setter_name="set_ionizer", icon="mdi:atom") def ionizer(self) -> bool: """True if negative ions generation is enabled.""" return self.data["anion"] - @property + @property @sensor("Battery Supported", icon="mdi:battery") def battery_supported(self) -> bool: """True if battery is supported.""" return self.data["battery_supported"] - @property + @property @sensor("Buttons Pressed", icon="mdi:gesture-tap-button") def buttons_pressed(self) -> str: """What buttons on the fan are pressed now.""" @@ -106,21 +106,28 @@ def buttons_pressed(self) -> str: if code == 2: return "Swing" return "Unknown" - @property + @property @setting("Buzzer", setter_name="set_buzzer", icon="mdi:volume-high") def buzzer(self) -> bool: """True if buzzer is turned on.""" return self.data["buzzer"] - @property + @property @setting("Child Lock", setter_name="set_child_lock", icon="mdi:lock") def child_lock(self) -> bool: """True if child lock if on.""" return self.data["child_lock"] - @property - @setting("Fan Level", setter_name="set_fan_speed", min_value=1, max_value=4, step=1, icon="mdi:fan") + @property + @setting( + "Fan Level", + setter_name="set_fan_speed", + min_value=1, + max_value=4, + step=1, + icon="mdi:fan", + ) def fan_level(self) -> int: """Fan level (1-4).""" return self.data["fan_level"] @@ -130,75 +137,98 @@ def fan_level(self) -> int: def fan_speed(self) -> int: """Fan speed (1-100).""" return self.speed - @property - @setting("Speed", setter_name="set_speed", min_value=1, max_value=100, step=1, unit="%", icon="mdi:fan") + @property + @setting( + "Speed", + setter_name="set_speed", + min_value=1, + max_value=100, + step=1, + unit="%", + icon="mdi:fan", + ) def speed(self) -> int: """Fan speed (1-100).""" return self.data["fan_speed"] - @property + @property @sensor("Humidity", unit="%", device_class="humidity", icon="mdi:water-percent") def humidity(self) -> int: """Air humidity in percent.""" return self.data["humidity"] - @property - @setting("LED Brightness", setter_name="set_led_brightness", min_value=0, max_value=100, step=1, unit="%", icon="mdi:brightness-6") + @property + @setting( + "LED Brightness", + setter_name="set_led_brightness", + min_value=0, + max_value=100, + step=1, + unit="%", + icon="mdi:brightness-6", + ) def led_brightness(self) -> int: """LED brightness (1-100).""" return self.data["light"] - @property + @property @setting("Mode", setter_name="set_mode", choices=OperationMode, icon="mdi:fan") def mode(self) -> OperationMode: """Operation mode (normal or nature).""" return OperationMode[OperationModeFanZA5(self.data["mode"]).name] - @property + @property @sensor("Power", icon="mdi:power") def power(self) -> str: """Power state.""" return "on" if self.data["power"] else "off" - @property + @property @sensor("Is On", icon="mdi:power") def is_on(self) -> bool: """True if device is currently on.""" return self.data["power"] - @property - @setting("Delay Off Countdown", setter_name="delay_off", unit="s", icon="mdi:timer-off-outline") + @property + @setting( + "Delay Off Countdown", + setter_name="delay_off", + unit="s", + icon="mdi:timer-off-outline", + ) def delay_off_countdown(self) -> int: """Countdown until turning off in minutes.""" return self.data["power_off_time"] - @property + @property @sensor("Power Supply Attached", icon="mdi:power-plug") def powersupply_attached(self) -> bool: """True is power supply is attached.""" return self.data["powersupply_attached"] - @property + @property @sensor("Speed RPM", unit="rpm", icon="mdi:fan") def speed_rpm(self) -> int: """Fan rotations per minute.""" return self.data["speed_rpm"] - @property + @property @setting("Oscillate", setter_name="set_oscillate", icon="mdi:arrow-left-right") def oscillate(self) -> bool: """True if oscillation is enabled.""" return self.data["swing_mode"] - @property + @property @setting("Angle", setter_name="set_angle", icon="mdi:angle-acute") def angle(self) -> int: """Oscillation angle.""" return self.data["swing_mode_angle"] - @property - @sensor("Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer") + @property + @sensor( + "Temperature", unit="°C", device_class="temperature", icon="mdi:thermometer" + ) def temperature(self) -> Any: """Air temperature (degree celsius).""" return self.data["temperature"] diff --git a/miio/integrations/zhimi/heater/heater.py b/miio/integrations/zhimi/heater/heater.py index 60c1e96c6..4e5b40c1e 100644 --- a/miio/integrations/zhimi/heater/heater.py +++ b/miio/integrations/zhimi/heater/heater.py @@ -82,7 +82,9 @@ def humidity(self) -> Optional[int]: return None @property - @sensor("Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature") + @sensor( + "Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature" + ) def temperature(self) -> float: """Current temperature.""" return self.data["temperature"] @@ -131,7 +133,9 @@ def use_time(self) -> int: return self.data["use_time"] @property - @sensor("Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration") + @sensor( + "Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration" + ) def delay_off_countdown(self) -> Optional[int]: """Countdown until turning off in seconds.""" if "poweroff_time" in self.data and self.data["poweroff_time"] is not None: diff --git a/miio/integrations/zhimi/heater/heater_miot.py b/miio/integrations/zhimi/heater/heater_miot.py index 15f66a239..32b14a3f0 100644 --- a/miio/integrations/zhimi/heater/heater_miot.py +++ b/miio/integrations/zhimi/heater/heater_miot.py @@ -148,19 +148,25 @@ def target_temperature(self) -> int: return self.data["target_temperature"] @property - @sensor("Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration") + @sensor( + "Delay Off Countdown", unit="s", icon="mdi:timer-sand", device_class="duration" + ) def delay_off_countdown(self) -> int: """Countdown until turning off in seconds.""" return self.data["countdown_time"] @property - @sensor("Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature") + @sensor( + "Temperature", unit="°C", icon="mdi:thermometer", device_class="temperature" + ) def temperature(self) -> float: """Current temperature.""" return self.data["temperature"] @property - @sensor("Relative Humidity", unit="%", icon="mdi:water-percent", device_class="humidity") + @sensor( + "Relative Humidity", unit="%", icon="mdi:water-percent", device_class="humidity" + ) def relative_humidity(self) -> Optional[int]: """Current relative humidity.""" return self.data.get("relative_humidity")