Create common interfaces for remaining device types (#895)

Introduce common module interfaces across smart and iot devices and provide better typing implementation for getting modules to support this.
This commit is contained in:
Steven B
2024-05-10 19:29:28 +01:00
committed by GitHub
parent 7d4dc4c710
commit 9473d97ad2
33 changed files with 673 additions and 220 deletions

View File

@@ -2,37 +2,16 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from ...feature import Feature
from ...interfaces.led import Led
from ..smartmodule import SmartModule
if TYPE_CHECKING:
from ..smartdevice import SmartDevice
class LedModule(SmartModule):
class LedModule(SmartModule, Led):
"""Implementation of led controls."""
REQUIRED_COMPONENT = "led"
QUERY_GETTER_NAME = "get_led_info"
def __init__(self, device: SmartDevice, module: str):
super().__init__(device, module)
self._add_feature(
Feature(
device=device,
container=self,
id="led",
name="LED",
icon="mdi:led-{state}",
attribute_getter="led",
attribute_setter="set_led",
type=Feature.Type.Switch,
category=Feature.Category.Config,
)
)
def query(self) -> dict:
"""Query to execute during the update cycle."""
return {self.QUERY_GETTER_NAME: {"led_rule": None}}
@@ -56,7 +35,7 @@ class LedModule(SmartModule):
This should probably be a select with always/never/nightmode.
"""
rule = "always" if enable else "never"
return await self.call("set_led_info", self.data | {"led_rule": rule})
return await self.call("set_led_info", dict(self.data, **{"led_rule": rule}))
@property
def night_mode_settings(self):

View File

@@ -6,14 +6,14 @@ import base64
import copy
from typing import TYPE_CHECKING, Any
from ...feature import Feature
from ...interfaces.lighteffect import LightEffect
from ..smartmodule import SmartModule
if TYPE_CHECKING:
from ..smartdevice import SmartDevice
class LightEffectModule(SmartModule):
class LightEffectModule(SmartModule, LightEffect):
"""Implementation of dynamic light effects."""
REQUIRED_COMPONENT = "light_effect"
@@ -22,29 +22,11 @@ class LightEffectModule(SmartModule):
"L1": "Party",
"L2": "Relax",
}
LIGHT_EFFECTS_OFF = "Off"
def __init__(self, device: SmartDevice, module: str):
super().__init__(device, module)
self._scenes_names_to_id: dict[str, str] = {}
def _initialize_features(self):
"""Initialize features."""
device = self._device
self._add_feature(
Feature(
device,
id="light_effect",
name="Light effect",
container=self,
attribute_getter="effect",
attribute_setter="set_effect",
category=Feature.Category.Config,
type=Feature.Type.Choice,
choices_getter="effect_list",
)
)
def _initialize_effects(self) -> dict[str, dict[str, Any]]:
"""Return built-in effects."""
# Copy the effects so scene name updates do not update the underlying dict.
@@ -64,7 +46,7 @@ class LightEffectModule(SmartModule):
return effects
@property
def effect_list(self) -> list[str] | None:
def effect_list(self) -> list[str]:
"""Return built-in effects list.
Example:
@@ -90,6 +72,9 @@ class LightEffectModule(SmartModule):
async def set_effect(
self,
effect: str,
*,
brightness: int | None = None,
transition: int | None = None,
) -> None:
"""Set an effect for the device.
@@ -108,6 +93,24 @@ class LightEffectModule(SmartModule):
params["id"] = effect_id
return await self.call("set_dynamic_light_effect_rule_enable", params)
async def set_custom_effect(
self,
effect_dict: dict,
) -> None:
"""Set a custom effect on the device.
:param str effect_dict: The custom effect dict to set
"""
raise NotImplementedError(
"Device does not support setting custom effects. "
"Use has_custom_effects to check for support."
)
@property
def has_custom_effects(self) -> bool:
"""Return True if the device supports setting custom effects."""
return False
def query(self) -> dict:
"""Query to execute during the update cycle."""
return {self.QUERY_GETTER_NAME: {"start_index": 0}}