Files
ha-deconz-bridge/docs/configuration.md
T
2026-05-30 15:44:40 +00:00

224 lines
10 KiB
Markdown

# Configuration
Configuration is Python, but typed and validated.
## Expected Module Exports
A runtime config module should export:
- `SETTINGS`: `ha_deconz_bridge.settings.Settings`
- `LIGHTS`: `dict[str, ha_deconz_bridge.lights.VirtualLightSpec]`
- `REMOTES`: optional `dict[str, ha_deconz_bridge.automation.RemoteSpec]`
- `AUTOMATIONS`: optional `tuple[ha_deconz_bridge.automation.AutomationRule, ...]`
## Example
```python
from configs.example_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 `1000` and Kelvin at or above `1000`
- an `(x, y)` tuple
- an RGB tuple, either `0..1` floats or `0..255` integers
`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`, `b`
- `color_temp`: `ww`, `cw`
- `brightness`: `w`
- `onoff`: `w`
- Every channel defines the emitted physical color and max output. The role only describes how the backend controller address is written.
- `ChannelSpec.semantic_label` is optional and may be:
- `rgb`
- `color_temp`
- `boost`
- If omitted, `rgb` and `color_temp` channels default to their mode name; `brightness` and `onoff` channels 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_temp` must 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 as `numba` when 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 `configs/example.py`. It imports focused files:
- `configs/example_controllers.py`: physical controller/channel helper functions and constants.
- `configs/example_lights.py`: individual virtual lights and which controllers they abstract.
- `configs/example_groups.py`: room/group membership, optional member restrictions, and group balancing policy.
- `configs/example_remotes.py`: physical remote sensor IDs mapped to internal `remote_id` values.
- `configs/example_actions.py`: scenes, action helpers, and remote automation mappings.
Real home configs should stay in ignored local files such as `configs/local/`. 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:
```bash
mkdir -p configs/local
cp configs/example.py configs/local/lights_config.py
cp configs/example_*.py configs/local/
```
Then edit the copied files. If you keep the split-file imports, update them from `configs.example_*` to `configs.local.example_*`, or rename the copied split files and import those names instead. Run with:
```bash
ha-deconz-bridge-service configs/local/lights_config.py
ha-deconz-bridge-debug-web configs/local/lights_config.py "Example Mixed North"
```
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:
```python
from ha_deconz_bridge.groups import GroupMemberSpec, GroupSpec
GROUPS = {
"Example Plant": GroupSpec(
name="Example Plant",
members=(
"Example Fixed White A",
"Example Fixed White B",
GroupMemberSpec("Example Mixed North", 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`.
```python
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="Example living remote",
model="RWL021",
double_press_window=0.45, # optional override
),
}
AUTOMATION_CONFIG = {
"example_living": {
"top": {
"single": (ActionSpec("set_scene", target="Example Living", brightness=180, color_temp=330),),
},
"bottom": {
"single": (ActionSpec("turn_off", target="Example Living"),),
},
"up": {
"hold": (ActionSpec("brightness_step", target="Example Living", 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 is `hold_x_0` or `double_hold_x_0`.
- If `y == x`, the trigger fires only on the x'th hold event.
Supported action kinds are:
- `turn_on`
- `turn_off`
- `set_scene`
- `brightness_step`
- `color_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`.
- `-A` or `All-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.
## Example Groups
`configs/example.py` exposes sanitized example groups in addition to individual lights:
- `Example Hall`: three RGB+CCT virtual lights.
- `Example Kitchen`: one RGB+CCT virtual light.
- `Example Bathroom`: one multi-controller ceiling light and one mirror light.
- `Example Bedroom`: two RGB+CCT virtual lights.
- `Example Living`: fixed-white, mixed RGB/CCT, and on/off examples.
- `Example Plant`: fixed-white examples plus one constrained mixed light, using relative member brightness balancing.