Source code for aioafero.v1.models.features
"""Feature Schemas used by various Afero resources."""
from dataclasses import dataclass
from enum import Enum
from typing import Any
from aioafero.util import percentage_to_ordered_list_item
[docs]
@dataclass(frozen=True, slots=True)
class AferoValueEmission:
"""One outbound Afero state derived from a feature."""
function_class: str
function_instance: str | None
value: Any
[docs]
@dataclass(kw_only=True)
class AferoFeature:
"""Base feature with required Afero function identity."""
function_class: str
function_instance: str | None
@property
def api_value(self) -> Any:
"""Value member to send to the Afero API."""
raise NotImplementedError
[docs]
def iter_afero_values(self) -> list[AferoValueEmission]:
"""Return outbound states owned by this feature."""
return [
AferoValueEmission(
self.function_class,
self.function_instance,
self.api_value,
)
]
[docs]
@dataclass(kw_only=True)
class ColorModeFeature(AferoFeature):
"""Represent the current mode (ie white, color) Feature object."""
mode: str
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return self.mode
[docs]
@dataclass(kw_only=True)
class ColorFeature(AferoFeature):
"""Represent `RGB` Feature object."""
red: int
green: int
blue: int
@property
def api_value(self) -> dict[str, dict[str, int]]:
"""Value to send to Afero API."""
return {
"color-rgb": {
"r": self.red,
"g": self.green,
"b": self.blue,
}
}
[docs]
@dataclass(kw_only=True)
class ColorTemperatureFeature(AferoFeature):
"""Represent Current temperature Feature."""
temperature: int
supported: list[int]
prefix: str = ""
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return f"{self.temperature}{self.prefix}"
[docs]
class CurrentPositionEnum(Enum):
"""Enum with available current position modes."""
LOCKED = "locked"
LOCKING = "locking"
UNKNOWN = "unknown"
UNLOCKED = "unlocked"
UNLOCKING = "unlocking"
@classmethod
def _missing_(cls, value):
return cls.UNKNOWN
[docs]
@dataclass(kw_only=True)
class CurrentPositionFeature(AferoFeature):
"""Represents the current position of the lock."""
position: CurrentPositionEnum
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return self.position.value
[docs]
@dataclass(kw_only=True)
class CurrentTemperatureFeature(AferoFeature):
"""Represents the current temperature."""
temperature: float
@property
def api_value(self) -> float:
"""Value to send to Afero API."""
return round(self.temperature, 1)
[docs]
@dataclass(kw_only=True)
class DimmingFeature(AferoFeature):
"""Represent Current temperature Feature."""
brightness: int
supported: list[int]
@property
def api_value(self) -> int:
"""Value to send to Afero API."""
return self.brightness
[docs]
@dataclass(kw_only=True)
class DirectionFeature(AferoFeature):
"""Represent Current Fan direction Feature."""
forward: bool
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return "forward" if self.forward else "reverse"
[docs]
@dataclass(kw_only=True)
class EffectFeature(AferoFeature):
"""Represent the current effect."""
effect: str
effects: dict[str, set[str]]
@property
def api_value(self) -> str:
"""Canonical effect value; multi-state emission uses ``iter_afero_values``."""
return self.effect
[docs]
def iter_afero_values(self) -> list[AferoValueEmission]:
"""Emit ordered color-sequence states for the selected effect."""
seq_key = next(
(
effect_group
for effect_group, effects in self.effects.items()
if self.effect in effects
),
None,
)
if seq_key is None:
return []
preset_val = self.effect if self.is_preset(self.effect) else seq_key
states = [
AferoValueEmission(self.function_class, "preset", preset_val),
]
if seq_key != "preset":
states.append(AferoValueEmission(self.function_class, seq_key, self.effect))
return states
[docs]
def is_preset(self, effect: str) -> bool:
"""Determine if the current state is a preset effect."""
return effect in self.effects.get("preset", ())
[docs]
@dataclass(kw_only=True)
class HVACModeFeature(AferoFeature):
"""Represent HVAC Mode Feature."""
mode: str | None
previous_mode: str | None
supported_modes: set[str]
modes: set[str]
@property
def api_value(self) -> str | None:
"""Value to send to Afero API."""
return self.mode
[docs]
@dataclass(kw_only=True)
class ModeFeature(AferoFeature):
"""Represent Current Fan mode Feature."""
mode: str | None
modes: set[str]
@property
def api_value(self) -> str | None:
"""Value to send to Afero API."""
return self.mode
[docs]
@dataclass(kw_only=True)
class NumbersFeature(AferoFeature):
"""Represents a numeric value."""
value: float
min: float
max: float
step: float
name: str | None
unit: str | None
@property
def api_value(self) -> float:
"""Value to send to Afero API."""
return self.value
[docs]
@dataclass(kw_only=True)
class OnFeature(AferoFeature):
"""Represent `On` Feature object as used by various Afero resources."""
on: bool
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return "on" if self.on else "off"
[docs]
@dataclass(kw_only=True)
class OpenFeature(AferoFeature):
"""Represent `Open` Feature object."""
open: bool
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return "on" if self.open else "off"
[docs]
@dataclass(kw_only=True)
class PresetFeature(AferoFeature):
"""Represent the current preset."""
enabled: bool
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return "enabled" if self.enabled else "disabled"
[docs]
@dataclass(kw_only=True)
class SecuritySensorConfigFeature(AferoFeature):
"""Represent the current security sensor configuration."""
sensor_id: int
chirpMode: int # noqa: N815
triggerType: int # noqa: N815
bypassType: int # noqa: N815
key_name: str
@property
def api_value(self) -> dict[str, dict[str, int]]:
"""Value to send to Afero API."""
return {
self.key_name: {
"chirpMode": self.chirpMode,
"triggerType": self.triggerType,
"bypassType": self.bypassType,
}
}
[docs]
@dataclass(kw_only=True)
class SelectFeature(AferoFeature):
"""Represent available options and currently selected."""
selected: str
selects: set[str]
name: str
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return self.selected
[docs]
@dataclass(kw_only=True)
class SecuritySystemDisarmPin(AferoFeature):
"""Represent the disarm pin feature."""
pin: int
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return str(self.pin)
[docs]
@dataclass(kw_only=True)
class SecuritySensorSirenFeature(AferoFeature):
"""Represent the current state of the siren."""
result_code: int | None
command: int | None
@property
def api_value(self) -> dict[str, dict[str, int | None]] | None:
"""Value to send to Afero API."""
if self.result_code is None and self.command is None:
return None
return {
"security-siren-action": {
"resultCode": self.result_code,
"command": self.command,
}
}
[docs]
@dataclass(kw_only=True)
class SpeedFeature(AferoFeature):
"""Represent Current Fan speed Feature."""
speed: int
speeds: list[str]
@property
def api_value(self) -> str:
"""Value to send to Afero API."""
return percentage_to_ordered_list_item(self.speeds, self.speed)
[docs]
@dataclass(kw_only=True)
class TargetTemperatureFeature(AferoFeature):
"""Represents the target temperature for auto."""
value: float
min: float
max: float
step: float
@property
def api_value(self) -> float:
"""Value to send to Afero API."""
return self.value