Source code for miio.integrations.genericmiot.status

import logging
from typing import TYPE_CHECKING, Dict, Iterable

from miio import DeviceStatus
from miio.miot_models import DeviceModel, MiotAccess, MiotProperty

_LOGGER = logging.getLogger(__name__)


if TYPE_CHECKING:
    from .genericmiot import GenericMiot


[docs] class GenericMiotStatus(DeviceStatus): """Generic status for miot devices.""" def __init__(self, response, dev): self._model: DeviceModel = dev._miot_model self._dev = dev self._data = {} self._data_by_siid_piid = {} self._data_by_normalized_name = {} self._initialize_data(response) def _initialize_data(self, response): def _is_valid_property_response(elem): code = elem.get("code") if code is None: _LOGGER.debug("Ignoring due to missing 'code': %s", elem) return False if code != 0: _LOGGER.warning("Ignoring due to error code '%s': %s", code, elem) return False needed_keys = ("did", "piid", "siid", "value") for key in needed_keys: if key not in elem: _LOGGER.debug("Ignoring due to missing '%s': %s", key, elem) return False return True for prop in response: if not _is_valid_property_response(prop): continue self._data[prop["did"]] = prop["value"] self._data_by_siid_piid[(prop["siid"], prop["piid"])] = prop["value"] self._data_by_normalized_name[self._normalize_name(prop["did"])] = prop[ "value" ] @property def data(self): """Implemented to support json output.""" return self._data def _normalize_name(self, id_: str) -> str: """Return a cleaned id for dict searches.""" return id_.replace(":", "_").replace("-", "_") def __getattr__(self, item): """Return attribute for name. This is overridden to provide access to properties using (siid, piid) tuple. """ # let devicestatus handle dunder methods if item.startswith("__") and item.endswith("__"): return super().__getattr__(item) normalized_name = self._normalize_name(item) if normalized_name in self._data_by_normalized_name: return self._data_by_normalized_name[normalized_name] # TODO: create a helper method and prohibit using non-normalized names if ":" in item: _LOGGER.warning("Use normalized names for accessing properties") serv, prop = item.split(":") prop = self._model.get_property(serv, prop) value = self._data[item] # TODO: this feels like a wrong place to convert value to enum.. if prop.choices is not None: for choice in prop.choices: if choice.value == value: return choice.description _LOGGER.warning( "Unable to find choice for value: %s: %s", value, prop.choices ) return self._data[item] @property def device(self) -> "GenericMiot": """Return the device which returned this status.""" return self._dev
[docs] def property_dict(self) -> Dict[str, MiotProperty]: """Return name-keyed dictionary of properties.""" res = {} # We use (siid, piid) to locate the property as not all devices mirror the did in response for (siid, piid), value in self._data_by_siid_piid.items(): prop = self._model.get_property_by_siid_piid(siid, piid) prop.value = value res[prop.name] = prop return res
@property def __cli_output__(self): """Return a CLI printable status.""" out = "" props = self.property_dict() service = None for _name, prop in props.items(): miot_prop: MiotProperty = prop.extras["miot_property"] if service is None or miot_prop.siid != service.siid: service = miot_prop.service out += f"Service [bold]{service.description} ({service.name})[/bold]\n" # type: ignore # FIXME out += f"\t{prop.description} ({prop.name}, access: {prop.pretty_access}): {prop.pretty_value}" if MiotAccess.Write in miot_prop.access: out += f" ({prop.format}" if prop.pretty_input_constraints is not None: out += f", {prop.pretty_input_constraints}" out += ")" if self.device._debug > 1: out += "\n\t[bold]Extras[/bold]\n" for extra_key, extra_value in prop.extras.items(): out += f"\t\t{extra_key} = {extra_value}\n" out += "\n" return out def __dir__(self) -> Iterable[str]: """Return a list of properties.""" return list(super().__dir__()) + list(self._data_by_normalized_name.keys()) def __repr__(self): """Return string representation of the status.""" s = f"<{self.__class__.__name__}" for name, value in self.property_dict().items(): s += f" {name}={value}" s += ">" return s def __str__(self): """Return simplified string representation of the status.""" s = f"<{self.__class__.__name__}" for name, value in self.property_dict().items(): s += f" {name}={value.pretty_value}" s += ">" return s