Restructure public config and deployment
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from ha_deconz_bridge.color import Color
|
||||
from ha_deconz_bridge.lights import ChannelSpec, ControllerModeSpec, ControllerSpec, VirtualLightSpec
|
||||
|
||||
|
||||
RGB_RED = (0.68, 0.31)
|
||||
RGB_GREEN = (0.18, 0.70)
|
||||
RGB_BLUE = (0.14, 0.05)
|
||||
CT_RANGE = (153, 500)
|
||||
DAYLIGHT_MIREDS = 182
|
||||
|
||||
|
||||
def define_lights(raw_lights: Mapping[str, Mapping[str, Any]]) -> dict[str, VirtualLightSpec]:
|
||||
return {
|
||||
light_name: _virtual_light(light_name, raw_spec)
|
||||
for light_name, raw_spec in raw_lights.items()
|
||||
}
|
||||
|
||||
|
||||
def _virtual_light(light_name: str, raw_spec: Mapping[str, Any]) -> VirtualLightSpec:
|
||||
controllers = {
|
||||
controller_id: _controller_spec(
|
||||
controller_id,
|
||||
raw_controller,
|
||||
default_name=light_name,
|
||||
)
|
||||
for controller_id, raw_controller in raw_spec["controllers"].items()
|
||||
}
|
||||
return VirtualLightSpec(
|
||||
name=str(raw_spec.get("name", light_name)),
|
||||
supported_color_modes=tuple(raw_spec["supported_color_modes"]),
|
||||
controllers=controllers,
|
||||
)
|
||||
|
||||
|
||||
def _controller_spec(
|
||||
controller_id: str,
|
||||
raw_controller: Mapping[str, Any] | ControllerSpec,
|
||||
*,
|
||||
default_name: str,
|
||||
) -> ControllerSpec:
|
||||
if isinstance(raw_controller, ControllerSpec):
|
||||
if raw_controller.zigbee_id != controller_id:
|
||||
raise ValueError(
|
||||
f"Controller key {controller_id!r} does not match spec zigbee_id {raw_controller.zigbee_id!r}."
|
||||
)
|
||||
return raw_controller
|
||||
|
||||
mireds = raw_controller.get("mireds")
|
||||
if mireds is not None:
|
||||
mireds = tuple(sorted(int(value) for value in mireds))
|
||||
raw_modes = raw_controller.get("modes")
|
||||
if raw_modes is None:
|
||||
raw_modes = {
|
||||
key: value
|
||||
for key, value in raw_controller.items()
|
||||
if key in {"onoff", "brightness", "rgb", "color_temp"}
|
||||
}
|
||||
modes = {
|
||||
mode_name: _mode_spec(mode_name, raw_mode, mireds=mireds)
|
||||
for mode_name, raw_mode in raw_modes.items()
|
||||
}
|
||||
return ControllerSpec(
|
||||
zigbee_id=controller_id,
|
||||
name=str(raw_controller.get("name", default_name)),
|
||||
modes=modes,
|
||||
mireds=mireds,
|
||||
)
|
||||
|
||||
|
||||
def _mode_spec(
|
||||
mode_name: str,
|
||||
raw_mode: Mapping[str, Any],
|
||||
*,
|
||||
mireds: tuple[int, int] | None,
|
||||
) -> ControllerModeSpec:
|
||||
channels = raw_mode.get("channels", raw_mode)
|
||||
return ControllerModeSpec(
|
||||
name=mode_name,
|
||||
channels={
|
||||
role: _channel_spec(role, raw_channel, mode_name=mode_name, mireds=mireds)
|
||||
for role, raw_channel in channels.items()
|
||||
if role not in {"name", "channels"}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _channel_spec(
|
||||
role: str,
|
||||
raw_channel: Mapping[str, Any],
|
||||
*,
|
||||
mode_name: str,
|
||||
mireds: tuple[int, int] | None,
|
||||
) -> ChannelSpec:
|
||||
label = raw_channel.get("label", raw_channel.get("semantic_label"))
|
||||
return ChannelSpec(
|
||||
role,
|
||||
_parse_color(
|
||||
raw_channel["color"],
|
||||
brightness=raw_channel.get("brightness"),
|
||||
temp_range=mireds or CT_RANGE,
|
||||
),
|
||||
label,
|
||||
)
|
||||
|
||||
|
||||
def _parse_color(
|
||||
value: Color | int | float | tuple[float, ...] | list[float],
|
||||
*,
|
||||
brightness: float | int | None,
|
||||
temp_range: tuple[int, int],
|
||||
) -> Color:
|
||||
output = 1.0 if brightness is None else float(brightness)
|
||||
if isinstance(value, Color):
|
||||
return value.with_brightness(output) if brightness is not None else value
|
||||
if isinstance(value, int | float):
|
||||
mireds = float(value)
|
||||
if mireds >= 1000.0:
|
||||
mireds = 1_000_000.0 / mireds
|
||||
return Color.from_color_temp(mireds, brightness=output, temp_range=temp_range)
|
||||
|
||||
components = tuple(float(component) for component in value)
|
||||
if len(components) == 2:
|
||||
return Color.from_xy(components, brightness=output)
|
||||
if len(components) == 3:
|
||||
scale = 255.0 if any(component > 1.0 for component in components) else 1.0
|
||||
return Color.from_rgb(tuple(component / scale for component in components), brightness=output)
|
||||
raise ValueError(f"Unsupported color value {value!r}.")
|
||||
Reference in New Issue
Block a user