Skip to content
Merged
7 changes: 2 additions & 5 deletions miio/airconditioningcompanion.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,9 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_ACPARTNER_V2,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

if model in MODELS_SUPPORTED:
self.model = model
else:
self.model = MODEL_ACPARTNER_V2
if self.model not in MODELS_SUPPORTED:
_LOGGER.error(
"Device model %s unsupported. Falling back to %s.", model, self.model
)
Expand Down
7 changes: 1 addition & 6 deletions miio/airdehumidifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_DEHUMIDIFIER_V1,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_DEHUMIDIFIER_V1
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

self.device_info: DeviceInfo

Expand Down
7 changes: 1 addition & 6 deletions miio/airfresh.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_AIRFRESH_VA2,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_AIRFRESH_VA2
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

@command(
default_output=format_output(
Expand Down
14 changes: 2 additions & 12 deletions miio/airfresh_t2017.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_AIRFRESH_A1,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_AIRFRESH_A1
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

@command(
default_output=format_output(
Expand Down Expand Up @@ -380,12 +375,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_AIRFRESH_T2017,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_AIRFRESH_T2017
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

@command(
default_output=format_output(
Expand Down
7 changes: 1 addition & 6 deletions miio/airhumidifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_HUMIDIFIER_V1,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_HUMIDIFIER_V1
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

# TODO: convert to use generic device info in the future
self.device_info: Optional[DeviceInfo] = None
Expand Down
6 changes: 3 additions & 3 deletions miio/airhumidifier_jsq.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_HUMIDIFIER_JSQ001,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

self.model = model if model in AVAILABLE_PROPERTIES else MODEL_HUMIDIFIER_JSQ001
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)
if model not in AVAILABLE_PROPERTIES:
self._model = MODEL_HUMIDIFIER_JSQ001

@command(
default_output=format_output(
Expand Down
7 changes: 1 addition & 6 deletions miio/airhumidifier_mjjsq.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_HUMIDIFIER_MJJSQ,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_HUMIDIFIER_MJJSQ
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

@command(
default_output=format_output(
Expand Down
21 changes: 3 additions & 18 deletions miio/airpurifier_airdog.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_AIRDOG_X3,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_AIRDOG_X3
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

@command(
default_output=format_output(
Expand Down Expand Up @@ -200,12 +195,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_AIRDOG_X5,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_AIRDOG_X5
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)


class AirDogX7SM(AirDogX3):
Expand All @@ -218,9 +208,4 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_AIRDOG_X7SM,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_AIRDOG_X7SM
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)
15 changes: 2 additions & 13 deletions miio/airqualitymonitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,18 +160,12 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_AIRQUALITYMONITOR_V1,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

if model in AVAILABLE_PROPERTIES:
self.model = model
elif model is not None:
self.model = MODEL_AIRQUALITYMONITOR_V1
if model not in AVAILABLE_PROPERTIES:
_LOGGER.error(
"Device model %s unsupported. Falling back to %s.", model, self.model
)
else:
# Force autodetection.
self.model = None

@command(
default_output=format_output(
Expand All @@ -191,11 +185,6 @@ def __init__(
)
def status(self) -> AirQualityMonitorStatus:
"""Return device status."""

if self.model is None:
info = self.info()
self.model = info.model

properties = AVAILABLE_PROPERTIES[self.model]

if self.model == MODEL_AIRQUALITYMONITOR_B1:
Expand Down
6 changes: 4 additions & 2 deletions miio/chuangmi_plug.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ def __init__(
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
self._model = model
else:
self.model = MODEL_CHUANGMI_PLUG_M1
self._model = MODEL_CHUANGMI_PLUG_M1

@command(
default_output=format_output(
Expand Down Expand Up @@ -182,6 +182,8 @@ def __init__(
start_id: int = 0,
debug: int = 0,
lazy_discover: bool = True,
*,
model: str = None
) -> None:
super().__init__(
ip, token, start_id, debug, lazy_discover, model=MODEL_CHUANGMI_PLUG_M1
Expand Down
28 changes: 28 additions & 0 deletions miio/click_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,30 @@ def __call__(self, func):
self.func = func
func._device_group_command = self
self.kwargs.setdefault("help", self.func.__doc__)

def _autodetect_model_if_needed(func):
def _wrap(self, *args, **kwargs):
skip_autodetect = func._device_group_command.kwargs.pop(
"skip_autodetect", False
)
if (
not skip_autodetect
and self._model is None
and self._info is None
):
_LOGGER.debug(
"Unknown model, trying autodetection. %s %s"
% (self._model, self._info)
)
self._fetch_info()
return func(self, *args, **kwargs)

# TODO HACK to make the command visible to cli
_wrap._device_group_command = func._device_group_command
return _wrap

func = _autodetect_model_if_needed(func)

return func

@property
Expand All @@ -183,6 +207,9 @@ def wrap(self, ctx, func):
else:
output = format_output("Running command {0}".format(self.command_name))

# Remove skip_autodetect before constructing the click.command
self.kwargs.pop("skip_autodetect", None)

func = output(func)
for decorator in self.decorators:
func = decorator(func)
Expand All @@ -195,6 +222,7 @@ def call(self, owner, *args, **kwargs):
DEFAULT_PARAMS = [
click.Option(["--ip"], required=True, callback=validate_ip),
click.Option(["--token"], required=True, callback=validate_token),
click.Option(["--model"], required=False),
]

def __init__(
Expand Down
51 changes: 45 additions & 6 deletions miio/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
from enum import Enum
from pprint import pformat as pf
from typing import Any, Optional # noqa: F401
from typing import Any, List, Optional # noqa: F401

import click

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

retry_count = 3
timeout = 5
_supported_models: List[str] = []

def __init__(
self,
Expand All @@ -63,9 +64,13 @@ def __init__(
debug: int = 0,
lazy_discover: bool = True,
timeout: int = None,
*,
model: str = None,
) -> None:
self.ip = ip
self.token = token
self._model = model
self._info = None
timeout = timeout if timeout is not None else self.timeout
self._protocol = MiIOProtocol(
ip, token, start_id, debug, lazy_discover, timeout
Expand All @@ -92,6 +97,7 @@ def send(
:param dict parameters: Parameters to send
:param int retry_count: How many times to retry on error
:param dict extra_parameters: Extra top-level parameters
:param str model: Force model to avoid autodetection
"""
retry_count = retry_count if retry_count is not None else self.retry_count
return self._protocol.send(
Expand Down Expand Up @@ -121,26 +127,59 @@ def raw_command(self, command, parameters):
"Model: {result.model}\n"
"Hardware version: {result.hardware_version}\n"
"Firmware version: {result.firmware_version}\n",
)
),
skip_autodetect=True,
)
def info(self) -> DeviceInfo:
"""Get miIO protocol information from the device.
def info(self, *, skip_cache=False) -> DeviceInfo:
"""Get (and cache) miIO protocol information from the device.

This includes information about connected wlan network, and hardware and
software versions.

:param skip_cache bool: Skip the cache
"""
if self._info is not None and not skip_cache:
return self._info

return self._fetch_info()

def _fetch_info(self):
"""Perform miIO.info query on the device and cache the result."""
try:
return DeviceInfo(self.send("miIO.info"))
devinfo = DeviceInfo(self.send("miIO.info"))
self._info = devinfo
_LOGGER.debug("Detected model %s", devinfo.model)
if devinfo.model not in self.supported_models:
_LOGGER.warning(
"Found an unsupported model '%s' for class '%s'. If this is working for you, please open an issue at https://github.com/rytilahti/python-miio/",
self.model,
self.__class__.__name__,
)

return devinfo
except PayloadDecodeException as ex:
raise DeviceInfoUnavailableException(
"Unable to request miIO.info from the device"
) from ex

@property
def raw_id(self):
def raw_id(self) -> int:
"""Return the last used protocol sequence id."""
return self._protocol.raw_id

@property
def supported_models(self) -> List[str]:
"""Return a list of supported models."""
return self._supported_models

@property
def model(self) -> str:
"""Return device model."""
if self._model is not None:
return self._model

return self.info().model

def update(self, url: str, md5: str):
"""Start an OTA update."""
payload = {
Expand Down
14 changes: 2 additions & 12 deletions miio/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_FAN_V3,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_FAN_V3
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

@command(
default_output=format_output(
Expand Down Expand Up @@ -532,12 +527,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_FAN_P5,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_FAN_P5
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

@command(
default_output=format_output(
Expand Down
7 changes: 1 addition & 6 deletions miio/fan_leshow.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,7 @@ def __init__(
lazy_discover: bool = True,
model: str = MODEL_FAN_LESHOW_SS4,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_FAN_LESHOW_SS4
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)

@command(
default_output=format_output(
Expand Down
Loading