Skip to content

Commit ae445fa

Browse files
committed
Refactor code structure for improved readability and maintainability
1 parent 938b4bc commit ae445fa

7 files changed

+2588
-81
lines changed

blueprints/automation/witb_plus_actions_lights_fan/v2/CHANGELOG_witb_plus_actions_lights_fan.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,87 @@ Format: `[version] — date — summary`, followed by itemized changes.
55

66
---
77

8+
## [2.3.1] — 2026-03-03 — Fan Run-On Orphan Fix + Tag Cleanup Blueprint
9+
10+
### Fixed
11+
- **FIX — CRITICAL: Fan run-on timer orphaned by mode:restart mid-sequence.**
12+
The `timer.start` for the fan run-on was previously located *after* the
13+
lights verify/retry block in the `lights_off` sequence. Any `mode:restart`
14+
kill during verify/retry delays (up to `verify_delay + retries × retry_interval`
15+
seconds) would kill the run before the timer started. The fan would stay ON
16+
permanently with no timer ever firing to turn it off, and `auto_tag_fan` would
17+
remain stuck ON.
18+
19+
Fix: `timer.start` is now called *before* the verify block, immediately after
20+
the first `light.turn_off` command. `timer.start` on an already-running timer
21+
is idempotent, so early invocation is safe in all paths. The `timer_finished`
22+
handler still owns the actual fan-off.
23+
24+
- **FIX: `auto_tag_lights` stuck ON after mode:restart kill during verify loop.**
25+
A restart during the verify delay or retry loop would exit before the tag-clear
26+
step, leaving `auto_tag_lights` ON even though lights were off. The tag would
27+
then block the next vacancy cycle's `tag_lights_ok_to_off` check until the room
28+
re-occupied and went vacant again (resetting the `lights_off` trigger). Addressed
29+
by the new companion cleanup blueprint (see Added below).
30+
31+
### Added
32+
- **Companion blueprint: `witb_plus_actions_cleanup.yaml` v1.0.0.**
33+
Belt-and-suspenders automation that catches any remaining `auto_tag_lights` or
34+
`auto_tag_fan` stuck-ON states that the main automation could not clear due to a
35+
`mode:restart` kill. Inputs mirror the Actions instance for the same room.
36+
Key behaviors:
37+
- Two independent soak timers: `lights_cleanup_delay` (default 5 min) for the
38+
lights tag; `fan_cleanup_delay` (default 20 min) for the fan tag.
39+
- Lights tag only cleared when all controlled lights are confirmed OFF.
40+
- Fan tag never cleared while `fan_runon_timer` is active — `timer_finished`
41+
in the main automation always owns that transition.
42+
- Both checks re-confirm vacancy at execution time.
43+
- `mode: single` — rapid vacancy/occupy cycles reset the timer cleanly.
44+
- Deploy one instance per room alongside the Actions automation.
45+
46+
### Changed
47+
- Verbose inline `# FIX v2.3.x` and `# v2.3.0:` comment blocks removed from
48+
blueprint YAML. Replaced with brief `# See CHANGELOG vX.Y.Z` pointers.
49+
All rationale lives here in CHANGELOG instead of cluttering the action sequences.
50+
51+
---
52+
53+
## [2.3.0] — 2026-03-xx — External Gating (Lights + Fan)
54+
55+
### Added
56+
- **`light_gating_entity`** (optional `binary_sensor`): external gate controlling
57+
whether lights may turn ON. Must be ON for lights to assert. Leave empty for
58+
no gating (always allow). Replaces the inline lux/sun evaluation that previously
59+
lived inside the blueprint. Compute lux threshold, sun elevation, curtain state,
60+
seasonal offsets, etc. in a separate automation and expose a single binary sensor
61+
— the Actions blueprint just consumes it.
62+
- **`fan_vacancy_gate_entity`** (optional `binary_sensor`): external gate
63+
controlling whether the fan may turn OFF on vacancy. OFF = hold fan on; ON = ok
64+
to turn fan off. When this gate transitions ON while the room is already vacant
65+
and the fan is still running (and no run-on timer is active), the fan turns off
66+
automatically via the new `fan_gate_cleared` trigger. Leave empty for no hold.
67+
Replaces the inline humidity polling loop.
68+
- **`fan_gate_cleared` trigger and action block**: fires when
69+
`fan_vacancy_gate_entity` transitions to ON. Guards: room still vacant, fan
70+
still running, run-on timer not active (run-on takes precedence).
71+
- **`lights_ok_to_turn_on`** and **`fan_ok_to_turn_off`** variables: derived
72+
from the new gate inputs; used consistently across all affected branches.
73+
74+
### Removed
75+
- Inline lux threshold inputs (`lux_sensor`, `lux_threshold`) and evaluation logic.
76+
- Inline humidity hold inputs and the polling `while` loop that held the fan on.
77+
Humidity logic now belongs in an external `fan_vacancy_gate_entity` automation.
78+
79+
### Migration
80+
Existing instances using lux or humidity hold inputs must:
81+
1. Create a separate automation that evaluates your lux/humidity conditions and
82+
writes the result to a `input_boolean` or `template` binary sensor.
83+
2. Wire that sensor into `light_gating_entity` / `fan_vacancy_gate_entity`.
84+
3. Remove the now-missing lux/humidity inputs from the old automation instance
85+
(HA will warn about unknown inputs on load).
86+
87+
---
88+
889
## [2.2.1] — 2026-02-26 — Bed Sensor Integration
990

1091
### Added

blueprints/automation/witb_plus_actions_lights_fan/v2/README_witb_plus_actions_lights_fan_v2.md

Lines changed: 116 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,79 @@
33
This folder contains the **actions blueprint** that pairs with WITB+ occupancy.
44

55
Use this when you already have room occupancy sensors from `witb_plus/v4` (for example
6-
`binary_sensor.<slug>_occupied_effective`) and want reliable light/fan actions with safety tags.
6+
`binary_sensor.<slug>_occupied_effective`) and want reliable light/fan actions with
7+
safety tags, external gating, and automatic tag cleanup.
78

89
---
910

10-
## 1. Blueprint
11-
12-
**File:** `witb_plus_actions_lights_fan.yaml`
13-
14-
This blueprint controls:
15-
- Lights (brightness profiles, optional sun/lux gating)
16-
- Fan (`fan.*` or `switch.*`)
17-
- Vacancy off behavior with optional fan run-on
18-
- Optional humidity hold (fan stays on while humidity remains high)
19-
- Optional startup cleanup for auto-tagged entities
20-
21-
It is intended to be used from the Home Assistant UI via **Create Automation from Blueprint**.
11+
## 1. Blueprints
12+
13+
### `witb_plus_actions_lights_fan.yaml` — main actions blueprint (v2.3.1)
14+
15+
Controls lights and fan for a single room based on occupancy state changes from
16+
`binary_sensor.<slug>_occupied_effective`. Key behaviors:
17+
18+
- **Lights ON/OFF** with day/night brightness profiles and optional soft-off dim warning.
19+
- **Manual-off hold** — if you turn lights off while occupied they stay off until
20+
vacancy clears `auto_tag_lights`, then re-arm next occupancy.
21+
- **Bed suppress** — if someone is in bed and lights are off, motion re-assertion
22+
(e.g. midnight bathroom return) does not turn lights back on.
23+
- **Light gating** — wire an external `binary_sensor` computed from lux, sun
24+
elevation, curtains, or season. Blueprint does not evaluate these inline.
25+
- **Fan ON on occupancy** with optional delay and night-disable mode.
26+
- **Fan run-on timer** after vacancy, with optional gate to hold the fan on while
27+
humidity or other conditions require it.
28+
- **Fan vacancy gate** — wire an external `binary_sensor` (e.g. humidity threshold).
29+
OFF = hold fan on; ON = ok to turn off. When the gate clears while the room is
30+
already vacant the fan turns off automatically.
31+
- **Auto-tag helpers** (`input_boolean`) track automation ownership of lights and fan
32+
for safe "only turn off what we turned on" behavior.
33+
- **Startup cleanup** with double-tap validation after HA restart.
34+
35+
### `witb_plus_actions_cleanup.yaml` — tag cleanup blueprint (v1.0.0)
36+
37+
Belt-and-suspenders companion that catches `auto_tag_lights` and `auto_tag_fan`
38+
helpers stuck ON after a `mode:restart` kill mid-sequence in the main automation.
39+
Deploy **one instance per room** alongside the main Actions instance.
40+
41+
- Two independent soak timers fire after vacancy: lights check (default 5 min),
42+
fan check (default 20 min).
43+
- Lights tag only cleared when all controlled lights are confirmed OFF.
44+
- Fan tag never cleared while `fan_runon_timer` is active.
45+
- Both checks re-confirm vacancy at evaluation time.
46+
- Inputs mirror the main Actions instance — wire the same entities.
47+
48+
See `CHANGELOG_witb_plus_actions_lights_fan.md` for full fix rationale.
2249

2350
---
2451

25-
## 2. Package Helpers (helpers only)
52+
## 2. Package Helpers
2653

27-
**Reference template:** `room_witb_actions_package_template.yaml`
54+
**Reference template:** `witb_plus_actions_lights_fan_package_template.yaml`
2855
**Generated output location:** `packages/`
2956

3057
Package files provide helper entities such as:
31-
- `input_boolean.<slug>_auto_lights_on`
32-
- `input_boolean.<slug>_auto_fan_on`
33-
- `timer.<slug>_actions_cooldown`
34-
- `timer.<slug>_fan_runon`
35-
- `input_datetime.<slug>_actions_night_start`
36-
- `input_datetime.<slug>_actions_night_end`
37-
- Optional `input_number` tuning helpers (brightness, fan %, lux threshold, humidity thresholds, fan delay)
3858

39-
Load these packages with:
59+
| Entity | Purpose |
60+
|---|---|
61+
| `input_boolean.<slug>_auto_lights_on` | Auto-tag for lights ownership |
62+
| `input_boolean.<slug>_auto_fan_on` | Auto-tag for fan ownership |
63+
| `input_boolean.<slug>_keep_on` | Blocking entity — prevents vacancy off |
64+
| `timer.<slug>_actions_cooldown` | ON-debounce cooldown timer |
65+
| `timer.<slug>_actions_fan_runon` | Fan run-on countdown timer |
66+
| `input_datetime.<slug>_actions_night_start` | Night window start |
67+
| `input_datetime.<slug>_actions_night_end` | Night window end |
68+
| `input_number.<slug>_actions_brightness_day_pct` | Day brightness % |
69+
| `input_number.<slug>_actions_brightness_night_pct` | Night brightness % |
70+
| `input_number.<slug>_actions_fan_pct_day` | Fan speed % day |
71+
| `input_number.<slug>_actions_fan_pct_night` | Fan speed % night |
72+
| `input_number.<slug>_actions_fan_runon_minutes` | Fan run-on duration |
73+
| `input_number.<slug>_actions_fan_on_delay_seconds` | Fan ON delay |
74+
| `binary_sensor.<slug>_actions_cooldown_active` | Cooldown active indicator |
75+
| `binary_sensor.<slug>_actions_fan_runon_active` | Fan run-on active indicator |
76+
| `binary_sensor.<slug>_actions_in_night_window` | Night window indicator |
77+
78+
Load packages with:
4079

4180
```yaml
4281
homeassistant:
@@ -47,26 +86,22 @@ homeassistant:
4786
4887
## 3. Generator Script
4988
50-
**File:** `generate_witb_plus_actions_packages_templated.py`
51-
52-
The script generates helper package YAML files only.
53-
It does **not** generate automations.
89+
**File:** `generate_witb_packages_templated.py`
5490

55-
Example:
91+
Generates per-room helper package YAML files from the template. Does **not**
92+
generate automations — those are created from blueprints in the HA UI.
5693

5794
```bash
58-
python3 generate_witb_plus_actions_packages_templated.py \
95+
# Generate packages for specific rooms
96+
python3 generate_witb_packages_templated.py \
5997
--rooms "Office" "Master Bathroom Toilet" \
60-
--template room_witb_actions_package_template.yaml \
98+
--template witb_plus_actions_lights_fan_package_template.yaml \
6199
--out ./packages
62-
```
63-
64-
Dry run:
65100
66-
```bash
67-
python3 generate_witb_plus_actions_packages_templated.py \
101+
# Dry run to preview output
102+
python3 generate_witb_packages_templated.py \
68103
--rooms "Office" \
69-
--template room_witb_actions_package_template.yaml \
104+
--template witb_plus_actions_lights_fan_package_template.yaml \
70105
--out ./packages \
71106
--dry-run
72107
```
@@ -75,17 +110,53 @@ python3 generate_witb_plus_actions_packages_templated.py \
75110

76111
## 4. Typical Setup Flow
77112

78-
1. Create room occupancy with `witb_plus/v4`.
79-
2. Generate helpers into `packages/`.
80-
3. Reload/restart Home Assistant so helpers exist.
81-
4. Create an automation from `WITB+ Actions - Lights + Fan`.
82-
5. Bind `occupied_effective` to `binary_sensor.<slug>_occupied_effective`.
83-
6. Bind lights/fan entities and optional helper inputs.
113+
1. Run `witb_plus/v4` blueprint for the room to get `binary_sensor.<slug>_occupied_effective`.
114+
2. Generate helpers via the script and reload/restart HA so all helpers exist.
115+
3. Create an automation from **WITB+ Actions - Lights + Fan** and wire:
116+
- `occupied_effective` → `binary_sensor.<slug>_occupied_effective`
117+
- `auto_tag_lights` → `input_boolean.<slug>_auto_lights_on`
118+
- `auto_tag_fan` → `input_boolean.<slug>_auto_fan_on`
119+
- `cooldown_timer` → `timer.<slug>_actions_cooldown`
120+
- `fan_runon_timer` → `timer.<slug>_actions_fan_runon`
121+
- `night_start_helper` / `night_end_helper` → corresponding `input_datetime` helpers
122+
- Brightness and fan % helpers → corresponding `input_number` helpers
123+
- `light_gating_entity` → your external lux/sun gate sensor (optional)
124+
- `fan_vacancy_gate_entity` → your external humidity gate sensor (optional)
125+
4. Create a second automation from **WITB+ Actions Tag Cleanup** for the same room,
126+
wiring the same lights, fan, tags, and timers. Use `mode: single`.
127+
5. Verify in Developer Tools → Template that the `occupied_effective` sensor
128+
transitions correctly before testing lights/fan behavior.
129+
130+
---
131+
132+
## 5. Architecture Notes
133+
134+
**Why two blueprints (occupancy + actions)?**
135+
WITB+ deliberately separates *occupancy inference* (WITB+ v4) from *action
136+
orchestration* (Actions). This allows multiple action automations to subscribe
137+
to the same occupancy sensor independently, and keeps each blueprint focused on
138+
one concern.
139+
140+
**Why external gating (v2.3.0+)?**
141+
Lux evaluation and humidity hold logic were removed from the Actions blueprint
142+
in v2.3.0. These belong in dedicated automations that write their verdict to a
143+
binary sensor. This keeps Actions simple and testable — you can force the gate
144+
ON/OFF manually in the UI to test lighting behavior independently of ambient
145+
conditions.
146+
147+
**Why a cleanup blueprint (v2.3.1+)?**
148+
`mode:restart` is the correct mode for an event-driven automation — it prevents
149+
stale queued runs from accumulating. The tradeoff is that any run killed mid-
150+
sequence cannot clean up after itself. The cleanup blueprint handles this with a
151+
time-delayed soak check that is safe, idempotent, and never turns anything off —
152+
it only clears boolean tags.
84153

85154
---
86155

87156
## Summary
88157

89-
- **WITB+ v4** handles occupancy inference.
90-
- **WITB+ Actions** handles light/fan behavior.
91-
- **Package files** provide helper entities for safe automation behavior and dashboard tuning.
158+
| Blueprint | Role | Instance count |
159+
|---|---|---|
160+
| `witb_plus/v4` | Occupancy inference | One per room |
161+
| `witb_plus_actions_lights_fan` | Light + fan control | One per room |
162+
| `witb_plus_actions_cleanup` | Tag stuck-ON recovery | One per room |

0 commit comments

Comments
 (0)