Add generic interface (descriptors) for obtaining device features

This commit is contained in:
Teemu Rytilahti
2024-02-03 19:42:08 +01:00
parent 458949157a
commit f0f1e478c4
9 changed files with 245 additions and 6 deletions

View File

@@ -18,6 +18,7 @@ import logging
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional, Sequence, Set
from ..descriptors import Descriptor
from ..device import Device, WifiNetwork
from ..deviceconfig import DeviceConfig
from ..emeterstatus import EmeterStatus
@@ -186,6 +187,7 @@ class IotDevice(Device):
self._sys_info: Any = None # TODO: this is here to avoid changing tests
self._features: Set[str] = set()
self._children: Sequence["IotDevice"] = []
self._supported_modules: Optional[Dict[str, IotModule]] = None
@property
def children(self) -> Sequence["IotDevice"]:
@@ -299,9 +301,33 @@ class IotDevice(Device):
self._last_update = response
self._set_sys_info(response["system"]["get_sysinfo"])
if not self._descriptors:
await self._initialize_descriptors()
await self._modular_update(req)
self._set_sys_info(self._last_update["system"]["get_sysinfo"])
async def _initialize_descriptors(self):
self.add_descriptor(
Descriptor(
device=self, name="RSSI", attribute_getter="rssi", icon="mdi:signal"
)
)
self.add_descriptor(
Descriptor(
device=self, name="Time", attribute_getter="time", show_in_hass=False
)
)
if "on_time" in self._sys_info:
self.add_descriptor(
Descriptor(
device=self,
name="On since",
attribute_getter="on_since",
icon="mdi:clock",
)
)
async def _modular_update(self, req: dict) -> None:
"""Execute an update query."""
if self.has_emeter:
@@ -310,6 +336,18 @@ class IotDevice(Device):
)
self.add_module("emeter", Emeter(self, self.emeter_type))
# TODO: perhaps modules should not have unsupported modules,
# making separate handling for this unnecessary
if self._supported_modules is None:
supported = {}
for module in self.modules.values():
if module.is_supported:
supported[module._module] = module
for _, module_desc in module._module_descriptors.items():
self.add_descriptor(module_desc)
self._supported_modules = supported
request_list = []
est_response_size = 1024 if "system" in req else 0
for module in self.modules.values():

View File

@@ -2,6 +2,7 @@
import logging
from typing import Any, Dict, Optional
from ..descriptors import Descriptor, DescriptorCategory, DescriptorType
from ..device_type import DeviceType
from ..deviceconfig import DeviceConfig
from ..protocol import BaseProtocol
@@ -56,6 +57,18 @@ class IotPlug(IotDevice):
self.add_module("time", Time(self, "time"))
self.add_module("cloud", Cloud(self, "cnCloud"))
self.add_descriptor(
Descriptor(
device=self,
name="LED",
icon="mdi:led-{state}",
attribute_getter="led",
attribute_setter="set_led",
category=DescriptorCategory.Config,
type=DescriptorType.Switch,
)
)
@property # type: ignore
@requires_update
def is_on(self) -> bool:
@@ -88,5 +101,4 @@ class IotPlug(IotDevice):
@requires_update
def state_information(self) -> Dict[str, Any]:
"""Return switch-specific state information."""
info = {"LED state": self.led, "On since": self.on_since}
return info
return {}

View File

@@ -4,6 +4,7 @@ try:
except ImportError:
from pydantic import BaseModel
from ...descriptors import Descriptor, DescriptorType
from .module import IotModule
@@ -25,6 +26,23 @@ class CloudInfo(BaseModel):
class Cloud(IotModule):
"""Module implementing support for cloud services."""
def __init__(self, device, module):
super().__init__(device, module)
self.add_descriptor(
Descriptor(
device=self,
name="Cloud Connection",
icon="mdi:cloud",
attribute_getter="is_connected",
type=DescriptorType.BinarySensor,
)
)
@property
def is_connected(self) -> bool:
"""Return true if device is connected to the cloud."""
return self.info.binded
def query(self):
"""Request cloud connectivity info."""
return self.query_for_command("get_info")

View File

@@ -2,8 +2,9 @@
import collections
import logging
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict
from ...descriptors import Descriptor
from ...exceptions import SmartDeviceException
if TYPE_CHECKING:
@@ -34,6 +35,14 @@ class IotModule(ABC):
def __init__(self, device: "IotDevice", module: str):
self._device = device
self._module = module
self._module_descriptors: Dict[str, Descriptor] = {}
def add_descriptor(self, desc):
"""Add module descriptor."""
module_desc_name = f"{self._module}_{desc.name}"
if module_desc_name in self._module_descriptors:
raise Exception("Duplicate name detected %s" % module_desc_name)
self._module_descriptors[module_desc_name] = desc
@abstractmethod
def query(self):