10 KiB
Configuration
Configuration is Python, but typed and validated.
Expected Module Exports
A runtime config module should export:
SETTINGS:ha_deconz_bridge.settings.SettingsLIGHTS:dict[str, ha_deconz_bridge.lights.VirtualLightSpec]REMOTES: optionaldict[str, ha_deconz_bridge.automation.RemoteSpec]AUTOMATIONS: optionaltuple[ha_deconz_bridge.automation.AutomationRule, ...]
Example
from controllers import define_lights
LIGHTS = define_lights({
"living_room": {
"supported_color_modes": ("rgb", "color_temp"),
"controllers": {
"7": {
"name": "North RGB+CCT",
"mireds": (153, 500),
"rgb": {
"r": {"color": (0.6949, 0.3051), "brightness": 202},
"g": {"color": (0.1414, 0.7181), "brightness": 479},
"b": {"color": (0.1443, 0.0427), "brightness": 124},
},
"color_temp": {
"ww": {"color": 417, "brightness": 520},
"cw": {"color": 154, "brightness": 576},
},
},
},
},
})
The controller ID ("7"), physical mode names ("rgb" / "color_temp"), and channel roles ("r", "g", "b", "ww", "cw") are taken from dictionary keys. color accepts:
- a single number as CT, using mireds below
1000and Kelvin at or above1000 - an
(x, y)tuple - an RGB tuple, either
0..1floats or0..255integers
label is optional. If omitted, channels inside physical rgb mode default to label rgb, and channels inside physical color_temp mode default to label color_temp. Use label only for unusual wiring, such as an RGB controller output connected to white LEDs.
Validation Rules
- Supported virtual modes must be one of:
("onoff",)("brightness",)- any non-empty subset of
("rgb", "color_temp")
- Controller mode roles must match the controller mode:
rgb:r,g,bcolor_temp:ww,cwbrightness:wonoff:w
- Every channel defines the emitted physical color and max output. The role only describes how the backend controller address is written.
ChannelSpec.semantic_labelis optional and may be:rgbcolor_tempboost
- If omitted,
rgbandcolor_tempchannels default to their mode name;brightnessandonoffchannels default to no label. - Solver priority is staged:
- dimmable same-label channels first
- then other dimmable channels
- then
boost/unlabeled dimmable channels - then same-label on/off channels
- then other on/off channels
- then
boost/unlabeled on/off channels
- Controllers may define multiple mutually exclusive modes, and the solver is free to choose one mode or turn that controller fully off.
- A virtual light that exposes
color_tempmust have native CT-capable emitters. - A controller that claims a mode unsupported by the real device fails startup validation.
Solver Responsiveness
Settings.target_cache_scale controls how precisely requested chromaticity is keyed in the solver cache. The default is 10000, which keeps adjacent RGB/CT slider positions distinct in normal use. Lower values make nearby requests reuse the same reference solve, but can create visible color dead zones where a small slider movement does not change the achieved color. Treat lower values as an explicit responsiveness experiment, not the default behavior.
Settings.approximate_chroma_band controls how far a CT-line solution may sit from the ideal blackbody point while still being accepted. The default is 0.025 in uv distance. Lower values make CT requests more color-accurate but may choose RGB compensation more often; higher values prefer broad-spectrum CT output more aggressively.
HA_DECONZ_BRIDGE_NUMERIC_BACKEND controls the numeric backend used by the solver:
scipy: default, current stable backend.numba: try the Numba active-set backend for supported LP solves, with SciPy fallback when needed.auto: same asnumbawhen Numba is importable, otherwise SciPy.
Numba is an optional dependency. Install it with the speed extra before selecting numba or auto.
HA_DECONZ_BRIDGE_WARMUP_ON_STARTUP defaults to true. When enabled, the service and debugger start a background warmup that runs one representative solve per supported HA mode. This compiles Numba-backed paths without blocking MQTT or the debugger HTTP port.
Config File Layout
The public example entry point is config.example/lights_config.py. It imports focused files:
config.example/controllers.py: physical controller/channel helper functions and constants.config.example/lights.py: individual virtual lights and which controllers they abstract.config.example/groups.py: room/group membership, optional member restrictions, and group balancing policy.config.example/remotes.py: physical remote sensor IDs mapped to internalremote_idvalues.config.example/actions.py: scenes, action helpers, and remote automation mappings.
Real home configs should stay in ignored local files such as config/. The entry point should stay small and only assemble SETTINGS, LIGHTS, GROUPS, REMOTES, and AUTOMATIONS.
For a private local setup, copy the public example into the ignored local directory:
mkdir -p config
cp config.example/*.py config/
cp config.example/service.env.example config/service.env
cp config.example/deploy.env.example config/deploy.env
Then edit the copied files. The example entry point imports sibling files such as actions.py, groups.py, and lights.py, so the same imports continue to work after copying into config/. Run with:
ha-deconz-bridge-service config/lights_config.py
ha-deconz-bridge-debug-web config/lights_config.py "Living Room Ceiling"
The runtime accepts either a Python module name or a .py file path.
Groups are defined with GroupSpec. A member can be listed by name, or wrapped in GroupMemberSpec to restrict how that member behaves only inside that group:
from ha_deconz_bridge.groups import GroupMemberSpec, GroupSpec
GROUPS = {
"Plant Lights": GroupSpec(
name="Plant Lights",
members=(
"Plant Light Left",
"Plant Light Right",
GroupMemberSpec("Living Room Ceiling", fixed_color=182),
),
balance="relative_member",
),
}
By default, a group derives its exposed modes from the union of its members. If any member supports rgb or color_temp, the group exposes those color modes. If no member supports color but at least one member supports dimming, the group exposes brightness. If no member is dimmable, the group is onoff. supported_color_modes is still available as an explicit override.
fixed_color makes that member act like a dimmable fixed-color contributor only inside that group. It accepts a single number as color temperature (<1000 is mireds, >=1000 is kelvin), an RGB triple, or an xy pair. RGB input is normalized, so (1.0, 1.0, 1.0) and (255, 255, 255) describe the same color.
fixed_brightness requires fixed_color and makes that member act like an on/off fixed-output contributor inside the group. Values up to 1.0 are treated as relative brightness; larger values are treated as HA 0..255 brightness. On readback, the member counts as on when it reports at least 90% of that fixed output. Direct control of the member light is unchanged.
balance="relative_member" sends the same relative brightness to each member. The default passthrough sends the group command to every compatible member without trying to equalize member brightness.
Remote Automation
Physical remotes are configured by deCONZ sensor ID, but automation rules bind to an internal remote_id.
from ha_deconz_bridge.automation import ActionSpec, RemoteSpec, automation_rules
REMOTES = {
"example_sensor_1": RemoteSpec(
sensor_id="example_sensor_1",
remote_id="example_living",
name="Living room remote",
model="RWL021",
double_press_window=0.45, # optional override
),
}
AUTOMATION_CONFIG = {
"example_living": {
"top": {
"single": (ActionSpec("set_scene", target="Living Room", brightness=180, color_temp=330),),
},
"bottom": {
"single": (ActionSpec("turn_off", target="Living Room"),),
},
"up": {
"hold": (ActionSpec("brightness_step", target="Living Room", brightness_step=16),),
},
},
}
AUTOMATIONS = automation_rules(AUTOMATION_CONFIG)
RemoteSpec.model is informational in the current implementation. It documents the remote type and gives us a place to add model-specific validation or decoding later. The current Hue dimmer mapping is for RWL021.
Supported base gestures are press, single, double, long, double_long, hold, and double_hold.
Hold-count gesture patterns are also supported:
hold_x: trigger only on the x'th hold event.double_hold_x: trigger only on the x'th hold event of a double-hold.hold_x_y: trigger from x through y, inclusive.double_hold_x_y: same for double-hold.- If
y < x, the trigger starts at x and continues until release. The recommended spelling for an unbounded hold ishold_x_0ordouble_hold_x_0. - If
y == x, the trigger fires only on the x'th hold event.
Supported action kinds are:
turn_onturn_offset_scenebrightness_stepcolor_temp_step
Action targets may be exact virtual light names, group names, or group expressions:
A+B+C: union of groups/lights A, B, and C.A-B: all members of A except members of B.A+B-C+D: parsed left-to-right.All: all individual virtual lights.- Empty string: same as
All. -AorAll-A: all individual virtual lights except A.
If the target exactly matches an exposed virtual light or group and contains no + or -, the action is sent to that virtual entity directly. Compound expressions expand to individual virtual lights.
Sample Groups
config.example/lights_config.py exposes sanitized example groups in addition to individual lights:
Hallway: three RGB+CCT virtual lights.Kitchen: one RGB+CCT virtual light.Bathroom: one multi-controller ceiling light and one mirror light.Bedroom: two RGB+CCT virtual lights.Living Room: fixed-white, mixed RGB/CCT, and on/off examples.Plant Lights: fixed-white lights plus one constrained mixed light, using relative member brightness balancing.