Initial public release

This commit is contained in:
ajp_anton
2026-05-30 15:44:40 +00:00
commit cc4e0c2c0f
56 changed files with 11840 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
from ha_deconz_bridge.settings import Settings
from configs.example_actions import AUTOMATIONS
from configs.example_groups import GROUPS
from configs.example_lights import LIGHTS
from configs.example_remotes import REMOTES
SETTINGS = Settings.from_env()
+70
View File
@@ -0,0 +1,70 @@
from ha_deconz_bridge.automation import ActionSpec, automation_rules
def kelvin_to_mireds(kelvin: int) -> int:
return int(round(1_000_000 / kelvin))
def scene(target: str, *, brightness: int, kelvin: int) -> ActionSpec:
return ActionSpec(
"set_scene",
target=target,
brightness=brightness,
color_temp=kelvin_to_mireds(kelvin),
)
def all_lights_scene(*, brightness: int, kelvin: int) -> tuple[ActionSpec, ...]:
return (
scene("Example Living", brightness=brightness, kelvin=kelvin),
scene("Example Hall", brightness=brightness, kelvin=kelvin),
scene("Example Kitchen", brightness=brightness, kelvin=kelvin),
scene("Example Bathroom", brightness=brightness, kelvin=kelvin),
scene("Example Bedroom", brightness=brightness, kelvin=kelvin),
)
def all_lights_off() -> tuple[ActionSpec, ...]:
return (
ActionSpec("turn_off", target="Example Living"),
ActionSpec("turn_off", target="Example Hall"),
ActionSpec("turn_off", target="Example Kitchen"),
ActionSpec("turn_off", target="Example Bathroom"),
ActionSpec("turn_off", target="Example Bedroom"),
)
def room_automation(room: str) -> dict[str, dict[str, tuple[ActionSpec, ...]]]:
return {
"top": {
"single": (scene(room, brightness=255, kelvin=4000),),
"double": (scene(room, brightness=255, kelvin=5500),),
"hold": all_lights_scene(brightness=255, kelvin=4000),
"double_hold": all_lights_scene(brightness=255, kelvin=5500),
},
"up": {
"single": (scene(room, brightness=153, kelvin=3000),),
"double": (scene(room, brightness=204, kelvin=3500),),
"hold": (ActionSpec("brightness_step", target=room, brightness_step=26),),
},
"down": {
"single": (scene(room, brightness=51, kelvin=2200),),
"double": (scene(room, brightness=102, kelvin=2500),),
"hold": (ActionSpec("brightness_step", target=room, brightness_step=-26),),
},
"bottom": {
"single": (ActionSpec("turn_off", target=room),),
"hold": all_lights_off(),
},
}
AUTOMATION_CONFIG = {
"example_living_remote": room_automation("Example Living"),
"example_kitchen_remote": room_automation("Example Kitchen"),
"example_hall_remote": room_automation("Example Hall"),
"example_bathroom_remote": room_automation("Example Bathroom"),
"example_bedroom_remote": room_automation("Example Bedroom"),
}
AUTOMATIONS = automation_rules(AUTOMATION_CONFIG)
+130
View File
@@ -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}.")
+42
View File
@@ -0,0 +1,42 @@
from ha_deconz_bridge.groups import GroupMemberSpec, GroupSpec
from configs.example_controllers import DAYLIGHT_MIREDS
GROUPS: dict[str, GroupSpec] = {
"Example Hall": GroupSpec(
name="Example Hall",
members=("Example Hall Floor", "Example Hall Corner", "Example Hall Cabinets"),
),
"Example Kitchen": GroupSpec(
name="Example Kitchen",
members=("Example Kitchen Counter",),
),
"Example Bathroom": GroupSpec(
name="Example Bathroom",
members=("Example Bathroom Ceiling", "Example Bathroom Mirror"),
),
"Example Bedroom": GroupSpec(
name="Example Bedroom",
members=("Example Bedroom West", "Example Bedroom East"),
),
"Example Living": GroupSpec(
name="Example Living",
members=(
"Example Fixed White A",
"Example Fixed White B",
"Example Mixed North",
"Example Plug A",
"Example Plug B",
),
),
"Example Plant": GroupSpec(
name="Example Plant",
members=(
"Example Fixed White A",
"Example Fixed White B",
GroupMemberSpec("Example Mixed North", fixed_color=DAYLIGHT_MIREDS),
),
balance="relative_member",
),
}
+251
View File
@@ -0,0 +1,251 @@
from configs.example_controllers import (
CT_RANGE,
DAYLIGHT_MIREDS,
RGB_BLUE,
RGB_GREEN,
RGB_RED,
define_lights,
)
LIGHTS = define_lights(
{
"Example Fixed White A": {
"supported_color_modes": ("brightness",),
"controllers": {
"fixed_a": {
"name": "Example Fixed White A",
"mireds": CT_RANGE,
"color_temp": {
"ww": {"color": DAYLIGHT_MIREDS, "brightness": 9000},
},
},
},
},
"Example Fixed White B": {
"supported_color_modes": ("brightness",),
"controllers": {
"fixed_b": {
"name": "Example Fixed White B",
"mireds": CT_RANGE,
"color_temp": {
"ww": {"color": DAYLIGHT_MIREDS, "brightness": 7000},
},
},
},
},
"Example Plug A": {
"supported_color_modes": ("onoff",),
"controllers": {
"plug_a": {
"name": "Example Plug A",
"onoff": {
"w": {"color": DAYLIGHT_MIREDS, "brightness": 8000, "label": "color_temp"},
},
},
},
},
"Example Plug B": {
"supported_color_modes": ("onoff",),
"controllers": {
"plug_b": {
"name": "Example Plug B",
"onoff": {
"w": {"color": DAYLIGHT_MIREDS, "brightness": 8000, "label": "color_temp"},
},
},
},
},
"Example Mixed North": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"mixed_rgb": {
"name": "Example Mixed North RGB",
"rgb": {
"r": {"color": RGB_RED, "brightness": 3000},
"g": {"color": RGB_GREEN, "brightness": 2480},
"b": {"color": RGB_BLUE, "brightness": 3000},
},
},
"mixed_ct": {
"name": "Example Mixed North CCT",
"mireds": CT_RANGE,
"color_temp": {
"ww": {"color": 500, "brightness": 25600},
"cw": {"color": 153, "brightness": 56000},
},
},
},
},
"Example Hall Floor": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"hall_floor": {
"name": "Example Hall Floor",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 2460},
"g": {"color": RGB_GREEN, "brightness": 2040},
"b": {"color": RGB_BLUE, "brightness": 1260},
},
"color_temp": {
"ww": {"color": 500, "brightness": 6000},
"cw": {"color": 153, "brightness": 16800},
},
},
},
},
"Example Hall Corner": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"hall_corner": {
"name": "Example Hall Corner",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 820},
"g": {"color": RGB_GREEN, "brightness": 680},
"b": {"color": RGB_BLUE, "brightness": 420},
},
"color_temp": {
"ww": {"color": 500, "brightness": 2000},
"cw": {"color": 153, "brightness": 5600},
},
},
},
},
"Example Hall Cabinets": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"hall_cabinets": {
"name": "Example Hall Cabinets",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 2460},
"g": {"color": RGB_GREEN, "brightness": 2040},
"b": {"color": RGB_BLUE, "brightness": 1260},
},
"color_temp": {
"ww": {"color": 500, "brightness": 6000},
"cw": {"color": 153, "brightness": 16800},
},
},
},
},
"Example Kitchen Counter": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"kitchen_counter": {
"name": "Example Kitchen Counter",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 1800},
"g": {"color": RGB_GREEN, "brightness": 2200},
"b": {"color": RGB_BLUE, "brightness": 1900},
},
"color_temp": {
"ww": {"color": 500, "brightness": 3800},
"cw": {"color": 153, "brightness": 3800},
},
},
},
},
"Example Bathroom Ceiling": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"bath_ceiling_1": {
"name": "Example Bathroom Ceiling 1",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 900},
"g": {"color": RGB_GREEN, "brightness": 420},
"b": {"color": RGB_BLUE, "brightness": 180},
},
"color_temp": {
"ww": {"color": 500, "brightness": 1700},
"cw": {"color": 153, "brightness": 2700},
},
},
"bath_ceiling_2": {
"name": "Example Bathroom Ceiling 2",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 900},
"g": {"color": RGB_GREEN, "brightness": 420},
"b": {"color": RGB_BLUE, "brightness": 180},
},
"color_temp": {
"ww": {"color": 500, "brightness": 1700},
"cw": {"color": 153, "brightness": 2700},
},
},
"bath_ceiling_3": {
"name": "Example Bathroom Ceiling 3",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 900},
"g": {"color": RGB_GREEN, "brightness": 420},
"b": {"color": RGB_BLUE, "brightness": 180},
},
"color_temp": {
"ww": {"color": 500, "brightness": 1700},
"cw": {"color": 153, "brightness": 2700},
},
},
},
},
"Example Bathroom Mirror": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"bath_mirror": {
"name": "Example Bathroom Mirror",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 4100},
"g": {"color": RGB_GREEN, "brightness": 3400},
"b": {"color": RGB_BLUE, "brightness": 2100},
},
"color_temp": {
"ww": {"color": 500, "brightness": 10000},
"cw": {"color": 153, "brightness": 28000},
},
},
},
},
"Example Bedroom West": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"bedroom_w": {
"name": "Example Bedroom West",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 1100},
"g": {"color": RGB_GREEN, "brightness": 810},
"b": {"color": RGB_BLUE, "brightness": 360},
},
"color_temp": {
"ww": {"color": 500, "brightness": 3200},
"cw": {"color": 153, "brightness": 4700},
},
},
},
},
"Example Bedroom East": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"bedroom_e": {
"name": "Example Bedroom East",
"mireds": CT_RANGE,
"rgb": {
"r": {"color": RGB_RED, "brightness": 1100},
"g": {"color": RGB_GREEN, "brightness": 810},
"b": {"color": RGB_BLUE, "brightness": 360},
},
"color_temp": {
"ww": {"color": 500, "brightness": 3200},
"cw": {"color": 153, "brightness": 4700},
},
},
},
},
}
)
+17
View File
@@ -0,0 +1,17 @@
from ha_deconz_bridge.automation import RemoteSpec
REMOTES = {
"example_sensor_1": RemoteSpec(
sensor_id="example_sensor_1",
remote_id="example_living_remote",
name="Example living remote",
model="RWL021",
),
"example_sensor_2": RemoteSpec(
sensor_id="example_sensor_2",
remote_id="example_kitchen_remote",
name="Example kitchen remote",
model="RWL021",
),
}