Restructure public config and deployment

This commit is contained in:
ajp_anton
2026-05-30 19:17:27 +00:00
parent cc4e0c2c0f
commit cdf1f4e3a1
24 changed files with 710 additions and 302 deletions
+32 -31
View File
@@ -14,7 +14,7 @@ A runtime config module should export:
## Example
```python
from configs.example_controllers import define_lights
from controllers import define_lights
LIGHTS = define_lights({
"living_room": {
@@ -92,29 +92,30 @@ Numba is an optional dependency. Install it with the `speed` extra before select
## Config File Layout
The public example entry point is `configs/example.py`. It imports focused files:
The public example entry point is `config.example/lights_config.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.
- `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 internal `remote_id` values.
- `config.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`.
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:
```bash
mkdir -p configs/local
cp configs/example.py configs/local/lights_config.py
cp configs/example_*.py configs/local/
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. 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:
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:
```bash
ha-deconz-bridge-service configs/local/lights_config.py
ha-deconz-bridge-debug-web configs/local/lights_config.py "Example Mixed North"
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.
@@ -125,12 +126,12 @@ Groups are defined with `GroupSpec`. A member can be listed by name, or wrapped
from ha_deconz_bridge.groups import GroupMemberSpec, GroupSpec
GROUPS = {
"Example Plant": GroupSpec(
name="Example Plant",
"Plant Lights": GroupSpec(
name="Plant Lights",
members=(
"Example Fixed White A",
"Example Fixed White B",
GroupMemberSpec("Example Mixed North", fixed_color=182),
"Plant Light Left",
"Plant Light Right",
GroupMemberSpec("Living Room Ceiling", fixed_color=182),
),
balance="relative_member",
),
@@ -156,7 +157,7 @@ REMOTES = {
"example_sensor_1": RemoteSpec(
sensor_id="example_sensor_1",
remote_id="example_living",
name="Example living remote",
name="Living room remote",
model="RWL021",
double_press_window=0.45, # optional override
),
@@ -165,13 +166,13 @@ REMOTES = {
AUTOMATION_CONFIG = {
"example_living": {
"top": {
"single": (ActionSpec("set_scene", target="Example Living", brightness=180, color_temp=330),),
"single": (ActionSpec("set_scene", target="Living Room", brightness=180, color_temp=330),),
},
"bottom": {
"single": (ActionSpec("turn_off", target="Example Living"),),
"single": (ActionSpec("turn_off", target="Living Room"),),
},
"up": {
"hold": (ActionSpec("brightness_step", target="Example Living", brightness_step=16),),
"hold": (ActionSpec("brightness_step", target="Living Room", brightness_step=16),),
},
},
}
@@ -211,13 +212,13 @@ Action targets may be exact virtual light names, group names, or group expressio
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
## Sample Groups
`configs/example.py` exposes sanitized example groups in addition to individual lights:
`config.example/lights_config.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.
- `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.
+2 -2
View File
@@ -29,7 +29,7 @@ Both variants:
Run:
```bash
.venv/bin/python -m ha_deconz_bridge.debugger.web_debugger ./configs/example.py "Example Mixed North" --host 127.0.0.1 --port 8765
.venv/bin/python -m ha_deconz_bridge.debugger.web_debugger ./config.example/lights_config.py "Living Room Ceiling" --host 127.0.0.1 --port 8765
```
Then open `http://127.0.0.1:8765` in a browser. If the debugger runs on the VM, tunnel the port over SSH if needed.
@@ -37,7 +37,7 @@ Then open `http://127.0.0.1:8765` in a browser. If the debugger runs on the VM,
For real hardware, pass your ignored local config instead:
```bash
.venv/bin/python -m ha_deconz_bridge.debugger.web_debugger ./configs/local/lights_config.py "Your Light Name" --host 127.0.0.1 --port 8765
.venv/bin/python -m ha_deconz_bridge.debugger.web_debugger ./config/lights_config.py "Your Light Name" --host 127.0.0.1 --port 8765
```
Inside the UI:
+53 -22
View File
@@ -4,17 +4,15 @@ This project is designed to run as a user-owned Python app on a small Linux host
## Public Repo Policy
The public repository intentionally does not track a real deployment script or real home config.
Keep these in ignored local files:
The public repository tracks a generic deployment script and sanitized example config, but does not track real home-specific values. Keep these in ignored local files under `config/`:
- real SSH hostnames, domains, and usernames
- real remote install paths
- real room, light, group, and remote names
- real deCONZ IDs and API keys
- local deployment scripts
- local deployment settings
The sanitized example config is [`configs/example.py`](../configs/example.py). A real deployment can use an ignored config such as `configs/local/lights_config.py`.
The sanitized example config is [`config.example/lights_config.py`](../config.example/lights_config.py). A real deployment can use an ignored config such as `config/lights_config.py`.
## Target Layout
@@ -22,20 +20,58 @@ A typical target machine can use this layout:
- `<install-dir>/app`: synced application code
- `<install-dir>/.venv`: virtualenv used by the service
- `<install-dir>/config/lights_config.py`: runtime light definitions
- `<install-dir>/config/*.py`: runtime light/group/remote definitions
- `<install-dir>/config/service.env`: runtime environment variables
- `~/.config/systemd/user/ha-deconz-bridge.service`: user service unit
- `~/.config/systemd/user/ha-deconz-bridge-debug.service`: optional web debugger service
## One Command Deployment
Start by copying the example config directory:
```bash
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:
- `config/lights_config.py` and the sibling Python files for your real lights, groups, and remotes.
- `config/service.env` for runtime MQTT/deCONZ values.
- `config/deploy.env` for the SSH target and remote install path.
Deploy or redeploy with:
```bash
python deploy/deploy.py
```
The script copies the app to the target, copies all `.py` files next to the configured `lights_config.py` entry point, copies service environment values, creates or reuses the target virtualenv, installs the package, installs the user systemd units, runs `systemctl --user daemon-reload`, and enables/starts `ha-deconz-bridge.service`.
Useful options:
```bash
python deploy/deploy.py --config-only
python deploy/deploy.py --host pi@raspberrypi.local
python deploy/deploy.py --remote-dir ~/ha-deconz-bridge
python deploy/deploy.py --with-speed
python deploy/deploy.py --no-start
```
`--config-only` copies only config/service env changes and restarts the service. Use it for normal light/config edits after the app is already installed.
## Service Units
The generic user service templates are tracked in [`deploy/`](../deploy/). They expect:
- `HA_DECONZ_BRIDGE_CONFIG` to point at the runtime config file
- `HA_DECONZ_BRIDGE_SERVICE_ENV` or an equivalent environment file to provide secrets and host-specific settings
- the deploy script to replace `__INSTALL_DIR__` with the target install path
- `<install-dir>/config/lights_config.py` and its imported sibling config files to exist
- `<install-dir>/config/service.env` to exist
- a virtualenv with the package installed
The environment file should not be committed. Use [`deploy/service.env.example`](../deploy/service.env.example) as a template.
The environment file should not be committed. Use [`config.example/service.env.example`](../config.example/service.env.example) or [`config.example/deploy.env.example`](../config.example/deploy.env.example) as a template.
## Manual Deployment Outline
@@ -46,11 +82,11 @@ The environment file should not be committed. Use [`deploy/service.env.example`]
```bash
python3 -m venv <install-dir>/.venv
<install-dir>/.venv/bin/pip install --upgrade pip
<install-dir>/.venv/bin/pip install -e '<install-dir>/app[speed]'
<install-dir>/.venv/bin/pip install "<install-dir>/app[speed]"
```
4. Copy a real ignored config to `<install-dir>/config/lights_config.py`.
5. Copy and edit `deploy/service.env.example` to `<install-dir>/config/service.env`.
4. Copy a real ignored config directory to `<install-dir>/config/`.
5. Copy and edit `config.example/service.env.example` to `<install-dir>/config/service.env`.
6. Install the systemd user units from `deploy/`.
7. Reload and start the service:
@@ -64,18 +100,13 @@ systemctl --user enable --now ha-deconz-bridge.service
Start from the public example:
```bash
mkdir -p configs/local
cp configs/example.py configs/local/lights_config.py
cp configs/example_*.py configs/local/
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
```
Edit the copied files for your real devices. If the entry point imports copied split files, make sure the imports point at `configs.local...` modules. For example:
```python
from configs.local.example_lights import LIGHTS
```
Then deploy or run with `configs/local/lights_config.py`. Keep that path private and uncommitted.
Edit the copied files for your real devices. 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/`. Then deploy or run with `config/lights_config.py`. Keep that path private and uncommitted.
## Required Runtime Values