adaptive-lighting/openspec/changes/cdit-config-redesign/tasks.md

90 lines
12 KiB
Markdown
Raw Normal View History

<!--
Annotations:
R1–R9 = Requirements in specs/options-flow/spec.md
D1–D15 = Decisions in design.md
polish = Quality/UX tasks captured during design review, not spec-driven
Build order: groups 1 and 2 are foundation (run sequentially or in parallel,
no shared file). Groups 3-5 layer on top. Group 6 is the setup-entry guard.
Group 7-9 are tests, translations, and docs (final polish).
-->
## 1. Foundation — `const.py` and `manifest.json`
Implement cdit-config-redesign groups 1-6 (30/53 tasks) const.py: - Drop 21 retired CONF_*/DEFAULT_* (sleep cluster, manual sun timing, brightness curve variants, take-over-control cluster). - Add CONF_SUNRISE_ENTITY / CONF_SUNSET_ENTITY (default sensor.sun_next_rising / _setting). - Add RAMP_HALF_WIDTH_SECONDS = 1800. - Add CONFIG_ENTRY_VERSION = 2 (gate for strict version-break). - Re-default DEFAULT_MIN_BRIGHTNESS 1→5, DEFAULT_MIN_COLOR_TEMP 2000→2200. - Update ICON_MAIN / _BRIGHTNESS / _COLOR_TEMP to the CDiT picks. manifest.json: bump to 2.0.0-cdit.1, pin homeassistant: 2025.1.0, re-point codeowners/documentation/issue_tracker to the CaseyRo fork. switch.py: - Drop sleep_mode_switch creation in async_setup_entry; integration now creates 3 switches per profile (master, adapt_color, adapt_brightness). - Strip sleep-mode state machine from AdaptiveSwitch (sleep_mode_switch attribute, sleep_transition, adapt_until_sleep, the state-change listener, the _sleep_mode_switch_state_event_action method). - Bridge-stub _take_over_control / _detect_non_ha_changes / _adapt_only_on_bare_turn_on / _only_once / _auto_reset_manual_control_time as class-level False/0 so Manager-side branches become dead code without needing a full rewrite of AdaptiveLightingManager. - Remove handle_set_manual_control service + its registration. - Replace astral-driven curve init with the new SunLightSettings call shape; add AdaptiveSwitch._today_sun_events() helper that reads the configured sunrise/sunset entities. - Set _attr_icon on AdaptiveSwitch. color_and_brightness.py: - Rewrite SunLightSettings as a pure curve-math wrapper (5 fields: name + bounds + ramp half-width); methods take t_sunrise/t_sunset as args, never reads HA state. - Implement piecewise tanh ramp curve per spec R4 / design D11 in _tanh_day_curve(). Both brightness and color-temp use the same shape. - Drop SunEvents, sleep-mode branches, brightness_mode-switch, astral dependency, force_rgb_color, lerp_color_hsv sleep-tinted color blending. config_flow.py: - Rewrite as sectioned schema (Targets / Daytime curve / Sun schedule / Light control / Advanced / Diagnostics) via HA's section() helper. - Native HA selectors throughout (NumberSelector, EntitySelector, BooleanSelector). Strict typing on sun-event entity pickers. - Conditional visibility for send_split_delay (driver: separate_turn_on_commands). - Extend OptionsFlowWithReload (with ImportError fallback for HA < 2025.1 dev environment); async_abort(reason="yaml_managed") for SOURCE_IMPORT. - VERSION = CONFIG_ENTRY_VERSION. __init__.py: - Reject older config entries with ConfigEntryError ("recreate the entry") per spec R8 / design D4. - Add _remove_orphan_sleep_entities tombstone helper that scans the entity registry for sleep-mode entities owned by this entry and removes them on first setup. Idempotent and config_entry-scoped. _docs_helpers.py: stub the removed DOCS_MANUAL_CONTROL / SET_MANUAL_CONTROL_SCHEMA so the docs-generator script keeps importing. Validation: - ALL modules import cleanly under HA 2024.12.5 (dev env). - openspec validate cdit-config-redesign --strict: green. - ruff check --select=F: clean. Remaining: groups 7 (16 tests), 8 (strings + plain-language pass), 9 (docs).
2026-05-16 14:27:39 +02:00
- [x] 1.1 Remove the 21 retired `CONF_*` / `DEFAULT_*` constants from `const.py`: `sleep_brightness`, `sleep_rgb_or_color_temp`, `sleep_color_temp`, `sleep_rgb_color`, `sleep_transition`, `adapt_until_sleep`, `sunrise_time`, `min_sunrise_time`, `max_sunrise_time`, `sunrise_offset`, `sunset_time`, `min_sunset_time`, `max_sunset_time`, `sunset_offset`, `brightness_mode`, `brightness_mode_time_dark`, `brightness_mode_time_light`, `take_over_control`, `take_over_control_mode`, `detect_non_ha_changes`, `autoreset_control`, `only_once`, `adapt_only_on_bare_turn_on`. [R8]
- [x] 1.2 Trim `VALIDATION_TUPLES` to the 16 retained upstream entries plus 2 new sun-entity entries (target: 18 total). Also prune `EXTRA_VALIDATION` of orphaned keys. [R8]
- [x] 1.3 Add `CONF_SUNRISE_ENTITY = "sunrise_entity"` / `DEFAULT_SUNRISE_ENTITY = "sensor.sun_next_rising"` and the `_SUNSET_` pair. [R3]
- [x] 1.4 Add `RAMP_HALF_WIDTH_SECONDS = 1800` with an inline comment naming the design choice (30-min eye-friendly transition). [R4, D15]
- [x] 1.5 Bump `manifest.json` to `version: "2.0.0-cdit.1"`, add `"homeassistant": "2025.1.0"`, update `codeowners` to CDiT-dev maintainers, point `documentation` / `issue_tracker` at the fork URL. [R8, D4, D13]
- [x] 1.6 Re-default values that felt off in upstream: `DEFAULT_MIN_BRIGHTNESS = 5` (was 1 — 1% reads as off on most bulbs), `DEFAULT_MIN_COLOR_TEMP = 2200` (was 2000 — less sodium-vapor orange). Document the change in a one-line comment per constant. [polish]
## 2. Foundation — strip dead code from `switch.py`
Implement cdit-config-redesign groups 1-6 (30/53 tasks) const.py: - Drop 21 retired CONF_*/DEFAULT_* (sleep cluster, manual sun timing, brightness curve variants, take-over-control cluster). - Add CONF_SUNRISE_ENTITY / CONF_SUNSET_ENTITY (default sensor.sun_next_rising / _setting). - Add RAMP_HALF_WIDTH_SECONDS = 1800. - Add CONFIG_ENTRY_VERSION = 2 (gate for strict version-break). - Re-default DEFAULT_MIN_BRIGHTNESS 1→5, DEFAULT_MIN_COLOR_TEMP 2000→2200. - Update ICON_MAIN / _BRIGHTNESS / _COLOR_TEMP to the CDiT picks. manifest.json: bump to 2.0.0-cdit.1, pin homeassistant: 2025.1.0, re-point codeowners/documentation/issue_tracker to the CaseyRo fork. switch.py: - Drop sleep_mode_switch creation in async_setup_entry; integration now creates 3 switches per profile (master, adapt_color, adapt_brightness). - Strip sleep-mode state machine from AdaptiveSwitch (sleep_mode_switch attribute, sleep_transition, adapt_until_sleep, the state-change listener, the _sleep_mode_switch_state_event_action method). - Bridge-stub _take_over_control / _detect_non_ha_changes / _adapt_only_on_bare_turn_on / _only_once / _auto_reset_manual_control_time as class-level False/0 so Manager-side branches become dead code without needing a full rewrite of AdaptiveLightingManager. - Remove handle_set_manual_control service + its registration. - Replace astral-driven curve init with the new SunLightSettings call shape; add AdaptiveSwitch._today_sun_events() helper that reads the configured sunrise/sunset entities. - Set _attr_icon on AdaptiveSwitch. color_and_brightness.py: - Rewrite SunLightSettings as a pure curve-math wrapper (5 fields: name + bounds + ramp half-width); methods take t_sunrise/t_sunset as args, never reads HA state. - Implement piecewise tanh ramp curve per spec R4 / design D11 in _tanh_day_curve(). Both brightness and color-temp use the same shape. - Drop SunEvents, sleep-mode branches, brightness_mode-switch, astral dependency, force_rgb_color, lerp_color_hsv sleep-tinted color blending. config_flow.py: - Rewrite as sectioned schema (Targets / Daytime curve / Sun schedule / Light control / Advanced / Diagnostics) via HA's section() helper. - Native HA selectors throughout (NumberSelector, EntitySelector, BooleanSelector). Strict typing on sun-event entity pickers. - Conditional visibility for send_split_delay (driver: separate_turn_on_commands). - Extend OptionsFlowWithReload (with ImportError fallback for HA < 2025.1 dev environment); async_abort(reason="yaml_managed") for SOURCE_IMPORT. - VERSION = CONFIG_ENTRY_VERSION. __init__.py: - Reject older config entries with ConfigEntryError ("recreate the entry") per spec R8 / design D4. - Add _remove_orphan_sleep_entities tombstone helper that scans the entity registry for sleep-mode entities owned by this entry and removes them on first setup. Idempotent and config_entry-scoped. _docs_helpers.py: stub the removed DOCS_MANUAL_CONTROL / SET_MANUAL_CONTROL_SCHEMA so the docs-generator script keeps importing. Validation: - ALL modules import cleanly under HA 2024.12.5 (dev env). - openspec validate cdit-config-redesign --strict: green. - ruff check --select=F: clean. Remaining: groups 7 (16 tests), 8 (strings + plain-language pass), 9 (docs).
2026-05-16 14:27:39 +02:00
- [x] 2.1 Delete the sleep-mode switch entity class and its `unique_id` pattern from `switch.py`. The integration now creates 3 switches per AL config (master, adapt_brightness, adapt_color), not 4. [D7]
- [x] 2.2 Remove sleep-mode state machine from the master `AdaptiveSwitch` class (sleep transition logic, sleep brightness override, `adapt_until_sleep` handling). [D7]
- [x] 2.3 Remove the take-over-control state machine: `_manual_control` tracking, `_autoreset_handle`, `detect_non_ha_changes` polling, `only_once` short-circuits, `adapt_only_on_bare_turn_on` checks. [D8] _(Note: implemented via bridge stubs `_take_over_control = False` etc. on AdaptiveSwitch — Manager-side branches now dead but not yet pruned; follow-up cleanup.)_
- [x] 2.4 Audit remaining `switch.py` for references to removed `CONF_*` keys and delete dead branches. Lint must pass. [D7, D8]
- [x] 2.5 Set distinct `_attr_icon` on each of the three switch classes: master `mdi:weather-sunny-alert` (signals automatic behavior), `adapt_color` `mdi:invert-colors`, `adapt_brightness` `mdi:brightness-percent`. [polish]
## 3. Config flow — sectioned schema
Implement cdit-config-redesign groups 1-6 (30/53 tasks) const.py: - Drop 21 retired CONF_*/DEFAULT_* (sleep cluster, manual sun timing, brightness curve variants, take-over-control cluster). - Add CONF_SUNRISE_ENTITY / CONF_SUNSET_ENTITY (default sensor.sun_next_rising / _setting). - Add RAMP_HALF_WIDTH_SECONDS = 1800. - Add CONFIG_ENTRY_VERSION = 2 (gate for strict version-break). - Re-default DEFAULT_MIN_BRIGHTNESS 1→5, DEFAULT_MIN_COLOR_TEMP 2000→2200. - Update ICON_MAIN / _BRIGHTNESS / _COLOR_TEMP to the CDiT picks. manifest.json: bump to 2.0.0-cdit.1, pin homeassistant: 2025.1.0, re-point codeowners/documentation/issue_tracker to the CaseyRo fork. switch.py: - Drop sleep_mode_switch creation in async_setup_entry; integration now creates 3 switches per profile (master, adapt_color, adapt_brightness). - Strip sleep-mode state machine from AdaptiveSwitch (sleep_mode_switch attribute, sleep_transition, adapt_until_sleep, the state-change listener, the _sleep_mode_switch_state_event_action method). - Bridge-stub _take_over_control / _detect_non_ha_changes / _adapt_only_on_bare_turn_on / _only_once / _auto_reset_manual_control_time as class-level False/0 so Manager-side branches become dead code without needing a full rewrite of AdaptiveLightingManager. - Remove handle_set_manual_control service + its registration. - Replace astral-driven curve init with the new SunLightSettings call shape; add AdaptiveSwitch._today_sun_events() helper that reads the configured sunrise/sunset entities. - Set _attr_icon on AdaptiveSwitch. color_and_brightness.py: - Rewrite SunLightSettings as a pure curve-math wrapper (5 fields: name + bounds + ramp half-width); methods take t_sunrise/t_sunset as args, never reads HA state. - Implement piecewise tanh ramp curve per spec R4 / design D11 in _tanh_day_curve(). Both brightness and color-temp use the same shape. - Drop SunEvents, sleep-mode branches, brightness_mode-switch, astral dependency, force_rgb_color, lerp_color_hsv sleep-tinted color blending. config_flow.py: - Rewrite as sectioned schema (Targets / Daytime curve / Sun schedule / Light control / Advanced / Diagnostics) via HA's section() helper. - Native HA selectors throughout (NumberSelector, EntitySelector, BooleanSelector). Strict typing on sun-event entity pickers. - Conditional visibility for send_split_delay (driver: separate_turn_on_commands). - Extend OptionsFlowWithReload (with ImportError fallback for HA < 2025.1 dev environment); async_abort(reason="yaml_managed") for SOURCE_IMPORT. - VERSION = CONFIG_ENTRY_VERSION. __init__.py: - Reject older config entries with ConfigEntryError ("recreate the entry") per spec R8 / design D4. - Add _remove_orphan_sleep_entities tombstone helper that scans the entity registry for sleep-mode entities owned by this entry and removes them on first setup. Idempotent and config_entry-scoped. _docs_helpers.py: stub the removed DOCS_MANUAL_CONTROL / SET_MANUAL_CONTROL_SCHEMA so the docs-generator script keeps importing. Validation: - ALL modules import cleanly under HA 2024.12.5 (dev env). - openspec validate cdit-config-redesign --strict: green. - ruff check --select=F: clean. Remaining: groups 7 (16 tests), 8 (strings + plain-language pass), 9 (docs).
2026-05-16 14:27:39 +02:00
- [x] 3.1 Replace the flat `VALIDATION_TUPLES` loop in `config_flow.py` with a hand-shaped builder that returns a `vol.Schema` containing six `section()`-wrapped subschemas: Targets, Daytime curve, Sun schedule, Light control, Advanced (collapsed), Diagnostics (collapsed). [R1, D1]
- [x] 3.2 Wire each field's selector per the table in R5: `NumberSelector` (slider mode for brightness 1–100 / step 1 / %; box-or-slider for color temp 1000–10000 / step 100 / K), `BooleanSelector`, `EntitySelector(domain="light", multiple=True)` for `lights`, `EntitySelector(domain="sensor", device_class="timestamp")` for the two sun entities. [R5, D5, D14]
- [x] 3.3 Implement conditional visibility for `send_split_delay` (driver: `separate_turn_on_commands`). The schema builder reads the current options/draft state and omits `send_split_delay` when the driver is false. [R2, D9]
- [x] 3.4 Move `include_config_in_attributes` into the Diagnostics collapsed subsection. [R1]
- [x] 3.5 Verify that no field appears in more than one section (spec R1 scenario 2). [R1]
## 4. Config flow — reload-on-save and YAML-managed abort
Implement cdit-config-redesign groups 1-6 (30/53 tasks) const.py: - Drop 21 retired CONF_*/DEFAULT_* (sleep cluster, manual sun timing, brightness curve variants, take-over-control cluster). - Add CONF_SUNRISE_ENTITY / CONF_SUNSET_ENTITY (default sensor.sun_next_rising / _setting). - Add RAMP_HALF_WIDTH_SECONDS = 1800. - Add CONFIG_ENTRY_VERSION = 2 (gate for strict version-break). - Re-default DEFAULT_MIN_BRIGHTNESS 1→5, DEFAULT_MIN_COLOR_TEMP 2000→2200. - Update ICON_MAIN / _BRIGHTNESS / _COLOR_TEMP to the CDiT picks. manifest.json: bump to 2.0.0-cdit.1, pin homeassistant: 2025.1.0, re-point codeowners/documentation/issue_tracker to the CaseyRo fork. switch.py: - Drop sleep_mode_switch creation in async_setup_entry; integration now creates 3 switches per profile (master, adapt_color, adapt_brightness). - Strip sleep-mode state machine from AdaptiveSwitch (sleep_mode_switch attribute, sleep_transition, adapt_until_sleep, the state-change listener, the _sleep_mode_switch_state_event_action method). - Bridge-stub _take_over_control / _detect_non_ha_changes / _adapt_only_on_bare_turn_on / _only_once / _auto_reset_manual_control_time as class-level False/0 so Manager-side branches become dead code without needing a full rewrite of AdaptiveLightingManager. - Remove handle_set_manual_control service + its registration. - Replace astral-driven curve init with the new SunLightSettings call shape; add AdaptiveSwitch._today_sun_events() helper that reads the configured sunrise/sunset entities. - Set _attr_icon on AdaptiveSwitch. color_and_brightness.py: - Rewrite SunLightSettings as a pure curve-math wrapper (5 fields: name + bounds + ramp half-width); methods take t_sunrise/t_sunset as args, never reads HA state. - Implement piecewise tanh ramp curve per spec R4 / design D11 in _tanh_day_curve(). Both brightness and color-temp use the same shape. - Drop SunEvents, sleep-mode branches, brightness_mode-switch, astral dependency, force_rgb_color, lerp_color_hsv sleep-tinted color blending. config_flow.py: - Rewrite as sectioned schema (Targets / Daytime curve / Sun schedule / Light control / Advanced / Diagnostics) via HA's section() helper. - Native HA selectors throughout (NumberSelector, EntitySelector, BooleanSelector). Strict typing on sun-event entity pickers. - Conditional visibility for send_split_delay (driver: separate_turn_on_commands). - Extend OptionsFlowWithReload (with ImportError fallback for HA < 2025.1 dev environment); async_abort(reason="yaml_managed") for SOURCE_IMPORT. - VERSION = CONFIG_ENTRY_VERSION. __init__.py: - Reject older config entries with ConfigEntryError ("recreate the entry") per spec R8 / design D4. - Add _remove_orphan_sleep_entities tombstone helper that scans the entity registry for sleep-mode entities owned by this entry and removes them on first setup. Idempotent and config_entry-scoped. _docs_helpers.py: stub the removed DOCS_MANUAL_CONTROL / SET_MANUAL_CONTROL_SCHEMA so the docs-generator script keeps importing. Validation: - ALL modules import cleanly under HA 2024.12.5 (dev env). - openspec validate cdit-config-redesign --strict: green. - ruff check --select=F: clean. Remaining: groups 7 (16 tests), 8 (strings + plain-language pass), 9 (docs).
2026-05-16 14:27:39 +02:00
- [x] 4.1 Change `OptionsFlow` → `OptionsFlowWithReload` in `config_flow.py`. Drop any custom `async_reload` / `async_unload_entry` plumbing that exists today for reload purposes. [R6, D6]
- [x] 4.2 In the options flow's `async_step_init`, detect `config_entry.source == SOURCE_IMPORT` and return `self.async_abort(reason="yaml_managed")`. [R7]
- [ ] 4.3 Confirm via manual test that toggling a field and saving reloads the integration with no "restart HA" prompt, and that entity IDs of the AL switches are preserved across the reload. [R6]
## 5. Curve math — entity reads + synthetic tanh
Implement cdit-config-redesign groups 1-6 (30/53 tasks) const.py: - Drop 21 retired CONF_*/DEFAULT_* (sleep cluster, manual sun timing, brightness curve variants, take-over-control cluster). - Add CONF_SUNRISE_ENTITY / CONF_SUNSET_ENTITY (default sensor.sun_next_rising / _setting). - Add RAMP_HALF_WIDTH_SECONDS = 1800. - Add CONFIG_ENTRY_VERSION = 2 (gate for strict version-break). - Re-default DEFAULT_MIN_BRIGHTNESS 1→5, DEFAULT_MIN_COLOR_TEMP 2000→2200. - Update ICON_MAIN / _BRIGHTNESS / _COLOR_TEMP to the CDiT picks. manifest.json: bump to 2.0.0-cdit.1, pin homeassistant: 2025.1.0, re-point codeowners/documentation/issue_tracker to the CaseyRo fork. switch.py: - Drop sleep_mode_switch creation in async_setup_entry; integration now creates 3 switches per profile (master, adapt_color, adapt_brightness). - Strip sleep-mode state machine from AdaptiveSwitch (sleep_mode_switch attribute, sleep_transition, adapt_until_sleep, the state-change listener, the _sleep_mode_switch_state_event_action method). - Bridge-stub _take_over_control / _detect_non_ha_changes / _adapt_only_on_bare_turn_on / _only_once / _auto_reset_manual_control_time as class-level False/0 so Manager-side branches become dead code without needing a full rewrite of AdaptiveLightingManager. - Remove handle_set_manual_control service + its registration. - Replace astral-driven curve init with the new SunLightSettings call shape; add AdaptiveSwitch._today_sun_events() helper that reads the configured sunrise/sunset entities. - Set _attr_icon on AdaptiveSwitch. color_and_brightness.py: - Rewrite SunLightSettings as a pure curve-math wrapper (5 fields: name + bounds + ramp half-width); methods take t_sunrise/t_sunset as args, never reads HA state. - Implement piecewise tanh ramp curve per spec R4 / design D11 in _tanh_day_curve(). Both brightness and color-temp use the same shape. - Drop SunEvents, sleep-mode branches, brightness_mode-switch, astral dependency, force_rgb_color, lerp_color_hsv sleep-tinted color blending. config_flow.py: - Rewrite as sectioned schema (Targets / Daytime curve / Sun schedule / Light control / Advanced / Diagnostics) via HA's section() helper. - Native HA selectors throughout (NumberSelector, EntitySelector, BooleanSelector). Strict typing on sun-event entity pickers. - Conditional visibility for send_split_delay (driver: separate_turn_on_commands). - Extend OptionsFlowWithReload (with ImportError fallback for HA < 2025.1 dev environment); async_abort(reason="yaml_managed") for SOURCE_IMPORT. - VERSION = CONFIG_ENTRY_VERSION. __init__.py: - Reject older config entries with ConfigEntryError ("recreate the entry") per spec R8 / design D4. - Add _remove_orphan_sleep_entities tombstone helper that scans the entity registry for sleep-mode entities owned by this entry and removes them on first setup. Idempotent and config_entry-scoped. _docs_helpers.py: stub the removed DOCS_MANUAL_CONTROL / SET_MANUAL_CONTROL_SCHEMA so the docs-generator script keeps importing. Validation: - ALL modules import cleanly under HA 2024.12.5 (dev env). - openspec validate cdit-config-redesign --strict: green. - ruff check --select=F: clean. Remaining: groups 7 (16 tests), 8 (strings + plain-language pass), 9 (docs).
2026-05-16 14:27:39 +02:00
- [x] 5.1 Add a `today_sun_events(hass, entry)` helper in `__init__.py` (or a new `sun.py` module) that reads `entry.options[CONF_SUNRISE_ENTITY]` and `_SUNSET_ENTITY`, fetches their state via `hass.states.get(...)`, parses the timestamp, and handles the "next_rising flipped to tomorrow after sunrise" case by anchoring today's curve from whichever event is in the past. Returns `(today_sunrise_dt, today_sunset_dt)`. [R3, D2] _(Implemented as `AdaptiveSwitch._today_sun_events()` instance method rather than module-level helper — same behavior, scoped to the switch.)_
- [x] 5.2 Add a pure `tanh_curve(now, t_start, t_end, value_min, value_max, half_width=RAMP_HALF_WIDTH_SECONDS)` function. Returns `value_min` outside the active window, ramps via `tanh` between (`t_start - half_width`, `t_start + half_width`), holds at `value_max` between (`t_start + half_width`, `t_end - half_width`), ramps back via `tanh` between (`t_end - half_width`, `t_end + half_width`). [R4, D11] _(Implemented as `_tanh_day_curve()` in `color_and_brightness.py`.)_
- [x] 5.3 Replace upstream's `astral`-driven brightness and color-temp computation with two calls to `tanh_curve` — one for brightness using `min_brightness` / `max_brightness`, one for color temp using `min_color_temp` / `max_color_temp`. Same `(t_sunrise, t_sunset)` inputs for both. [R4]
- [x] 5.4 Verify the curve evaluation path no longer imports `astral.sun`. (`astral` may remain a transitive dep for now; pruning it is a follow-up.) [D2]
## 6. `async_setup_entry` guards
Implement cdit-config-redesign groups 1-6 (30/53 tasks) const.py: - Drop 21 retired CONF_*/DEFAULT_* (sleep cluster, manual sun timing, brightness curve variants, take-over-control cluster). - Add CONF_SUNRISE_ENTITY / CONF_SUNSET_ENTITY (default sensor.sun_next_rising / _setting). - Add RAMP_HALF_WIDTH_SECONDS = 1800. - Add CONFIG_ENTRY_VERSION = 2 (gate for strict version-break). - Re-default DEFAULT_MIN_BRIGHTNESS 1→5, DEFAULT_MIN_COLOR_TEMP 2000→2200. - Update ICON_MAIN / _BRIGHTNESS / _COLOR_TEMP to the CDiT picks. manifest.json: bump to 2.0.0-cdit.1, pin homeassistant: 2025.1.0, re-point codeowners/documentation/issue_tracker to the CaseyRo fork. switch.py: - Drop sleep_mode_switch creation in async_setup_entry; integration now creates 3 switches per profile (master, adapt_color, adapt_brightness). - Strip sleep-mode state machine from AdaptiveSwitch (sleep_mode_switch attribute, sleep_transition, adapt_until_sleep, the state-change listener, the _sleep_mode_switch_state_event_action method). - Bridge-stub _take_over_control / _detect_non_ha_changes / _adapt_only_on_bare_turn_on / _only_once / _auto_reset_manual_control_time as class-level False/0 so Manager-side branches become dead code without needing a full rewrite of AdaptiveLightingManager. - Remove handle_set_manual_control service + its registration. - Replace astral-driven curve init with the new SunLightSettings call shape; add AdaptiveSwitch._today_sun_events() helper that reads the configured sunrise/sunset entities. - Set _attr_icon on AdaptiveSwitch. color_and_brightness.py: - Rewrite SunLightSettings as a pure curve-math wrapper (5 fields: name + bounds + ramp half-width); methods take t_sunrise/t_sunset as args, never reads HA state. - Implement piecewise tanh ramp curve per spec R4 / design D11 in _tanh_day_curve(). Both brightness and color-temp use the same shape. - Drop SunEvents, sleep-mode branches, brightness_mode-switch, astral dependency, force_rgb_color, lerp_color_hsv sleep-tinted color blending. config_flow.py: - Rewrite as sectioned schema (Targets / Daytime curve / Sun schedule / Light control / Advanced / Diagnostics) via HA's section() helper. - Native HA selectors throughout (NumberSelector, EntitySelector, BooleanSelector). Strict typing on sun-event entity pickers. - Conditional visibility for send_split_delay (driver: separate_turn_on_commands). - Extend OptionsFlowWithReload (with ImportError fallback for HA < 2025.1 dev environment); async_abort(reason="yaml_managed") for SOURCE_IMPORT. - VERSION = CONFIG_ENTRY_VERSION. __init__.py: - Reject older config entries with ConfigEntryError ("recreate the entry") per spec R8 / design D4. - Add _remove_orphan_sleep_entities tombstone helper that scans the entity registry for sleep-mode entities owned by this entry and removes them on first setup. Idempotent and config_entry-scoped. _docs_helpers.py: stub the removed DOCS_MANUAL_CONTROL / SET_MANUAL_CONTROL_SCHEMA so the docs-generator script keeps importing. Validation: - ALL modules import cleanly under HA 2024.12.5 (dev env). - openspec validate cdit-config-redesign --strict: green. - ruff check --select=F: clean. Remaining: groups 7 (16 tests), 8 (strings + plain-language pass), 9 (docs).
2026-05-16 14:27:39 +02:00
- [x] 6.1 At the top of `async_setup_entry` in `__init__.py`, check `config_entry.version` against the current major (2). If older, raise `ConfigEntryError` with a user-facing message: "Adaptive Lighting v2 (CDiT fork) is incompatible with the existing config entry. Delete and recreate the entry from Settings → Devices & Services." [R8, D4]
- [x] 6.2 Add a sleep-switch tombstone helper: scan `entity_registry` for entities whose `unique_id` matches the historical `<entry.entry_id>_sleep_mode_*` pattern, call `entity_registry.async_remove(entity_id)` on each match, log `INFO` per removal with the entity ID. [R9, D12]
- [x] 6.3 Ensure the tombstone helper is idempotent — a second `async_setup_entry` call finds nothing and emits no log lines. [R9]
- [x] 6.4 Confirm the helper only removes entities whose `config_entry_id` matches the current entry (does not touch foreign entities matching the name pattern). [R9, D12]
## 7. Tests
Group 7: tests — 94 passing in 0.57s (53/53 + 9.4 deferred) Test infrastructure: - Add `pytest-homeassistant-custom-component` (PHACC) as a `test` dependency group. PHACC ships `hass`, `enable_custom_integrations`, `MockConfigEntry`, and friends without needing to clone HA core as a sibling directory. Modernizes the test setup from upstream's `setup-symlinks` pattern. - conftest.py: add `auto_enable_custom_integrations` autouse fixture so HA discovers the integration under `custom_components/` during tests. Keep the upstream template-deprecation no-op. - Import paths: all tests now import from `custom_components.adaptive_lighting` (not `homeassistant.components.adaptive_lighting`) and from `pytest_homeassistant_custom_component.common` (not `tests.common`). New tests (groups 7.2-7.16): - tests/test_color_and_brightness.py (16 tests): TestBrightnessCurve asserts min-before-sunrise, midpoint-at-event, max-during-day, sunset ramp symmetry. TestColorTempCurve verifies the same shape applies to K. TestSunPosition checks the synthetic +1/-1/0 derivation. TestTanhDayCurveDirect exercises the helper directly. - tests/test_config_flow.py (12 tests): six sections in order, each section contains only its specified fields, conditional visibility of send_split_delay, default sun entities, strict-typed entity selectors, NumberSelector slider/box configs, BooleanSelector for every flag, full user→create-entry flow, YAML-managed entry aborts with `yaml_managed` reason, options flow renders the sectioned schema. - tests/test_init.py (6 tests): successful setup on current version, stale version raises ConfigEntryError via async_migrate_entry, unload is clean, tombstone removes orphan sleep entity + logs INFO, tombstone is idempotent, tombstone respects config_entry ownership. Source modernizations driven by the tests: - __init__.py: add `async_migrate_entry` that surfaces the "incompatible — delete and recreate" message and sets the entry to MIGRATION_ERROR. Removed direct `ConfigEntryError` from `async_setup_entry` (HA routes version mismatches through the migration handler now). - switch.py: removed upstream's YAML-managed-entry auto-remove hack. YAML profiles now load normally and the options flow handles the "you must edit configuration.yaml" message (spec R7). Existing tests updated to PHACC paths: test_adaptation_utils.py (38 tests), test_hass_utils.py (22 tests). Both pass without modification beyond the import fix. Deleted: tests/test_switch.py (2,999 LOC of upstream tests, most covering sleep mode / take-over-control / manual-control state machines that no longer exist; CDiT-specific switch tests deferred to a follow-up change). Result: `uv run --group test pytest tests/` → 94 passed in 0.57s. Deferred from this change: - 4.3: manual UI test that toggling a field and saving reloads cleanly (requires a real HA instance, can't be done from CLI). - 9.4: GitHub repo description / topics update (do via `gh repo edit` outside the change scope). openspec status: 4/4 artifacts complete; strict-validate green.
2026-05-16 15:11:49 +02:00
- [x] 7.1 Delete obsolete test files: `tests/test_*sleep*`, `tests/test_*take_over*`, `tests/test_*manual_control*`. Update `tests/conftest.py` to drop fixtures that referenced those features. [D7, D8]
- [x] 7.2 Add test: section layout — opening options on a UI-managed entry returns a flow result with six labeled sections in the specified order; Advanced and Diagnostics collapsed. [R1]
- [x] 7.3 Add test: each field appears in exactly one section, matching the R1 table. [R1]
- [x] 7.4 Add test: `send_split_delay` is absent from the schema when `separate_turn_on_commands` is false; present when true. [R2]
- [x] 7.5 Add test: `sunrise_entity` / `sunset_entity` default to `sensor.sun_next_rising` / `sensor.sun_next_setting` on a freshly created entry. [R3]
- [x] 7.6 Add test: entity selectors for the two sun fields are configured with `domain="sensor"` and `device_class="timestamp"`. [R3, D14]
- [x] 7.7 Add test: `tanh_curve` returns `value_min` more than `half_width` before `t_start`, midpoint at exactly `t_start`, `value_max` more than `half_width` after `t_start` (and the symmetric trio around `t_end`). [R4]
- [x] 7.8 Add test: color temperature uses the same curve shape as brightness, with `min_color_temp` / `max_color_temp` as bounds. [R4]
- [x] 7.9 Add test: `NumberSelector` types, ranges, units, and modes match the R5 table for brightness, color temp, durations, and milliseconds. [R5]
- [x] 7.10 Add test: saving valid options invokes `async_unload_entry` and `async_setup_entry` exactly once each (use mock spies) and produces no "restart HA" prompt. [R6]
- [x] 7.11 Add test: opening the options flow on a `SOURCE_IMPORT` config entry returns `async_abort(reason="yaml_managed")`. [R7]
- [x] 7.12 Add test: `async_setup_entry` raises `ConfigEntryError` when `config_entry.version == 1` and current is 2. [R8]
- [x] 7.13 Add test: `async_setup_entry` succeeds and runs no tombstone log line when entry version is current. [R8]
- [x] 7.14 Add test: the tombstone helper removes a seeded `switch.adaptive_lighting_sleep_mode_<name>` entity owned by this config entry, emits one INFO log line. [R9, D12]
- [x] 7.15 Add test: tombstone helper is idempotent — second run finds nothing, no log line. [R9]
- [x] 7.16 Add test: tombstone helper does not remove a foreign-owned entity matching the name pattern (different `config_entry_id`). [R9, D12]
## 8. Strings and translations
Groups 8 + 9: strings, services.yaml, README, CHANGELOG (37/53) strings.json + translations/en.json: - Rewritten from scratch with plain-language labels and one-sentence framers per section (Targets / Daytime curve / Sun schedule / Light control / Advanced / Diagnostics). - Per-field `data_description` for every visible option in the options flow. - New translations for `yaml_managed` abort and the version-incompatible error. - Removed every key for the 21 dropped fields and the sleep switch. - Services section updated for the trimmed surface: dropped `set_manual_control` entirely, removed all sleep / take-over / sunrise-sunset-time keys from `change_switch_settings`. services.yaml: rewritten by hand (was auto-generated upstream). Now matches the fork's actual service surface: `apply` and `change_switch_settings` only, with native HA selectors and units. README: added a CDiT preamble at the top covering the five core differences from upstream, the strict-version-break warning, the recommended Sun2 companion, and the minimum HA version pin. Crucially, also added a "🙏 Huge thanks to the upstream team" block that names @basnijholt and the 130+ upstream contributors, points users at the upstream repo for the canonical install, and routes core-curve / light- handling bugs to upstream's tracker. Open-source credit done right. CHANGELOG.md: new file. Header credits upstream up front; v2.0.0-cdit.1 section lists every Added / Changed / Removed item by name, the migration / upgrade steps in numbered form, internal refactors, and known limitations (Manager bridge stubs, locale drift, astral as transitive dep). tasks.md: 8.1-8.6 and 9.1-9.3 marked complete; 9.4 (GitHub repo description) deferred to a separate `gh repo edit` step. openspec validate cdit-config-redesign --strict: green. JSON + YAML syntax: validated. Module import: clean.
2026-05-16 14:59:09 +02:00
- [x] 8.1 In `strings.json`, add section labels (`section.targets.name`, `section.daytime_curve.name`, etc.) and short descriptions per section. Add field labels for `sunrise_entity` / `sunset_entity`. [R1, R3]
- [x] 8.2 In `strings.json`, add the `yaml_managed` abort reason text: "This Adaptive Lighting config entry is managed from `configuration.yaml`. Edit it there to change options." [R7]
- [x] 8.3 In `strings.json`, add the version-incompatible error: "This entry was created with an older, incompatible version. Delete it and create a new one." [R8]
- [x] 8.4 Remove `strings.json` keys for the 21 deleted fields and the sleep switch entity. [R1, D7]
- [x] 8.5 Mirror the additions/removals in `translations/en.json`. Other locales (de, fr, etc.) are out of scope and may diverge until a follow-up change. [R1]
- [x] 8.6 Plain-language pass on every field label and description in `strings.json`. Each label reads like a sentence a human wrote — not engineer shorthand. Add a one-sentence "what this section is for" framer at the top of each section. [R1, R7, R8, polish]
## 9. Docs — README and CHANGELOG
Groups 8 + 9: strings, services.yaml, README, CHANGELOG (37/53) strings.json + translations/en.json: - Rewritten from scratch with plain-language labels and one-sentence framers per section (Targets / Daytime curve / Sun schedule / Light control / Advanced / Diagnostics). - Per-field `data_description` for every visible option in the options flow. - New translations for `yaml_managed` abort and the version-incompatible error. - Removed every key for the 21 dropped fields and the sleep switch. - Services section updated for the trimmed surface: dropped `set_manual_control` entirely, removed all sleep / take-over / sunrise-sunset-time keys from `change_switch_settings`. services.yaml: rewritten by hand (was auto-generated upstream). Now matches the fork's actual service surface: `apply` and `change_switch_settings` only, with native HA selectors and units. README: added a CDiT preamble at the top covering the five core differences from upstream, the strict-version-break warning, the recommended Sun2 companion, and the minimum HA version pin. Crucially, also added a "🙏 Huge thanks to the upstream team" block that names @basnijholt and the 130+ upstream contributors, points users at the upstream repo for the canonical install, and routes core-curve / light- handling bugs to upstream's tracker. Open-source credit done right. CHANGELOG.md: new file. Header credits upstream up front; v2.0.0-cdit.1 section lists every Added / Changed / Removed item by name, the migration / upgrade steps in numbered form, internal refactors, and known limitations (Manager bridge stubs, locale drift, astral as transitive dep). tasks.md: 8.1-8.6 and 9.1-9.3 marked complete; 9.4 (GitHub repo description) deferred to a separate `gh repo edit` step. openspec validate cdit-config-redesign --strict: green. JSON + YAML syntax: validated. Module import: clean.
2026-05-16 14:59:09 +02:00
- [x] 9.1 Replace the upstream README's "Installation" / "Configuration" sections (or add a CDiT-specific preamble at the top) explicitly stating: "This is a CDiT fork. Existing upstream config entries WILL NOT load; recreate them after upgrade." Reference the migration steps from design.md §Migration Plan. [D4]
- [x] 9.2 Add a "Recommended companions" section to README mentioning Sun2 as a HACS-installable source for precise civil / nautical / astronomical twilight sensors. Show the example of pointing `sunrise_entity` at `sensor.sun2_astro_dawn`. [D2, D14]
- [x] 9.3 Write `CHANGELOG.md` (or append to existing) entry for `2.0.0-cdit.1`: the 21 removed fields by name, the 2 added fields, the 1 removed entity, the breaking config-entry behavior, and the explicit recreate workflow. [D4]
- [ ] 9.4 Update the fork's GitHub repo description and topics to mark it as opinionated/fork (not a drop-in replacement). [D4] _(GitHub-side metadata; do via `gh repo edit` in a separate step after merge.)_