// the find
basnijholt/adaptive-lighting
Adaptive Lighting custom component for Home Assistant
Adaptive Lighting is a Home Assistant custom component that adjusts light brightness and color temperature throughout the day based on the sun's position, following circadian rhythm principles. It intercepts `light.turn_on` calls to apply color/brightness immediately, runs on a configurable interval, and handles manual override detection. This is for Home Assistant users who want their smart lights to feel less like a tech demo and more like real lighting.
The manual control detection is actually well-thought-out — it tracks whether a change came from HA or from outside it, and `pause_changed` mode lets color and brightness adapt independently when only one was manually touched. The configuration surface is unusually complete: you can clamp sunrise/sunset times, offset them, choose between linear/tanh/default brightness curves, and separate the turn-on commands for hardware that can't accept color and brightness simultaneously. Translation coverage across 30+ languages is real work, not an afterthought. The test suite covers adaptation math and config flow separately, which means the circadian calculation logic can be verified without spinning up a full HA instance.
`detect_non_ha_changes` works by calling `homeassistant.update_entity` on every interval tick, which on a big light setup will generate noticeable bus traffic — the docs warn about it but it's architecturally inelegant and there's no better solution offered. `change_switch_settings` doesn't persist to config, so any runtime tuning (alarm-based sunrise, scene-triggered overrides) evaporates on restart; this is a real usability hole for anyone building automations that depend on it. The `multi_light_intercept` option that splits a single `light.turn_on` into per-light calls can silently break scenes if lights span different switches — the warning is buried in a config table footnote. The webapp simulator is a separate Python app with its own requirements that duplicates the color math from the main component rather than importing it, so drift between them is a when not an if.