Skip to content

Commit

Permalink
Use _mappings for all miot integrations (#1349)
Browse files Browse the repository at this point in the history
* Simplifies the code as there is no need to define supported models separately
  * The keys from mappings are used as supported models, this will be extended to miio devices in the future, too.
* Mass convert miot devices to use _mappings instead of mapping
* Add tests to verify that deprecated 'mapping' is not used and the data structure is what is expected, re #1344
* As class properties are supported only from python3.9 onwards, the supported_models is now defined in the meta class.
  • Loading branch information
rytilahti committed Mar 7, 2022
1 parent 6e5b153 commit 5676303
Show file tree
Hide file tree
Showing 20 changed files with 290 additions and 235 deletions.
6 changes: 4 additions & 2 deletions miio/airconditioner_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"timer": {"siid": 10, "piid": 3},
}

_MAPPINGS = {model: _MAPPING for model in SUPPORTED_MODELS}


CLEANING_STAGES = [
"Stopped",
"Condensing water",
Expand Down Expand Up @@ -281,8 +284,7 @@ def timer(self) -> TimerStatus:
class AirConditionerMiot(MiotDevice):
"""Main class representing the air conditioner which uses MIoT protocol."""

_supported_models = SUPPORTED_MODELS
mapping = _MAPPING
_mappings = _MAPPINGS

@command(
default_output=format_output(
Expand Down
68 changes: 34 additions & 34 deletions miio/airhumidifier_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,39 @@
from .miot_device import DeviceStatus, MiotDevice

_LOGGER = logging.getLogger(__name__)
_MAPPING = {
# Source http://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:humidifier:0000A00E:zhimi-ca4:2
# Air Humidifier (siid=2)
"power": {"siid": 2, "piid": 1}, # bool
"fault": {"siid": 2, "piid": 2}, # [0, 15] step 1
"mode": {"siid": 2, "piid": 5}, # 0 - Auto, 1 - lvl1, 2 - lvl2, 3 - lvl3
"target_humidity": {"siid": 2, "piid": 6}, # [30, 80] step 1
"water_level": {"siid": 2, "piid": 7}, # [0, 128] step 1
"dry": {"siid": 2, "piid": 8}, # bool
"use_time": {"siid": 2, "piid": 9}, # [0, 2147483600], step 1
"button_pressed": {"siid": 2, "piid": 10}, # 0 - none, 1 - led, 2 - power
"speed_level": {"siid": 2, "piid": 11}, # [200, 2000], step 10
# Environment (siid=3)
"temperature": {"siid": 3, "piid": 7}, # [-40, 125] step 0.1
"fahrenheit": {"siid": 3, "piid": 8}, # [-40, 257] step 0.1
"humidity": {"siid": 3, "piid": 9}, # [0, 100] step 1
# Alarm (siid=4)
"buzzer": {"siid": 4, "piid": 1},
# Indicator Light (siid=5)
"led_brightness": {"siid": 5, "piid": 2}, # 0 - Off, 1 - Dim, 2 - Brightest
# Physical Control Locked (siid=6)
"child_lock": {"siid": 6, "piid": 1}, # bool
# Other (siid=7)
"actual_speed": {"siid": 7, "piid": 1}, # [0, 2000] step 1
"power_time": {"siid": 7, "piid": 3}, # [0, 4294967295] step 1
"clean_mode": {"siid": 7, "piid": 5}, # bool


SMARTMI_EVAPORATIVE_HUMIDIFIER_2 = "zhimi.humidifier.ca4"


_MAPPINGS = {
SMARTMI_EVAPORATIVE_HUMIDIFIER_2: {
# Source http://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:humidifier:0000A00E:zhimi-ca4:2
# Air Humidifier (siid=2)
"power": {"siid": 2, "piid": 1}, # bool
"fault": {"siid": 2, "piid": 2}, # [0, 15] step 1
"mode": {"siid": 2, "piid": 5}, # 0 - Auto, 1 - lvl1, 2 - lvl2, 3 - lvl3
"target_humidity": {"siid": 2, "piid": 6}, # [30, 80] step 1
"water_level": {"siid": 2, "piid": 7}, # [0, 128] step 1
"dry": {"siid": 2, "piid": 8}, # bool
"use_time": {"siid": 2, "piid": 9}, # [0, 2147483600], step 1
"button_pressed": {"siid": 2, "piid": 10}, # 0 - none, 1 - led, 2 - power
"speed_level": {"siid": 2, "piid": 11}, # [200, 2000], step 10
# Environment (siid=3)
"temperature": {"siid": 3, "piid": 7}, # [-40, 125] step 0.1
"fahrenheit": {"siid": 3, "piid": 8}, # [-40, 257] step 0.1
"humidity": {"siid": 3, "piid": 9}, # [0, 100] step 1
# Alarm (siid=4)
"buzzer": {"siid": 4, "piid": 1},
# Indicator Light (siid=5)
"led_brightness": {"siid": 5, "piid": 2}, # 0 - Off, 1 - Dim, 2 - Brightest
# Physical Control Locked (siid=6)
"child_lock": {"siid": 6, "piid": 1}, # bool
# Other (siid=7)
"actual_speed": {"siid": 7, "piid": 1}, # [0, 2000] step 1
"power_time": {"siid": 7, "piid": 3}, # [0, 4294967295] step 1
"clean_mode": {"siid": 7, "piid": 5}, # bool
}
}


Expand Down Expand Up @@ -248,17 +255,10 @@ def clean_mode(self) -> bool:
return self.data["clean_mode"]


SMARTMI_EVAPORATIVE_HUMIDIFIER_2 = "zhimi.humidifier.ca4"

SUPPORTED_MODELS = [SMARTMI_EVAPORATIVE_HUMIDIFIER_2]


class AirHumidifierMiot(MiotDevice):
"""Main class representing the air humidifier which uses MIoT protocol."""

_supported_models = SUPPORTED_MODELS

mapping = _MAPPING
_mappings = _MAPPINGS

@command(
default_output=format_output(
Expand Down
1 change: 0 additions & 1 deletion miio/airpurifier_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,6 @@ def filter_left_time(self) -> Optional[int]:
class AirPurifierMiot(MiotDevice):
"""Main class representing the air purifier which uses MIoT protocol."""

_supported_models = list(_MAPPINGS.keys())
_mappings = _MAPPINGS

@command(
Expand Down
67 changes: 34 additions & 33 deletions miio/airqualitymonitor_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,39 @@

MODEL_AIRQUALITYMONITOR_CGDN1 = "cgllc.airm.cgdn1"

_MAPPING_CGDN1 = {
# Source https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-cgdn1:1
# Environment
"humidity": {"siid": 3, "piid": 1}, # [0, 100] step 1
"pm25": {"siid": 3, "piid": 4}, # [0, 1000] step 1
"pm10": {"siid": 3, "piid": 5}, # [0, 1000] step 1
"temperature": {"siid": 3, "piid": 7}, # [-30, 100] step 0.00001
"co2": {"siid": 3, "piid": 8}, # [0, 9999] step 1
# Battery
"battery": {"siid": 4, "piid": 1}, # [0, 100] step 1
"charging_state": {
"siid": 4,
"piid": 2,
}, # 1 - Charging, 2 - Not charging, 3 - Not chargeable
"voltage": {"siid": 4, "piid": 3}, # [0, 65535] step 1
# Settings
"start_time": {"siid": 9, "piid": 2}, # [0, 2147483647] step 1
"end_time": {"siid": 9, "piid": 3}, # [0, 2147483647] step 1
"monitoring_frequency": {
"siid": 9,
"piid": 4,
}, # 1, 60, 300, 600, 0; device accepts [0..600]
"screen_off": {
"siid": 9,
"piid": 5,
}, # 15, 30, 60, 300, 0; device accepts [0..300], 0 means never
"device_off": {
"siid": 9,
"piid": 6,
}, # 15, 30, 60, 0; device accepts [0..60], 0 means never
"temperature_unit": {"siid": 9, "piid": 7},
_MAPPINGS = {
MODEL_AIRQUALITYMONITOR_CGDN1: {
# Source https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-cgdn1:1
# Environment
"humidity": {"siid": 3, "piid": 1}, # [0, 100] step 1
"pm25": {"siid": 3, "piid": 4}, # [0, 1000] step 1
"pm10": {"siid": 3, "piid": 5}, # [0, 1000] step 1
"temperature": {"siid": 3, "piid": 7}, # [-30, 100] step 0.00001
"co2": {"siid": 3, "piid": 8}, # [0, 9999] step 1
# Battery
"battery": {"siid": 4, "piid": 1}, # [0, 100] step 1
"charging_state": {
"siid": 4,
"piid": 2,
}, # 1 - Charging, 2 - Not charging, 3 - Not chargeable
"voltage": {"siid": 4, "piid": 3}, # [0, 65535] step 1
# Settings
"start_time": {"siid": 9, "piid": 2}, # [0, 2147483647] step 1
"end_time": {"siid": 9, "piid": 3}, # [0, 2147483647] step 1
"monitoring_frequency": {
"siid": 9,
"piid": 4,
}, # 1, 60, 300, 600, 0; device accepts [0..600]
"screen_off": {
"siid": 9,
"piid": 5,
}, # 15, 30, 60, 300, 0; device accepts [0..300], 0 means never
"device_off": {
"siid": 9,
"piid": 6,
}, # 15, 30, 60, 0; device accepts [0..60], 0 means never
"temperature_unit": {"siid": 9, "piid": 7},
}
}


Expand Down Expand Up @@ -169,8 +171,7 @@ def display_temperature_unit(self):
class AirQualityMonitorCGDN1(MiotDevice):
"""Qingping Air Monitor Lite."""

mapping = _MAPPING_CGDN1
_supported_models = [MODEL_AIRQUALITYMONITOR_CGDN1]
_mappings = _MAPPINGS

@command(
default_output=format_output(
Expand Down
5 changes: 5 additions & 0 deletions miio/click_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ def get_device_group(dcls):
mcs._device_classes.add(cls)
return cls

@property
def supported_models(cls):
"""Return list of supported models."""
return cls._mappings.keys() or cls._supported_models


class DeviceGroup(click.MultiCommand):
class Command:
Expand Down
42 changes: 24 additions & 18 deletions miio/curtain_youpin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,33 @@
from .miot_device import DeviceStatus, MiotDevice

_LOGGER = logging.getLogger(__name__)
_MAPPING = {
# # Source http://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:curtain:0000A00C:lumi-hagl05:1
# Curtain
"motor_control": {"siid": 2, "piid": 2}, # 0 - Pause, 1 - Open, 2 - Close, 3 - auto
"current_position": {"siid": 2, "piid": 3}, # Range: [0, 100, 1]
"status": {"siid": 2, "piid": 6}, # 0 - Stopped, 1 - Opening, 2 - Closing
"target_position": {"siid": 2, "piid": 7}, # Range: [0, 100, 1]
# curtain_cfg
"is_manual_enabled": {"siid": 4, "piid": 1}, #
"polarity": {"siid": 4, "piid": 2},
"is_position_limited": {"siid": 4, "piid": 3},
"night_tip_light": {"siid": 4, "piid": 4},
"run_time": {"siid": 4, "piid": 5}, # Range: [0, 255, 1]
# motor_controller
"adjust_value": {"siid": 5, "piid": 1}, # Range: [-100, 100, 1]
}


# Model: ZNCLDJ21LM (also known as "Xiaomiyoupin Curtain Controller (Wi-Fi)"
MODEL_CURTAIN_HAGL05 = "lumi.curtain.hagl05"

_MAPPINGS = {
MODEL_CURTAIN_HAGL05: {
# # Source http://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:curtain:0000A00C:lumi-hagl05:1
# Curtain
"motor_control": {
"siid": 2,
"piid": 2,
}, # 0 - Pause, 1 - Open, 2 - Close, 3 - auto
"current_position": {"siid": 2, "piid": 3}, # Range: [0, 100, 1]
"status": {"siid": 2, "piid": 6}, # 0 - Stopped, 1 - Opening, 2 - Closing
"target_position": {"siid": 2, "piid": 7}, # Range: [0, 100, 1]
# curtain_cfg
"is_manual_enabled": {"siid": 4, "piid": 1}, #
"polarity": {"siid": 4, "piid": 2},
"is_position_limited": {"siid": 4, "piid": 3},
"night_tip_light": {"siid": 4, "piid": 4},
"run_time": {"siid": 4, "piid": 5}, # Range: [0, 255, 1]
# motor_controller
"adjust_value": {"siid": 5, "piid": 1}, # Range: [-100, 100, 1]
}
}


class MotorControl(enum.Enum):
Pause = 0
Expand Down Expand Up @@ -114,8 +121,7 @@ def adjust_value(self) -> int:
class CurtainMiot(MiotDevice):
"""Main class representing the lumi.curtain.hagl05 curtain."""

mapping = _MAPPING
_supported_models = ["lumi.curtain.hagl05"]
_mappings = _MAPPINGS

@command(
default_output=format_output(
Expand Down
3 changes: 2 additions & 1 deletion miio/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import warnings
from enum import Enum
from pprint import pformat as pf
from typing import Any, List, Optional # noqa: F401
from typing import Any, Dict, List, Optional # noqa: F401

import click

Expand Down Expand Up @@ -57,6 +57,7 @@ class Device(metaclass=DeviceGroupMeta):

retry_count = 3
timeout = 5
_mappings: Dict[str, Any] = {}
_supported_models: List[str] = []

def __init__(
Expand Down
1 change: 0 additions & 1 deletion miio/heater_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ class HeaterMiot(MiotDevice):
(zhimi.heater.za2)."""

_mappings = _MAPPINGS
_supported_models = list(_MAPPINGS.keys())

@command(
default_output=format_output(
Expand Down
31 changes: 16 additions & 15 deletions miio/integrations/fan/dmaker/fan_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,6 @@


MIOT_MAPPING = {
MODEL_FAN_1C: {
# https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:fan:0000A005:dmaker-1c:1
"power": {"siid": 2, "piid": 1},
"fan_level": {"siid": 2, "piid": 2},
"child_lock": {"siid": 3, "piid": 1},
"swing_mode": {"siid": 2, "piid": 3},
"power_off_time": {"siid": 2, "piid": 10},
"buzzer": {"siid": 2, "piid": 11},
"light": {"siid": 2, "piid": 12},
"mode": {"siid": 2, "piid": 7},
},
MODEL_FAN_P9: {
# Source https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:fan:0000A005:dmaker-p9:1
"power": {"siid": 2, "piid": 1},
Expand Down Expand Up @@ -70,6 +59,20 @@
},
}

FAN1C_MAPPINGS = {
MODEL_FAN_1C: {
# https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:fan:0000A005:dmaker-1c:1
"power": {"siid": 2, "piid": 1},
"fan_level": {"siid": 2, "piid": 2},
"child_lock": {"siid": 3, "piid": 1},
"swing_mode": {"siid": 2, "piid": 3},
"power_off_time": {"siid": 2, "piid": 10},
"buzzer": {"siid": 2, "piid": 11},
"light": {"siid": 2, "piid": 12},
"mode": {"siid": 2, "piid": 7},
}
}

SUPPORTED_ANGLES = {
MODEL_FAN_P9: [30, 60, 90, 120, 150],
MODEL_FAN_P10: [30, 60, 90, 120, 140],
Expand Down Expand Up @@ -231,8 +234,6 @@ def child_lock(self) -> bool:

class FanMiot(MiotDevice):
_mappings = MIOT_MAPPING
# TODO Fan1C should be merged to FanMiot
_supported_models = list(set(MIOT_MAPPING) - {MODEL_FAN_1C})

@command(
default_output=format_output(
Expand Down Expand Up @@ -373,8 +374,8 @@ def set_rotate(self, direction: MoveDirection):


class Fan1C(MiotDevice):
mapping = MIOT_MAPPING[MODEL_FAN_1C]
_supported_models = [MODEL_FAN_1C]
# TODO Fan1C should be merged to FanMiot, or moved into its separate file
_mappings = FAN1C_MAPPINGS

def __init__(
self,
Expand Down
1 change: 0 additions & 1 deletion miio/integrations/fan/zhimi/zhimi_miot.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ def temperature(self) -> Any:

class FanZA5(MiotDevice):
_mappings = MIOT_MAPPING
_supported_models = list(MIOT_MAPPING.keys())

@command(
default_output=format_output(
Expand Down
7 changes: 4 additions & 3 deletions miio/integrations/humidifier/deerma/airhumidifier_jsqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
"overwet_protect": {"siid": 7, "piid": 3}, # bool
}

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


class AirHumidifierJsqsException(DeviceException):
pass
Expand Down Expand Up @@ -145,9 +148,7 @@ def overwet_protect(self) -> Optional[bool]:
class AirHumidifierJsqs(MiotDevice):
"""Main class representing the air humidifier which uses MIoT protocol."""

_supported_models = ["deerma.humidifier.jsqs", "deerma.humidifier.jsq5"]

mapping = _MAPPING
_mappings = MIOT_MAPPING

@command(
default_output=format_output(
Expand Down
5 changes: 3 additions & 2 deletions miio/integrations/petwaterdispenser/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@
"timezone": {"siid": 9, "piid": 1},
}

MIOT_MAPPING = {model: _MAPPING for model in SUPPORTED_MODELS}


class PetWaterDispenser(MiotDevice):
"""Main class representing the Pet Waterer / Pet Drinking Fountain / Smart Pet Water
Dispenser."""

mapping = _MAPPING
_supported_models = SUPPORTED_MODELS
_mappings = MIOT_MAPPING

@command(
default_output=format_output(
Expand Down

0 comments on commit 5676303

Please sign in to comment.