Migrate TurnOnBehaviours to mashumaro (#1285)

This commit is contained in:
Steven B. 2024-11-20 14:35:51 +00:00 committed by GitHub
parent 0e5013d4b4
commit 955e7ab4d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 54 additions and 25 deletions

View File

@ -4,10 +4,13 @@ from __future__ import annotations
import logging import logging
import re import re
from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import cast from typing import Annotated, cast
from pydantic.v1 import BaseModel, Field, root_validator from mashumaro import DataClassDictMixin
from mashumaro.config import BaseConfig
from mashumaro.types import Alias
from ..device_type import DeviceType from ..device_type import DeviceType
from ..deviceconfig import DeviceConfig from ..deviceconfig import DeviceConfig
@ -35,9 +38,12 @@ class BehaviorMode(str, Enum):
Last = "last_status" Last = "last_status"
#: Use chosen preset. #: Use chosen preset.
Preset = "customize_preset" Preset = "customize_preset"
#: Circadian
Circadian = "circadian"
class TurnOnBehavior(BaseModel): @dataclass
class TurnOnBehavior(DataClassDictMixin):
"""Model to present a single turn on behavior. """Model to present a single turn on behavior.
:param int preset: the index number of wanted preset. :param int preset: the index number of wanted preset.
@ -48,34 +54,30 @@ class TurnOnBehavior(BaseModel):
to contain either the preset index, or ``None`` for the last known state. to contain either the preset index, or ``None`` for the last known state.
""" """
#: Index of preset to use, or ``None`` for the last known state. class Config(BaseConfig):
preset: int | None = Field(alias="index", default=None) """Serialization config."""
omit_none = True
serialize_by_alias = True
#: Wanted behavior #: Wanted behavior
mode: BehaviorMode mode: BehaviorMode
#: Index of preset to use, or ``None`` for the last known state.
@root_validator preset: Annotated[int | None, Alias("index")] = None
def _mode_based_on_preset(cls, values: dict) -> dict: brightness: int | None = None
"""Set the mode based on the preset value.""" color_temp: int | None = None
if values["preset"] is not None: hue: int | None = None
values["mode"] = BehaviorMode.Preset saturation: int | None = None
else:
values["mode"] = BehaviorMode.Last
return values
class Config:
"""Configuration to make the validator run when changing the values."""
validate_assignment = True
class TurnOnBehaviors(BaseModel): @dataclass
class TurnOnBehaviors(DataClassDictMixin):
"""Model to contain turn on behaviors.""" """Model to contain turn on behaviors."""
#: The behavior when the bulb is turned on programmatically. #: The behavior when the bulb is turned on programmatically.
soft: TurnOnBehavior = Field(alias="soft_on") soft: Annotated[TurnOnBehavior, Alias("soft_on")]
#: The behavior when the bulb has been off from mains power. #: The behavior when the bulb has been off from mains power.
hard: TurnOnBehavior = Field(alias="hard_on") hard: Annotated[TurnOnBehavior, Alias("hard_on")]
TPLINK_KELVIN = { TPLINK_KELVIN = {
@ -303,7 +305,7 @@ class IotBulb(IotDevice):
async def get_turn_on_behavior(self) -> TurnOnBehaviors: async def get_turn_on_behavior(self) -> TurnOnBehaviors:
"""Return the behavior for turning the bulb on.""" """Return the behavior for turning the bulb on."""
return TurnOnBehaviors.parse_obj( return TurnOnBehaviors.from_dict(
await self._query_helper(self.LIGHT_SERVICE, "get_default_behavior") await self._query_helper(self.LIGHT_SERVICE, "get_default_behavior")
) )
@ -314,7 +316,7 @@ class IotBulb(IotDevice):
you should use :func:`get_turn_on_behavior` to get the current settings. you should use :func:`get_turn_on_behavior` to get the current settings.
""" """
return await self._query_helper( return await self._query_helper(
self.LIGHT_SERVICE, "set_default_behavior", behavior.dict(by_alias=True) self.LIGHT_SERVICE, "set_default_behavior", behavior.to_dict()
) )
async def get_light_state(self) -> dict[str, dict]: async def get_light_state(self) -> dict[str, dict]:

View File

@ -176,6 +176,23 @@ MOTION_MODULE = {
} }
} }
LIGHT_DETAILS = {
"color_rendering_index": 80,
"err_code": 0,
"incandescent_equivalent": 60,
"lamp_beam_angle": 150,
"max_lumens": 800,
"max_voltage": 120,
"min_voltage": 110,
"wattage": 10,
}
DEFAULT_BEHAVIOR = {
"err_code": 0,
"hard_on": {"mode": "circadian"},
"soft_on": {"mode": "last_status"},
}
class FakeIotProtocol(IotProtocol): class FakeIotProtocol(IotProtocol):
def __init__(self, info, fixture_name=None, *, verbatim=False): def __init__(self, info, fixture_name=None, *, verbatim=False):
@ -399,6 +416,8 @@ class FakeIotTransport(BaseTransport):
}, },
"smartlife.iot.smartbulb.lightingservice": { "smartlife.iot.smartbulb.lightingservice": {
"get_light_state": light_state, "get_light_state": light_state,
"get_light_details": LIGHT_DETAILS,
"get_default_behavior": DEFAULT_BEHAVIOR,
"transition_light_state": transition_light_state, "transition_light_state": transition_light_state,
"set_preferred_state": set_preferred_state, "set_preferred_state": set_preferred_state,
}, },
@ -409,6 +428,8 @@ class FakeIotTransport(BaseTransport):
"smartlife.iot.lightStrip": { "smartlife.iot.lightStrip": {
"set_light_state": transition_light_state, "set_light_state": transition_light_state,
"get_light_state": light_state, "get_light_state": light_state,
"get_light_details": LIGHT_DETAILS,
"get_default_behavior": DEFAULT_BEHAVIOR,
"set_preferred_state": set_preferred_state, "set_preferred_state": set_preferred_state,
}, },
"smartlife.iot.common.system": { "smartlife.iot.common.system": {

View File

@ -497,3 +497,9 @@ SYSINFO_SCHEMA_BULB = SYSINFO_SCHEMA.extend(
@bulb @bulb
def test_device_type_bulb(dev: Device): def test_device_type_bulb(dev: Device):
assert dev.device_type in {DeviceType.Bulb, DeviceType.LightStrip} assert dev.device_type in {DeviceType.Bulb, DeviceType.LightStrip}
@bulb_iot
async def test_turn_on_behaviours(dev: IotBulb):
behavior = await dev.get_turn_on_behavior()
assert behavior