Sitting Lamp → Hue circadian renderer (design)¶
Ticket: #93 · Date: 2026-07-21
Goal¶
Migrate the master-bedroom sitting-area lamp from the on/off ZigBee switch (switch.master_bedroom_sitting_lamp) to the already-paired Philips Hue bulb light.master_bedroom_sitting_room_lamp_bulb (friendly name "Sitting Room Lamp Bulb"), and rewrite script.master_bedroom_sitting_lamp_render so that when the lamp is ON it renders a circadian colour-temperature + brightness that tracks the sun and is sensitive to room darkness (motion + sun-below-horizon, plus a storm-darkened daytime room via a lux sensor).
Verified facts¶
- Bulb:
light.master_bedroom_sitting_room_lamp_bulb, platformhue.supported_color_modes: [color_temp, xy],min_color_temp_kelvin: 2000,max_color_temp_kelvin: 6535, brightness. Full tunable-white + colour. - Power independence: the bulb reports
offbut available whileswitch.master_bedroom_sitting_lampisoff→ the bulb has its own always-on power. No smart-bulb-behind-a-smart-switch trap. The old ZigBee plug is freed; leave it paired but drop it from the renderer/scripts (may be repurposed). - Lux sensor:
sensor.night_light_7_illuminance— the master-bedroom night light (NL7) illuminance. - Sun tracker: reuse
sensor.ambiance_scene(configuration_ambiance_addition.yaml) — the 10-phase sun-elevation template the Govee ambiance lamps already key off (night/dawn/first_light/sunrise/morning/midday/afternoon/golden_hour/sunset/dusk).
The "dark" test (replaces bare sun below_horizon)¶
dark = is_state('sun.sun','below_horizon')
or (states('sensor.night_light_7_illuminance') | float(999)
< states('input_number.sitting_lamp_dark_lux') | float(20))
- New helper
input_number.sitting_lamp_dark_lux(configuration.yaml): min 0, max 200, step 1,unit_of_measurement: lx,icon: mdi:brightness-5. Noinitial:(persistent via restore_state); set to ~20 once after first deploy. Dashboard-tunable. - The
float(999)default means a missing/unknown lux reading is treated as bright (fails the dark test) — fail-safe: a dead sensor won't spuriously light the lamp during the day.
Circadian output¶
When the priority stack decides ON, call light.turn_on on the bulb with a Kelvin + brightness picked by:
- Sun above horizon (only reachable via the daytime-storm branch) → warm/cozy override:
2700 K, 65%. Storm = cozy, not office-bright. - Sun below horizon → the circadian phase map keyed on
sensor.ambiance_scene:
| phase | Kelvin | brightness % |
|---|---|---|
| night | 2200 | 40 |
| dawn | 2700 | 35 |
| first_light | 3000 | 45 |
| sunrise | 3500 | 55 |
| morning | 4500 | 80 |
| midday | 5500 | 100 |
| afternoon | 4500 | 85 |
| golden_hour | 2900 | 75 |
| sunset | 2500 | 65 |
| dusk | 2200 | 55 |
(brightness bumped 2026-07-21 — the evening/night end was too dim for reading; night 20→40, golden_hour 60→75, sunset 45→65, dusk 30→55, cozy 50→65.)
(midday/morning/afternoon/sunrise phases only occur with the sun up, so in practice the sun-below path uses the warm end — golden_hour/sunset/dusk/night/first_light/dawn. The full table is kept so the map is total and future-proof.) Values chosen for a bedroom; tune live later.
brightness sent as brightness_pct; Kelvin as color_temp_kelvin. All within the bulb's 2000–6535 K.
Priority stack (rewritten script.master_bedroom_sitting_lamp_render)¶
Same 5 branches; branch 2/5 gates use the new dark, ON actions render circadian:
- Away (
zone.home | int == 0) →stop(away presence-sim owns the lamp). - Waking (
morning_armedon ANDdark) → ON circadian. - Sleeping (
input_boolean.sleepingon) → OFF. - Movie (
media_player.master_bedroom_apple_tvplaying ORmedia_player.bedroom_tvon) → OFF. - Occupied (
binary_sensor.bedroom_occupancyon) AND ON-window → ON circadian, where ON-window =(sun below_horizon AND time 16:00–02:00)← evening, as before (the wrap-around gate that stops a 5 am relight when a partner's still in bed after Good Morning clearssleeping) OR(sun above_horizon AND dark-by-lux)← daytime storm (renders the warm/cozy override). - default → OFF.
Rendering ON is a shared step: compute (kelvin, pct) per the rule above, light.turn_on the bulb. Mode stays restart.
Trigger automation (master_bedroom_sitting_lamp_render_triggers)¶
Keep existing (morning_armed, sleeping, bedroom_occupancy on / off-for-20m, sun.sun below/above, the two media players, zone.home). Add: - sensor.ambiance_scene (state change) → re-render so Kelvin/brightness drift through the evening. - sensor.night_light_7_illuminance — trigger on crossing the threshold. Since the threshold is a tunable helper, use a template trigger on the dark-by-lux boolean (or a numeric trigger with the helper as the below:) so storm onset/clear re-renders. Also add input_number.sitting_lamp_dark_lux as a trigger so changing the threshold re-renders.
Away simulation / away-off (_away_sim, _away_off)¶
Move from switch.turn_on/off to light.turn_on/off on the bulb. Away-sim's random "on" turns the bulb to a fixed cozy 2500 K, 40% (a lived-in look); "off" turns it off. Away-off at 23:00 → light.turn_off.
Downstream reference updates¶
Change every switch.master_bedroom_sitting_lamp reference that means "the sitting lamp" to the bulb: - script.good_night (scripts.yaml) and script.we_are_leaving (lists it explicitly ~L169). - homekit.yaml (expose the bulb instead of the switch, if it was exposed). - CLAUDE.md §23 (Master Bedroom Sitting Lamp) + the group.lights note (still not a member — the renderer owns sleep-off), and automations.yaml §50 header comment. - Docs: docs/lighting.md (schedule + description), docs/automations.md (§50 reference), projects/smart_lighting_scenes.md. - The old switch.master_bedroom_sitting_lamp is left paired (not removed) but no longer driven.
Testing / deploy¶
- New entity refs for Tier-3: add to
tests/fixtures/entities_allow.txtwith comments —light.master_bedroom_sitting_room_lamp_bulb(Hue, already live but not yet snapshotted) andinput_number.sitting_lamp_dark_lux(created on next restart).sensor.night_light_7_illuminanceis already inentities.txt. Fold all into the nextmake snapshot. - Deploy path: touches
scripts.yaml+automations.yaml+configuration.yaml→ CI runscheck_configthen fullha core restart(configuration.yaml changed). Automations hot-reload only applies when just automations change; here it's a restart. - Post-deploy: set
input_number.sitting_lamp_dark_luxto 20; verify the bulb renders circadian in the evening and warm/cozy on a simulated dark-lux (or lower the threshold to test). Render-test thedarktemplate live via/api/template.
Addendum (2026-07-21) — manual override + Alexa on/off¶
Follow-up to the same ticket: the dark test means the lamp never comes on when the room is bright, so add a way to force it on (e.g. a daytime reading light).
- New helper
input_boolean.sitting_lamp_manual(friendly name "Sitting Room Lamp", persistent). - Renderer: new
manualbranch below sleeping/movie, above the dark-gated occupied branch —manual on → ON at the circadian setting, ignoring the dark test.cozyis tightened to the storm case only (sun above horizon AND lux-dark AND occupied), so manual-on by day uses the true cool/bright midday circadian value rather than the warm override. - Auto-clear:
automation.master_bedroom_sitting_lamp_manual_clear— bedroom empty for 2 min AND manual on → turn the override off (→ renderer mode).script.wake_housealso clears it each morning (alongside the corner/office manuals). The manual boolean is added as a render trigger. - Alexa: expose
input_boolean.sitting_lamp_manualas the switch "Sitting Room Lamp" (a switch supports both on and off, unlike a script-scene). "On" = force on; "off" = back to renderer mode (dark by day / when you leave; the evening ambiance rule may still light it — accepted, per the "off = renderer mode" decision). The raw bulb stays unexposed to Alexa (direct control would fight the renderer). First non-script entity on the Alexa allowlist — update the CLAUDE.md index. - Decisions (user-confirmed): manual only ignores the dark test (sleeping/movie still win); Alexa "off" returns to auto (no separate force-off state). A hard force-off is a possible later follow-up.
Out of scope / follow-ups¶
- No colour (xy) scenes — circadian is colour-temperature only (bedroom, not a party lamp).
- Adaptive-Lighting HACS not introduced; we reuse the home-grown
ambiance_scenesun tracker for consistency with the Govee lamps.