phlyght/phlyght/models.py

850 lines
28 KiB
Python

from types import new_class
from typing import (
Any,
Literal,
Optional,
Type,
ClassVar,
TypeVar,
)
from uuid import UUID as _UUID, uuid4
from typing import TypeVar, Generic
from enum import Enum, auto
from httpx import AsyncClient
from pydantic import BaseConfig, BaseModel, Field
from pydantic.main import ModelMetaclass, BaseModel
from pydantic.generics import GenericModel
from pydantic.dataclasses import dataclass
from requests import delete
try:
from ujson import dumps, loads
except ImportError:
try:
from orjson import dumps, loads
except ImportError:
from json import dumps, loads
_type = type
_T = TypeVar("_T")
_D = TypeVar("_D", bound=dict)
__all__ = (
"Entity",
"Archetype",
"RoomType",
"Attributes",
"HueEntsV2",
"HueEntsV1",
"_XY",
)
# mypy: enable-incomplete-feature=TypeVarTuple
Ent = TypeVar("Ent", bound="Entity")
def default_uuid():
return UUID(str(uuid4()))
def config_dumps(obj: Any) -> str:
return ret if isinstance(ret := dumps(obj), str) else ret.decode()
class Config(BaseConfig):
json_loads = loads
json_dumps = lambda *args, **kwargs: (
d if isinstance(d := dumps(*args, **kwargs), str) else d.decode()
)
smart_union = True
allow_mutations = True
class HueConfig(BaseConfig):
allow_population_by_field_name = True
json_loads = loads
json_dumps = lambda *args, **kwargs: (
d if isinstance(d := dumps(*args, **kwargs), str) else d.decode()
)
smart_union = True
allow_mutation = True
class UUID(_UUID):
def __json__(self):
return self.__str__()
def validate(*args, **kwargs):
return kwargs
class Entity(BaseModel):
__module__ = "phlyght"
__cache__: ClassVar[dict[str, Type]] = {}
id: Optional[UUID] = Field(
default_factory=lambda: UUID("00000000-0000-0000-0000-000000000000")
)
type: ClassVar[str] = "unknown"
Config = HueConfig
__config__ = HueConfig
@classmethod
def cache_client(cls, client):
Entity.client = client
@classmethod
def get_entities(cls) -> dict[str, Type]:
return cls.__cache__
@classmethod
def get_plural(cls, name):
if name.endswith("y"):
return name[:-1] + "ies"
return name + "s"
def __new__(cls, client=None, **kwargs):
clz = type(cls.__name__, (BaseModel,), {}).__new__(cls)
return clz
async def get(self):
return await getattr(self.client, f"get_{self.type}")(self.id)
async def create(self, **kwargs):
if not hasattr(self.client, f"create_{self.type}"):
return
for k, v in kwargs.items():
if hasattr(self, k) and getattr(self, k) != v:
setattr(self, k, v)
await getattr(self.client, f"create_{self.type}")(self)
async def update(self, **kwargs):
for k, v in kwargs.items():
if hasattr(self, k) and getattr(self, k) != v:
setattr(self, k, v)
await getattr(self.client, f"set_{self.type}")(self.id, self)
async def delete(self):
if _fn := getattr(self.client, f"delete_{self.type}", None):
await _fn(self.id, self)
def __init_subclass__(cls, **_):
super().__init_subclass__()
Entity.__cache__[
_.get_default() if (_ := cls.__fields__.get("type")) else "unknown"
] = cls
def __hash__(self) -> int:
return hash(self.id)
class BaseAttribute(BaseModel):
Config = HueConfig
__config__ = HueConfig
def __init_subclass__(cls, *args, **kwargs):
cls.Config = HueConfig
class RoomType(Enum):
@staticmethod
def _generate_next_value_(name, *_):
return name.lower()
LIVING_ROOM = auto()
KITCHEN = auto()
DINING = auto()
BEDROOM = auto()
KIDS_BEDROOM = auto()
BATHROOM = auto()
NURSERY = auto()
RECREATION = auto()
OFFICE = auto()
GYM = auto()
HALLWAY = auto()
TOILET = auto()
FRONT_DOOR = auto()
GARAGE = auto()
TERRACE = auto()
GARDEN = auto()
DRIVEWAY = auto()
CARPORT = auto()
HOME = auto()
DOWNSTAIRS = auto()
UPSTAIRS = auto()
TOP_FLOOR = auto()
ATTIC = auto()
GUEST_ROOM = auto()
STAIRCASE = auto()
LOUNGE = auto()
MAN_CAVE = auto()
COMPUTER = auto()
STUDIO = auto()
MUSIC = auto()
TV = auto()
READING = auto()
CLOSET = auto()
STORAGE = auto()
LAUNDRY_ROOM = auto()
BALCONY = auto()
PORCH = auto()
BARBECUE = auto()
POOL = auto()
OTHER = auto()
def __json__(self):
return self.value
class Archetype(Enum):
@staticmethod
def _generate_next_value_(name, *_):
return name.lower()
BRIDGE_V2 = auto()
UNKNOWN_ARCHETYPE = auto()
CLASSIC_BULB = auto()
SULTAN_BULB = auto()
FLOOD_BULB = auto()
SPOT_BULB = auto()
CANDLE_BULB = auto()
LUSTER_BULB = auto()
PENDANT_ROUND = auto()
PENDANT_LONG = auto()
CEILING_ROUND = auto()
CEILING_SQUARE = auto()
FLOOR_SHADE = auto()
FLOOR_LANTERN = auto()
TABLE_SHADE = auto()
RECESSED_CEILING = auto()
RECESSED_FLOOR = auto()
SINGLE_SPOT = auto()
DOUBLE_SPOT = auto()
TABLE_WASH = auto()
WALL_LANTERN = auto()
WALL_SHADE = auto()
FLEXIBLE_LAMP = auto()
GROUND_SPOT = auto()
WALL_SPOT = auto()
PLUG = auto()
HUE_GO = auto()
HUE_LIGHTSTRIP = auto()
HUE_IRIS = auto()
HUE_BLOOM = auto()
BOLLARD = auto()
WALL_WASHER = auto()
HUE_PLAY = auto()
VINTAGE_BULB = auto()
CHRISTMAS_TREE = auto()
HUE_CENTRIS = auto()
HUE_LIGHTSTRIP_TV = auto()
HUE_TUBE = auto()
HUE_SIGNE = auto()
def __json__(self):
return f'"{self.value}"'
_T = TypeVar("_T")
@dataclass
class _XY:
x: float
y: float
def __post_init__(self):
if self.x < 0.01:
self.x = 0.01
if self.y < 0.01:
self.y = 0.01
if self.x > 0.99:
self.x = 0.99
if self.y > 0.99:
self.y = 0.99
def __json__(self):
return (
'{"y":' + f'{int(10000*self.x)/10000}, "x": {int(10000*self.y)/10000}' + "}"
)
@dataclass
class On:
on: bool = True
def __json__(self):
return '{"on": ' + f"{self.on}" + "}"
XY = _XY | tuple[float, float]
class Attributes:
class Action(BaseAttribute):
on: "Attributes.On" = Field(default_factory=lambda: Attributes.On(on=True))
dimming: "Attributes.Dimming" = Field(
default_factory=lambda: Attributes.Dimming(
brightness=100.0, min_dim_level=100.0
)
)
color: "Attributes.ColorPoint" = Field(
default_factory=lambda: Attributes.ColorPoint()
)
color_temperature: "Attributes.ScenePaletteColorTemp" = Field(
default_factory=lambda: Attributes.ScenePaletteColorTemp(mirek=500)
)
gradient: "Attributes.Gradient" = Field(
default_factory=lambda: Attributes.Gradient(points_capable=2, points=[])
)
effects: "Attributes.Effects" = Field(
default_factory=lambda: Attributes.Effects()
)
dynamics: "Attributes.SceneDynamics" = Field(
default_factory=lambda: Attributes.SceneDynamics(duration=1)
)
class Actions(BaseAttribute):
target: "Attributes.Identifier" = Field(...)
action: "Attributes.Action" = Field(default_factory=lambda: Attributes.Action())
dimming: "Attributes.Dimming" = Field(
default_factory=lambda: Attributes.Dimming(
brightness=100.0, min_dim_level=100.0
)
)
color: "Attributes.ColorPoint" = Field(
default_factory=lambda: Attributes.ColorPoint()
)
class Alert(BaseAttribute):
action: Literal["breathe", "unknown"] = "unknown"
class Button(BaseAttribute):
last_event: Literal[
"initial_press",
"repeat",
"short_release",
"long_release",
"double_short_release",
"long_press",
] = "initial_press"
class Color(BaseAttribute):
xy: "Attributes.XY" = Field(default_factory=lambda: Attributes.XY(x=0.0, y=0.0))
gamut: "Attributes.Gamut" = Field(default_factory=lambda: Attributes.Gamut())
gamut_type: Literal["A", "B", "C"] = "A"
class ColorPointColor(BaseAttribute):
color: "Attributes.ColorPoint" = Field(
default_factory=lambda: Attributes.ColorPoint()
)
class ColorPoint(BaseAttribute):
xy: "Attributes.XY" = Field(default_factory=lambda: Attributes.XY(x=0.0, y=0.0))
class ColorMirekSchema(BaseAttribute):
mirek_minimum: int = Field(default=153, ge=153, le=500)
mirek_maximum: int = Field(default=500, ge=153, le=500)
class ColorTemp(BaseAttribute):
mirek: Optional[int] = Field(default=0, ge=153, le=500)
mirek_valid: Optional[bool] = True
mirek_schema: Optional["Attributes.ColorMirekSchema"] = Field(
default_factory=lambda: Attributes.ColorMirekSchema()
)
class Dependee(BaseAttribute):
type: str = "unknown"
target: "Attributes.Identifier" = Field(...)
level: str = "unknown"
class Dimming(BaseAttribute):
brightness: Optional[float] = Field(default=100, gt=0, le=100)
min_dim_level: Optional[float] = Field(default=0, ge=0, le=100)
class DimmingDelta(BaseAttribute):
action: Optional[Literal["up", "down", "stop"]] = "stop"
brightness_delta: Optional[float] = Field(default=0, ge=0, le=100)
class Dynamics(BaseAttribute):
status: str = "unknown"
status_values: Optional[list[str]] = Field(default_factory=list)
speed: Optional[float] = Field(default=0.0, ge=0, le=100)
speed_valid: Optional[bool] = True
class Effects(BaseAttribute):
effect: Optional[list[str]] = Field(default_factory=list)
status_values: Optional[list[str]] = Field(default_factory=list)
status: str = "unknown"
effect_values: Optional[list[str]] = Field(default_factory=list)
class EntChannel(BaseAttribute):
channel_id: int = Field(ge=0, le=255)
position: Optional["Attributes.XYZ"] = Field(
default_factory=lambda: Attributes.XYZ(x=0.0, y=0.0, z=0.0)
)
members: list["Attributes.SegmentRef"] = Field(default_factory=list)
class EntLocation(BaseAttribute):
service_location: list["Attributes.ServiceLocation"] = Field(
default_factory=list
)
class Gamut(BaseAttribute):
red: XY = Field(default_factory=lambda: Attributes.XY(x=0.0, y=0.0))
green: XY = Field(default_factory=lambda: Attributes.XY(x=0.0, y=0.0))
blue: XY = Field(default_factory=lambda: Attributes.XY(x=0.0, y=0.0))
class Gradient(BaseAttribute):
points: Optional[list["Attributes.ColorPointColor"]] = Field(
default_factory=list
)
points_capable: Optional[int] = Field(default=1, ge=0, le=255)
class Identifier(BaseAttribute):
rid: UUID = Field(default_factory=default_uuid)
rtype: str = "unknown"
class LightColor(BaseAttribute):
xy: Optional[XY] = Field(default_factory=lambda: _XY(x=0.0, y=0.0))
class LightLevelValue(BaseAttribute):
light_level: Optional[int] = Field(default=0, ge=0, le=100000)
light_level_valid: Optional[bool] = True
class LightEffect(BaseAttribute):
effect: Optional[Literal["fire", "candle", "no_effect"]] = "no_effect"
class Metadata(BaseAttribute):
name: str = "unknown"
archetype: Archetype | RoomType = Archetype.UNKNOWN_ARCHETYPE
image: "Attributes.Identifier" = Field(
default_factory=lambda: Attributes.Identifier(), repr=False
)
class Motion(BaseAttribute):
motion: Optional[bool] = False
motion_valid: Optional[bool] = True
class On(BaseAttribute):
on: Optional[bool] = Field(default=True, alias="on")
class Palette(BaseAttribute):
color: Optional[list["Attributes.PaletteColor"]] = Field(default_factory=list)
dimming: Optional[list["Attributes.Dimming"]] = Field(default_factory=list)
color_temperature: list["Attributes.PaletteTemperature"] = Field(
default_factory=list
)
class PaletteColor(BaseAttribute):
color: Optional["Attributes.ColorPoint"] = Field(
default_factory=lambda: Attributes.ColorPoint()
)
dimming: Optional["Attributes.Dimming"] = Field(
default_factory=lambda: Attributes.Dimming(brightness=100.0)
)
class PaletteTemperature(BaseAttribute):
color_temperature: Optional["Attributes.ScenePaletteColorTemp"] = Field(
default_factory=lambda: Attributes.ScenePaletteColorTemp(mirek=500)
)
dimming: Optional["Attributes.Dimming"] = Field(
default_factory=lambda: Attributes.Dimming(
brightness=100.0, min_dim_level=100.0
)
)
class PowerState(BaseAttribute):
battery_state: Optional[Literal["normal", "low", "critical"]] = "normal"
battery_level: Optional[float] = Field(default=100.0, le=100.0, ge=0.0)
class ProductData(BaseAttribute):
model_id: Optional[str] = "unknown"
manufacturer_name: Optional[str] = "unknown"
product_name: Optional[str] = "unknown"
product_archetype: Optional[Archetype] = Archetype.UNKNOWN_ARCHETYPE
certified: Optional[bool] = False
software_version: Optional[str] = Field(
default="0.0.0", regex=r"\d+\.(?:\d+\.)*\d+"
)
hardware_platform_type: Optional[str] = "unknown"
class RelativeRotary(BaseAttribute):
last_event: Optional["Attributes.RotaryEvent"] = Field(
default_factory=lambda: Attributes.RotaryEvent()
)
class RotaryEvent(BaseAttribute):
action: Optional[Literal["start", "repeat", "unknown"]] = "unknown"
rotation: Optional["Attributes.RotaryRotation"] = Field(
default_factory=lambda: Attributes.RotaryRotation(
direction="clock_wise", duration=0.0, steps=0
)
)
class RotaryRotation(BaseAttribute):
direction: Literal["clock_wise", "counter_clock_wise"] = "clock_wise"
duration: Optional[float] = Field(0.0, ge=0.0)
steps: Optional[int] = Field(0, ge=0)
class SceneDynamics(BaseAttribute):
duration: Optional[int] = Field(0, ge=0)
class SceneEffects(BaseAttribute):
effect: Optional[Literal["fire", "candle", "no_effect"]] = "no_effect"
class ScenePaletteColorTemp(BaseAttribute):
mirek: int = Field(153, ge=153, le=500)
class Segment(BaseAttribute):
start: int = Field(0, ge=0)
length: int = Field(1, ge=1)
class SegmentManager(BaseAttribute):
configurable: Optional[bool] = True
max_segments: Optional[int] = Field(default=1, ge=1)
segments: Optional[list["Attributes.Segment"]] = Field(default_factory=list)
class SegmentRef(BaseAttribute):
service: "Attributes.Identifier" = Field(
default_factory=lambda: Attributes.Identifier()
)
index: int = 0
class ServiceLocation(BaseAttribute):
service: "Attributes.Identifier" = Field(
default_factory=lambda: Attributes.Identifier()
)
position: "Attributes.XYZ" = Field(
default_factory=lambda: Attributes.XYZ(x=0.0, y=0.0, z=0.0)
)
positions: list[Type["Attributes.XYZ"]] = Field(max_items=2, min_items=1)
class StreamProxy(BaseAttribute):
mode: Literal["auto", "manual"] = "manual"
node: "Attributes.Identifier" = Field(
default_factory=lambda: Attributes.Identifier()
)
class Temp(BaseAttribute):
temperature: float = Field(default=0.0, lt=100.0, gt=-100.0)
temperature_valid: bool = True
class TimedEffects(BaseAttribute):
effect: Optional[str] = "none"
duration: Optional[float] = Field(default=0.0, ge=0.0)
status_values: Optional[list[str]] = Field(default_factory=list)
status: Optional[str] = "unknown"
effect_values: Optional[list[str]] = Field(default_factory=list)
class XY(BaseAttribute):
x: float = Field(0.0, ge=0.0, le=1.0)
y: float = Field(0.0, ge=0.0, le=1.0)
class XYZ(BaseAttribute):
x: float = Field(ge=-1.0, le=1.0)
y: float = Field(ge=-1.0, le=1.0)
z: float = Field(ge=-1.0, le=1.0)
class HueEntsV1:
class UserConfiguration(Entity):
name: str
swupdate: dict
swupdate2: dict
whitelist: list[str]
portalstate: dict
apiversion: str
swversion: str
proxyaddress: str
v: BaseModel
for k, v in Attributes.__dict__.items():
if k.startswith("__"):
continue
if isinstance(v, type) and issubclass(v, BaseModel):
v.update_forward_refs()
class HueEntsV2:
class BehaviorInstance(Entity):
type: ClassVar[str] = "beheavior_instance"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
script_id: str = Field("", regex=r"^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$")
enabled: bool = True
state: dict[str, Any] = Field(default_factory=dict)
configuration: dict[str, Any] = Field(default_factory=dict)
dependees: list[Attributes.Dependee] = Field(default_factory=list)
status: Literal[
"initializing", "running", "disabled", "errored"
] = "initializing"
last_error: str = "none"
metadata: dict[Literal["name"], str] = Field(default_factory=dict)
migrated_from: str = "unknown"
class BehaviorScript(Entity):
type: ClassVar[str] = "behavior_script"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
description: str = ""
configuration_schema: dict[Any, Any] = Field(default_factory=dict)
trigger_schema: dict[Any, Any] = Field(default_factory=dict)
state_schema: dict[Any, Any] = Field(default_factory=dict)
version: str = Field("0.0.1", regex=r"^[0-9]+\.[0-9]+\.[0-9]+$")
metadata: dict[Any, Any] = Field(default_factory=dict)
supported_features: list[str] = Field(default_factory=list)
max_number_of_instances: int = Field(default=0, ge=0, le=255)
class Bridge(Entity):
type: ClassVar[str] = "bridge"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
bridge_id: str = ""
time_zone: dict[str, str] = Field(default_factory=dict)
class BridgeHome(Entity):
type: ClassVar[str] = "bridge_home"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
services: list[Attributes.Identifier] = Field(default_factory=list)
children: list[Attributes.Identifier] = Field(default_factory=list)
class Button(Entity):
type: ClassVar[str] = "button"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
metadata: dict[Literal["control_id"], int] = Field(default_factory=dict)
button: Attributes.Button = Field(default_factory=Attributes.Button)
class Device(Entity):
type: ClassVar[str] = "device"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
services: list[Attributes.Identifier] = Field(default_factory=list)
metadata: Attributes.Metadata = Field(default_factory=Attributes.Metadata)
product_data: Attributes.ProductData = Field(
default_factory=lambda: Attributes.ProductData()
)
class DevicePower(Entity):
type: ClassVar[str] = "device_power"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
power_state: Attributes.PowerState = Field(
default_factory=Attributes.PowerState
)
class Entertainment(Entity):
type: ClassVar[str] = "entertainment"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
renderer: bool = False
proxy: bool = False
max_streams: int = Field(1, ge=1)
segments: Attributes.SegmentManager = Field(
default_factory=Attributes.SegmentManager
)
class EntertainmentConfiguration(Entity):
type: ClassVar[str] = "entertainment_configuration"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
metadata: dict[Literal["name"], str] = Field(default_factory=dict)
name: str = ""
configuration_type: Optional[
Literal["screen", "monitor", "music", "3dspace", "other"]
] = "screen"
status: Literal["active", "inactive"] = "inactive"
active_streamer: Attributes.Identifier = Field(
default_factory=Attributes.Identifier
)
stream_proxy: Attributes.StreamProxy = Field(
default_factory=Attributes.StreamProxy
)
channels: list[Attributes.EntChannel] = Field(default_factory=list)
locations: Attributes.EntLocation = Field(
default_factory=Attributes.EntLocation
)
light_services: list[Attributes.Identifier] = Field(default_factory=list)
class GeofenceClient(Entity):
type: ClassVar[str] = "geofence_client"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
name: str = ""
class Geolocation(Entity):
type: ClassVar[str] = "geolocation"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
is_configured: bool = False
class GroupedLight(Entity):
type: ClassVar[str] = "grouped_light"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
on: Attributes.On = Field(default_factory=Attributes.On)
alert: Attributes.Alert = Field(default_factory=Attributes.Alert)
class Homekit(Entity):
id: UUID
type: ClassVar[str] = "resource"
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
status: Literal["paired", "pairing", "unpaired"] = "unpaired"
class Light(Entity):
# id: UUID
id_v1: Optional[str] = Field(
default="", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$", exclude=True
)
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
metadata: Attributes.Metadata = Field(default_factory=Attributes.Metadata)
on: Attributes.On = Field(default_factory=Attributes.On)
dimming: Attributes.Dimming = Field(default_factory=Attributes.Dimming)
dimming_delta: Attributes.DimmingDelta = Field(
default_factory=Attributes.DimmingDelta
)
color_temperature: Attributes.ColorTemp = Field(
default_factory=Attributes.ColorTemp
)
color_temperature_delta: dict = Field(default_factory=dict)
color: Attributes.LightColor = Field(default_factory=Attributes.LightColor)
gradient: Attributes.Gradient = Field(default_factory=Attributes.Gradient)
dynamics: Attributes.Dynamics = Field(default_factory=Attributes.Dynamics)
alert: Attributes.Alert = Field(default_factory=Attributes.Alert)
signaling: dict = Field(default_factory=dict)
mode: Literal["normal", "streaming"] = "normal"
effects: Attributes.LightEffect = Field(
default_factory=lambda: Attributes.LightEffect(effect="fire")
)
timed_effects: Attributes.TimedEffects = Field(
default_factory=Attributes.TimedEffects
)
type: ClassVar[str] = "light"
class LightLevel(Entity):
type: ClassVar[str] = "light_level"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
enabled: bool = True
light: Attributes.LightLevelValue = Field(
default_factory=Attributes.LightLevelValue
)
class Motion(Entity):
type: ClassVar[str] = "motion"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
enabled: bool = True
motion: Attributes.Motion = Field(default_factory=Attributes.Motion)
class RelativeRotary(Entity):
id: UUID
type: ClassVar[str] = "relative_rotary"
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
relative_rotary: Attributes.RelativeRotary = Field(
default_factory=Attributes.RelativeRotary
)
class Resource(Entity):
id: UUID
type: ClassVar[str] = "device"
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
class Room(Entity):
type: ClassVar[str] = "room"
id: Optional[UUID]
id_v1: Optional[str] = Field(
"", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$"
)
services: Optional[list[Attributes.Identifier]] = Field(
default_factory=list, alias="service"
)
metadata: Optional[Attributes.Metadata] = Field(
default_factory=Attributes.Metadata
)
children: Optional[list[Attributes.Identifier]] = Field(default_factory=list)
class Scene(Entity):
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
metadata: Attributes.Metadata = Field(default_factory=Attributes.Metadata)
group: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
actions: list[Attributes.Actions] = Field(default_factory=list)
palette: Attributes.Palette = Field(default_factory=Attributes.Palette)
speed: float = 0.0
auto_dynamic: bool = False
type: ClassVar[str] = "scene"
class Temperature(Entity):
type: ClassVar[str] = "temperature"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
enabled: bool = True
temperature: Attributes.Temp = Field(default_factory=Attributes.Temp)
class ZGPConnectivity(Entity):
type: ClassVar[str] = "zgp_connectivity"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
status: Optional[
Literal[
"connected",
"disconnected",
"connectivity_issue",
"unidirectional_incoming",
]
] = "connected"
source_id: str = ""
class ZigbeeConnectivity(Entity):
type: ClassVar[str] = "zigbee_connectivity"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: Attributes.Identifier = Field(default_factory=Attributes.Identifier)
status: Optional[
Literal[
"connected",
"disconnected",
"connectivity_issue",
"unidirectional_incoming",
]
] = "connected"
mac_address: str = Field("", regex=r"^(?:[0-9a-fA-F]{2}(?:-|:)?){6}$")
class Zone(Entity):
type: ClassVar[str] = "zone"
id: UUID
id_v1: str = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
services: list[Attributes.Identifier] = Field(
default_factory=list, alias="service"
)
metadata: Attributes.Metadata = Field(default_factory=Attributes.Metadata)
children: list[Attributes.Identifier] = Field(default_factory=list)
for k, v in HueEntsV2.__dict__.items():
if k.startswith("__"):
continue
if isinstance(v, type) and issubclass(v, BaseModel):
v.update_forward_refs()