phlyght/phlyght/models.py

573 lines
14 KiB
Python
Raw Normal View History

2022-11-28 10:48:43 +00:00
from typing import Any, Literal, Optional, Generic, TypeAlias, TypeVar
2022-11-28 04:03:57 +00:00
from uuid import UUID
from dataclasses import dataclass
from enum import Enum, auto
2022-11-28 08:54:10 +00:00
from pydantic import BaseModel, Field
__all__ = (
"Archetype",
2022-11-28 10:48:43 +00:00
"RoomType",
2022-11-28 08:54:10 +00:00
"Room",
"Light",
"Scene",
"Zone",
"BridgeHome",
"GroupedLight",
"Device",
"Bridge",
"DevicePower",
"ZigbeeConnectivity",
"ZGPConnectivity",
"Motion",
"Temperature",
"LightLevel",
"Button",
"BehaviorScript",
"BehaviorInstance",
"GeofenceClient",
"Geolocation",
"EntertainmentConfiguration",
2022-11-28 10:48:43 +00:00
"Entertainment",
"Resource",
"Homekit",
2022-11-28 08:54:10 +00:00
)
2022-11-28 10:48:43 +00:00
_T_M: TypeAlias = "Archetype | Room | Light | Scene | Zone | BridgeHome | GroupedLight | Device | Bridge | DevicePower | ZigbeeConnectivity | ZGPConnectivity | Motion | Temperature | LightLevel | Button | BehaviorScript | BehaviorInstance | GeofenceClient | Geolocation | EntertainmentConfiguration | Entertainment | Resource | Homekit"
_T = TypeVar("_T")
2022-11-28 10:48:43 +00:00
class RoomType(Enum):
@staticmethod
def _generate_next_value_(name, start, count, last_values):
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()
class Archetype(Enum):
@staticmethod
def _generate_next_value_(name, start, count, last_values):
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()
@dataclass
2022-11-28 08:54:10 +00:00
class _Dimming:
brightness: float
min_dim_level: Optional[float] = Field(0, repr=False)
@dataclass
2022-11-28 08:54:10 +00:00
class _XY:
x: float
y: float
2022-11-28 04:03:57 +00:00
@dataclass
2022-11-28 08:54:10 +00:00
class _On:
on: bool = Field(..., alias="on")
2022-11-28 04:03:57 +00:00
@dataclass
2022-11-28 08:54:10 +00:00
class _ColorPoint:
xy: _XY
2022-11-28 04:03:57 +00:00
@dataclass(frozen=True)
2022-11-28 08:54:10 +00:00
class _Identifier:
rid: str
rtype: str
2022-11-28 04:03:57 +00:00
@dataclass(frozen=True)
2022-11-28 08:54:10 +00:00
class _Metadata:
name: str
2022-11-28 10:48:43 +00:00
archetype: Optional[Archetype | RoomType] = Archetype.UNKNOWN_ARCHETYPE
2022-11-28 08:54:10 +00:00
image: Optional[_Identifier] = Field(None, repr=False)
2022-11-28 08:54:10 +00:00
class _HueGroupedMeta(type):
def update(cls):
for v in cls.__dict__.values():
if hasattr(v, "update_forward_refs"):
v.update_forward_refs()
2022-11-28 08:54:10 +00:00
class _HueGrouped(metaclass=_HueGroupedMeta):
...
2022-11-28 08:54:10 +00:00
class _Lights(_HueGrouped):
2022-11-28 04:03:57 +00:00
class ColorTemperature(BaseModel):
mirek: Optional[int]
mirek_valid: bool
mirek_schema: dict[str, float]
2022-11-28 04:03:57 +00:00
class Gamut(BaseModel):
2022-11-28 08:54:10 +00:00
red: _XY
green: _XY
blue: _XY
2022-11-28 04:03:57 +00:00
class Color(BaseModel):
2022-11-28 08:54:10 +00:00
xy: _XY
gamut: "_Lights.Gamut"
2022-11-28 04:03:57 +00:00
gamut_type: Literal["A"] | Literal["B"] | Literal["C"]
class Dynamics(BaseModel):
status: str
status_values: list[str]
speed: float
speed_valid: bool
class Gradient(BaseModel):
2022-11-28 08:54:10 +00:00
points: list[_ColorPoint]
2022-11-28 04:03:57 +00:00
points_capable: int
class Effects(BaseModel):
effect: Optional[list[str]] = Field(repr=False)
status_values: list[str] = Field(repr=False)
status: str
effect_values: list[str] = Field(repr=False)
2022-11-28 04:03:57 +00:00
class TimedEffects(BaseModel):
effect: str
2022-11-28 04:03:57 +00:00
duration: int
status_values: list[str] = Field(repr=False)
status: str
effect_values: list[str] = Field(repr=False)
2022-11-28 04:03:57 +00:00
class Light(Generic[_T], BaseModel):
2022-11-28 04:03:57 +00:00
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field(
..., regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$"
)
2022-11-28 08:54:10 +00:00
owner: _Identifier
metadata: _Metadata
on: _On = Field(repr=False)
dimming: _Dimming
2022-11-28 04:03:57 +00:00
dimming_delta: dict
color_temperature: Optional["_Lights.ColorTemperature"]
2022-11-28 04:03:57 +00:00
color_temperature_delta: Optional[dict]
color: Optional["_Lights.Color"]
gradient: Optional["_Lights.Gradient"]
dynamics: "_Lights.Dynamics"
alert: dict[str, list[str]]
2022-11-28 04:03:57 +00:00
signaling: dict
mode: str
effects: "_Lights.Effects"
type: Literal["light"]
_Lights.update()
2022-11-28 08:54:10 +00:00
class _Scenes(_HueGrouped):
class Action(BaseModel):
2022-11-28 08:54:10 +00:00
on: Optional[_On]
dimming: Optional[_Dimming]
color: Optional[_ColorPoint]
color_temperature: Optional[dict[str, float]]
2022-11-28 08:54:10 +00:00
gradient: Optional[dict[str, list[_ColorPoint]]]
effects: Optional[dict[str, str]]
dynamics: Optional[dict[str, float]]
class Actions(BaseModel):
2022-11-28 08:54:10 +00:00
target: _Identifier
action: "_Scenes.Action" = Field(repr=False)
2022-11-28 08:54:10 +00:00
dimming: Optional[_Dimming]
color: Optional[_ColorPoint]
class PaletteColor(BaseModel):
2022-11-28 08:54:10 +00:00
color: _ColorPoint
dimming: _Dimming
class PaletteTemperature(BaseModel):
color_temperature: dict[str, float]
2022-11-28 08:54:10 +00:00
dimming: _Dimming
class Palette(BaseModel):
color: list["_Scenes.PaletteColor"]
2022-11-28 08:54:10 +00:00
dimming: Optional[list[_Dimming]]
color_temperature: list["_Scenes.PaletteTemperature"]
class Scene(BaseModel):
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field(
..., regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$"
)
2022-11-28 08:54:10 +00:00
metadata: _Metadata
group: _Identifier
actions: list["_Scenes.Actions"]
palette: "_Scenes.Palette"
speed: float
auto_dynamic: bool
type: Literal["scene"]
_Scenes.update()
class Room(BaseModel):
type: Literal["room"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
services: list[_Identifier]
metadata: _Metadata
children: list[_Identifier]
class Zone(BaseModel):
type: Literal["zone"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
services: list[_Identifier]
metadata: _Metadata
children: list[_Identifier]
class BridgeHome(BaseModel):
type: Literal["bridge_home"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
services: list[_Identifier]
children: list[_Identifier]
class GroupedLight(BaseModel):
type: Literal["grouped_light"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
on: _On = Field(repr=False)
2022-11-28 10:48:43 +00:00
alert: dict[str, list[str]]
2022-11-28 08:54:10 +00:00
class _ProductData(BaseModel):
model_id: str
manufacturer_name: str
product_name: str
product_archetype: Archetype
certified: bool
software_version: Optional[str]
hardware_platform_type: Optional[str]
class Device(BaseModel):
type: Literal["device"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
services: list[_Identifier]
metadata: _Metadata
product_data: _ProductData
class Bridge(BaseModel):
type: Literal["bridge"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
bridge_id: str
time_zone: dict[str, str]
2022-11-28 08:54:10 +00:00
class _PowerState(BaseModel):
battery_state: Literal["normal", "low", "critical"]
2022-11-28 10:48:43 +00:00
battery_level: float = Field(le=100.0, ge=0.0)
class DevicePower(BaseModel):
type: Literal["device_power"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
owner: _Identifier
power_state: _PowerState
class ZigbeeConnectivity(BaseModel):
type: Literal["zigbee_connectivity"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
owner: _Identifier
status: Literal[
"connected", "disconnected", "connectivity_issue", "unidirectional_incoming"
]
mac_address: str
class ZGPConnectivity(BaseModel):
type: Literal["zgp_connectivity"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
owner: _Identifier
status: Literal[
"connected", "disconnected", "connectivity_issue", "unidirectional_incoming"
]
source_id: str
class Motion(BaseModel):
type: Literal["motion"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
owner: _Identifier
enabled: bool
motion: dict[str, bool]
class _Temp(BaseModel):
temperature: float = Field(lt=100.0, gt=-100.0)
temperature_valid: bool
class Temperature(BaseModel):
type: Literal["temperature"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
owner: _Identifier
enabled: bool
temperature: _Temp
class _Light(BaseModel):
light_level: int
light_level_valid: bool
class LightLevel(BaseModel):
type: Literal["light_level"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
owner: _Identifier
enabled: bool
light: _Light
class Button(BaseModel):
type: Literal["button"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 08:54:10 +00:00
owner: _Identifier
metadata: dict[Literal["control_id"], int]
button: dict[
Literal["last_event"],
Literal[
"initial_press",
"repeat",
"short_release",
"long_release",
"double_short_release",
],
]
class BehaviorScript(BaseModel):
type: Literal["behavior_script"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
description: str
configuration_schema: dict[str, Any]
trigger_schema: dict[str, Any]
state_schema: dict[str, Any]
version: str
metadata: dict[str, str]
2022-11-28 08:54:10 +00:00
class _Dependee(BaseModel):
type: str
2022-11-28 08:54:10 +00:00
target: _Identifier
level: str
class BehaviorInstance(BaseModel):
type: Literal["behavior_instance"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
script_id: str
enabled: bool
state: Optional[dict[str, Any]]
configuration: dict[str, Any]
2022-11-28 08:54:10 +00:00
dependees: list[_Dependee]
status: Literal["initializing", "running", "disabled", "errored"]
last_error: str
metadata: dict[Literal["name"], str]
migrated_from: Optional[str] = None
class GeofenceClient(BaseModel):
type: Literal["geofence_client"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
name: str
class Geolocation(BaseModel):
type: Literal["geolocation"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
is_configured: bool = False
2022-11-28 08:54:10 +00:00
class _StreamProxy(BaseModel):
mode: Literal["auto", "manual"]
2022-11-28 08:54:10 +00:00
node: _Identifier
2022-11-28 10:48:43 +00:00
class _XYZ(BaseModel):
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 _SegmentRef(BaseModel):
service: _Identifier
index: int
class _EntertainmentChannel(BaseModel):
channel_id: int = Field(ge=0, le=255)
position: _XYZ
members: list[_SegmentRef]
class _ServiceLocation(BaseModel):
service: _Identifier
position: _XYZ
positions: list[_XYZ] = Field(max_items=2, min_items=1)
class _EntertainmentLocation(BaseModel):
service_location: Optional[list[_ServiceLocation]] = []
class EntertainmentConfiguration(BaseModel):
type: Literal["entertainment_configuration"]
id: UUID
2022-11-28 10:48:43 +00:00
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
metadata: dict[Literal["name"], str]
name: Optional[str] = ""
configuration_type: Literal["screen", "monitor", "music", "3dspace", "other"]
status: Literal["active", "inactive"]
2022-11-28 10:48:43 +00:00
active_streamer: Optional[_Identifier] = None
2022-11-28 08:54:10 +00:00
stream_proxy: _StreamProxy
2022-11-28 10:48:43 +00:00
channels: list[_EntertainmentChannel]
locations: Optional[_EntertainmentLocation] = None
light_services: list[_Identifier]
class _Segment(BaseModel):
start: int = Field(..., ge=0)
length: int = Field(..., ge=1)
class _SegmentManager(BaseModel):
configurable: bool
max_segments: int = Field(..., ge=1)
segments: list[_Segment]
class Entertainment(BaseModel):
2022-11-28 10:48:43 +00:00
type: Literal["entertainment"]
id: UUID
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
owner: _Identifier
renderer: bool
proxy: bool
max_streams: Optional[int] = Field(1, ge=1)
segments: Optional[_SegmentManager] = None
class Homekit(BaseModel):
2022-11-28 10:48:43 +00:00
id: UUID
type: Optional[str]
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
status: Literal["paired", "pairing", "unpaired"]
class Resource(BaseModel):
2022-11-28 10:48:43 +00:00
id: UUID
type: Optional[str]
id_v1: Optional[str] = Field("", regex=r"^(\/[a-z]{4,32}\/[0-9a-zA-Z-]{1,32})?$")
2022-11-28 04:03:57 +00:00
Light = _Lights.Light
Scene = _Scenes.Scene