From 7b645f715e6c97626e37d2cc76525862aec0e291 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Thu, 11 Aug 2022 21:26:20 +0200 Subject: [PATCH] Move DeviceStatus and its helpers to separate class --- miio/device.py | 103 +------------------------------------------ miio/devicestatus.py | 97 ++++++++++++++++++++++++++++++++++++++++ miio/powerstrip.py | 3 +- 3 files changed, 101 insertions(+), 102 deletions(-) create mode 100644 miio/devicestatus.py diff --git a/miio/device.py b/miio/device.py index f2eba5ebd..4ce27d0b4 100644 --- a/miio/device.py +++ b/miio/device.py @@ -1,18 +1,7 @@ -import inspect import logging -import warnings from enum import Enum from pprint import pformat as pf -from typing import ( # noqa: F401 - Any, - Dict, - List, - Optional, - Union, - get_args, - get_origin, - get_type_hints, -) +from typing import Any, Dict, List, Optional, Union # noqa: F401 import click @@ -24,6 +13,7 @@ SwitchDescriptor, ) from .deviceinfo import DeviceInfo +from .devicestatus import DeviceStatus from .exceptions import DeviceInfoUnavailableException, PayloadDecodeException from .miioprotocol import MiIOProtocol @@ -37,95 +27,6 @@ class UpdateState(Enum): Idle = "idle" -def sensor(*, name: str, icon: str = "mdi:sensor", unit: str = "", **kwargs): - """Decorator helper to create sensordescriptors for status classes. - - The information can be used by users of the library to programatically find out what - types of sensors are available for the device. - """ - - def decorator_sensor(func): - property_name = func.__name__ - - def _sensor_type_for_return_type(func): - rtype = get_type_hints(func).get("return") - if get_origin(rtype) is Union: # Unwrap Optional[] - rtype, _ = get_args(rtype) - - if rtype == bool: - return "binary" - else: - return "sensor" - - sensor_type = _sensor_type_for_return_type(func) - descriptor = SensorDescriptor( - id=str(property_name), - property=str(property_name), - name=name, - icon=icon, - unit=unit, - type=sensor_type, - **kwargs, - ) - func._sensor = descriptor - - return func - - return decorator_sensor - - -class _StatusMeta(type): - """Meta class to provide introspectable properties.""" - - def __new__(metacls, name, bases, namespace, **kwargs): - cls = super().__new__(metacls, name, bases, namespace) - cls._sensors = [] - for n in namespace: - prop = getattr(namespace[n], "fget", None) - if prop: - sensor = getattr(prop, "_sensor", None) - if sensor: - _LOGGER.debug(f"Found sensor: {sensor} for {name}") - cls._sensors.append(sensor) - - return cls - - -class DeviceStatus(metaclass=_StatusMeta): - """Base class for status containers. - - All status container classes should inherit from this class: - - * This class allows downstream users to access the available information in an - introspectable way. - * The __repr__ implementation returns all defined properties and their values. - """ - - def __repr__(self): - props = inspect.getmembers(self.__class__, lambda o: isinstance(o, property)) - - s = f"<{self.__class__.__name__}" - for prop_tuple in props: - name, prop = prop_tuple - try: - # ignore deprecation warnings - with warnings.catch_warnings(record=True): - prop_value = prop.fget(self) - except Exception as ex: - prop_value = ex.__class__.__name__ - - s += f" {name}={prop_value}" - s += ">" - return s - - def sensors(self): - """Return the list of sensors exposed by the status container. - - You can use @sensor decorator to define sensors inside your status class. - """ - return self._sensors - - class Device(metaclass=DeviceGroupMeta): """Base class for all device implementations. diff --git a/miio/devicestatus.py b/miio/devicestatus.py new file mode 100644 index 000000000..29356f724 --- /dev/null +++ b/miio/devicestatus.py @@ -0,0 +1,97 @@ +import inspect +import logging +import warnings +from typing import Union, get_args, get_origin, get_type_hints + +from .descriptors import SensorDescriptor + +_LOGGER = logging.getLogger(__name__) + + +class _StatusMeta(type): + """Meta class to provide introspectable properties.""" + + def __new__(metacls, name, bases, namespace, **kwargs): + cls = super().__new__(metacls, name, bases, namespace) + cls._sensors = [] + for n in namespace: + prop = getattr(namespace[n], "fget", None) + if prop: + sensor = getattr(prop, "_sensor", None) + if sensor: + _LOGGER.debug(f"Found sensor: {sensor} for {name}") + cls._sensors.append(sensor) + + return cls + + +class DeviceStatus(metaclass=_StatusMeta): + """Base class for status containers. + + All status container classes should inherit from this class: + + * This class allows downstream users to access the available information in an + introspectable way. + * The __repr__ implementation returns all defined properties and their values. + """ + + def __repr__(self): + props = inspect.getmembers(self.__class__, lambda o: isinstance(o, property)) + + s = f"<{self.__class__.__name__}" + for prop_tuple in props: + name, prop = prop_tuple + try: + # ignore deprecation warnings + with warnings.catch_warnings(record=True): + prop_value = prop.fget(self) + except Exception as ex: + prop_value = ex.__class__.__name__ + + s += f" {name}={prop_value}" + s += ">" + return s + + def sensors(self): + """Return the list of sensors exposed by the status container. + + You can use @sensor decorator to define sensors inside your status class. + """ + return self._sensors + + +def sensor(*, name: str, icon: str = "mdi:sensor", unit: str = "", **kwargs): + """Decorator helper to create sensordescriptors for status classes. + + The information can be used by users of the library to programatically find out what + types of sensors are available for the device. + """ + + def decorator_sensor(func): + property_name = func.__name__ + + def _sensor_type_for_return_type(func): + rtype = get_type_hints(func).get("return") + if get_origin(rtype) is Union: # Unwrap Optional[] + rtype, _ = get_args(rtype) + + if rtype == bool: + return "binary" + else: + return "sensor" + + sensor_type = _sensor_type_for_return_type(func) + descriptor = SensorDescriptor( + id=str(property_name), + property=str(property_name), + name=name, + icon=icon, + unit=unit, + type=sensor_type, + **kwargs, + ) + func._sensor = descriptor + + return func + + return decorator_sensor diff --git a/miio/powerstrip.py b/miio/powerstrip.py index 2fe3bbfb0..2b43dcfae 100644 --- a/miio/powerstrip.py +++ b/miio/powerstrip.py @@ -6,7 +6,8 @@ import click from .click_common import EnumType, command, format_output -from .device import Device, DeviceStatus, sensor +from .device import Device +from .devicestatus import DeviceStatus, sensor from .exceptions import DeviceException from .utils import deprecated