Commit graph

20 commits

Author SHA1 Message Date
Casey Romkes
f4ec31ecc8
docs: reconcile number-entity count to five (add ramp_half_width) (#1)
The README What's-new callout and the CHANGELOG 2.1.0 entry still said four number entities, while the README table/intro and the runtime-range-controls spec say five (the four range bounds plus ramp_half_width). Update the callout to list all five and add a CHANGELOG entry for the fifth entity; leave the historical 2.1.0 'four' entry intact. Docs only -- code and spec already reflect five. Filed as openspec change fix-number-entity-count-docs.
2026-07-08 23:02:22 +02:00
Casey
e6e6be31df Archive add-runtime-ramp-width: 15/15 + sync main specs 2026-06-07 22:30:58 +02:00
Casey
ada3538449 Rename range sliders to lower/upper + friendlier new-profile defaults
Device page sorts entities alphabetically by name, so 'Min/Max …' always
listed max above min ('a' < 'i'). Rename the four range numbers to
'Brightness lower/upper' and 'Color temp lower/upper' so each quantity's
lower bound sorts first. Pin suggested_object_id to the field key so new
profiles keep slugging number.<profile>_min_brightness etc., identical to
profiles created before the rename; existing entity_ids are registry-
preserved via unchanged unique_ids.

New-profile defaults: brightness 5-100% -> 10-90% (5% proved too dim on
gloomy days; 90% leaves manual-boost headroom), min color temp 2200K ->
2000K (deeper warm tone won in practice). Existing profiles unaffected —
saved options and restored slider values take precedence.

Specs synced: runtime-range-controls naming table + scenarios,
output-sensors collision references. Two new tests cover the slug pin
and the lower/upper friendly-name composition.
2026-06-05 10:49:40 +02:00
Casey
76f04237d7 lux_reduction sensor: report reduction %, not retained factor
The 'Lux reduction' sensor published the retained brightness factor, so it
read 100% when nothing was being reduced (0% actual reduction) — the label
and value were inverses. Now it publishes the actual reduction:
100 - round(factor*100), so 0% = no dimming, higher = more cut, None when
the gate is inactive.

Updates the output-sensors spec scenarios to match (71 -> 29, 100 -> 0,
adds the gate-inactive None case) and adds a publish-level value test
(none existed — the prior scenarios were never enforced in code).
2026-05-30 22:35:59 +02:00
Casey
c3b948874e Archive add-lux-target: 26/26 + sync main specs
New lux-feedback spec, updated options-flow (7 sections, lux conditionals,
live reading), updated output-sensors (conditional ambient_lux + lux_reduction).
2026-05-25 16:15:16 +02:00
Casey
11fe23253c Implement add-lux-target: reduce-only ambient lux gate
Optional per-profile lux sensor binding that dims lights when daylight
alone exceeds a user-set target (factor = target/current). Lights turn
off entirely below min_brightness. Two config fields, live lux reading
in the options flow, two conditional output sensors, sensor state
subscription with 5pp significance guard. 26/26 tasks, 138 tests green.
2026-05-25 16:11:52 +02:00
Casey
d31d1928e5 Archive add-output-sensors: 41/41 + new main spec
Sensors are live on Tailscale HA, the change is complete end-to-end.
Moves the change to openspec/changes/archive/2026-05-21-add-output-sensors/
and promotes the delta spec to openspec/specs/output-sensors/spec.md
as a new capability (5 requirements, 13 scenarios).
2026-05-21 10:34:44 +02:00
Casey
1b21228b30 Implement add-output-sensors: 36/41 tasks complete
Three sensor entities per AL profile expose the curve's current outputs
plus actual sun elevation as graphable numerics with
SensorStateClass.MEASUREMENT, so HA's recorder + apexcharts-card chart
them natively:

- sensor.<profile>_output_brightness   (% — from self._settings)
- sensor.<profile>_output_color_temp   (K — from self._settings)
- sensor.<profile>_sun_elevation       (° — from sun.sun.attributes.elevation)

Architecture: master switch publishes computed outputs to
hass.data[DOMAIN][entry_id]["outputs"] after each curve tick, then fires
a per-entry dispatcher signal. Sensors subscribe and read from the cache.
Single computation path, push-based updates, no polling.

The existing master switch attributes (brightness_pct, color_temp_kelvin,
synthetic sun_position in [-1,+1]) are unchanged — sensors are purely
additive. Manifest bumped to 2.2.0-cdit.1 (minor; no breaking changes).

Artifact realignment along the way: Q2 answer assumed sun_position was
Sun2 elevation degrees, but reading color_and_brightness.py revealed
it's a synthetic float in [-1,+1] derived from the brightness curve.
Switched to pulling actual elevation from sun.sun (Decision 8 added),
renamed sensor from sun_position to sun_elevation. Friendly names use
asymmetric "Output" prefix to dodge collision with the adapt-brightness
switch and Min/Max color-temp numbers.

Remaining: group 6 (manual live-HA verification on
homeassistant.onca-blenny.ts.net) — requires HACS deploy.

15 new sensor tests pass; full suite green (123/123). openspec
validate --strict green. ruff clean on new files.
2026-05-21 10:11:52 +02:00
Casey
a6eb2dbe68 Propose add-output-sensors: 3 sensors per profile for recorder graphing
Promotes the master switch's current_brightness, current_color_temp, and
sun_position attributes to first-class sensor entities with
SensorStateClass.MEASUREMENT so HA's recorder graphs them as numerics.
This is the explicit complement to the killed add-lovelace-card: stock
apexcharts-card / History panel becomes the charting story once values
live on graphable entities.

Architecture: master switch publishes computed outputs to
hass.data[DOMAIN][entry_id]["outputs"] after each curve tick, then fires
a per-entry dispatcher signal. Sensors subscribe to the signal and read
from the cache. Single computation path; pure-reader sensors.

Naming: "Output brightness" / "Output color temp" disambiguate from
adapt-brightness switch and min/max number entities. "Sun position"
needs no prefix. sun_position is solar elevation in degrees (unit °)
sourced from the user's configured Sun2 / sensor.sun_* entity.

4 artifacts: proposal, design (8 decisions), spec (5 requirements, 13
scenarios), tasks (7 groups, 33 checkboxes). Strict-validate green.
2026-05-18 10:07:50 +02:00
Casey
70983a82f4 Drop house-mode-modes and add-lovelace-card change proposals
Both fail the "will the maintainer actually use this?" test for a
single-household fork:

- house-mode-modes: state-driven switch flips are what Node-RED already
  does in the CDiT stack. Pushing them into the integration competes
  with Node-RED's purpose and adds HA-core-churn maintenance forever.
- add-lovelace-card: maintainer wouldn't use a custom Lit + TypeScript +
  Vite + HACS plugin card. Stock tile + apexcharts cards in Lovelace
  YAML cover the same need without a frontend ecosystem in a Python repo.

Integration scope stays: sun curve + runtime entities + options flow.
2026-05-17 22:21:12 +02:00
Casey
b8497b500f Archive add-runtime-range-controls
47/47 tasks complete, including live verification on production HA:
- 4 number entities × 6 profiles registered (42 AL entities total)
- Slider drag does not reload integration (entry.modified_at stable)
- Curve picks up new slider value on next recompute
- Options flow seeds from entity state, not entry.options
- Save snaps sliders to typed values via reload
- RestoreNumber persists slider position across HA restart
- Friendly names now read as "<profile> Brightness" / "Color" / etc.

Delta synced into canonical specs:
- options-flow MR4 (curve bound values come from entities)
- runtime-range-controls capability added (7 requirements)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-17 21:58:32 +02:00
Casey
d5d3ecc9d4 Implement add-runtime-range-controls
Adds four live-tunable `number` entities per AL profile (min/max
brightness, min/max color temp) that own the runtime curve values.
Slider changes take effect on the next curve tick — no integration
reload. State persists across HA restart via `RestoreNumber`.

Curve math now reads `min_brightness`, `max_brightness`,
`min_color_temp`, `max_color_temp` from the four runtime entities
via the entity registry, falling back to `entry.options` when an
entity is unavailable. The options flow seeds its four range fields
from the current entity state so the dialog matches reality.

Also fixes entity friendly names via HA's `has_entity_name`
composition: profile "Dining MVP" now reads as "Dining MVP",
"Dining MVP Brightness", "Dining MVP Color" — short enough for
HA's tightest cards. `unique_id`s are unchanged so existing
entity_ids stay stable.

Manifest bumped to 2.1.0-cdit.1 (minor, no breaking changes).
14 new tests in `tests/test_number_platform.py`; 108 passing
overall. OpenSpec change archived once 9.x live-HA verification
completes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-16 22:24:39 +02:00
Casey
c1a98a5c91 Archive cdit-config-redesign: 53/53 + repo description
Task 9.4: GitHub repo description and topics set via `gh repo edit`.
Description: "CDiT-opinionated fork of basnijholt/adaptive-lighting —
sectioned config, entity-driven sun timing (Sun2-friendly), synthetic
tanh curve. Not a drop-in replacement; star upstream too." Topics:
home-assistant, hacs, adaptive-lighting, circadian-lighting, cdit,
opinionated-fork, custom-component, home-automation.

Archive: change moved to openspec/changes/archive/2026-05-16-cdit-
config-redesign/. The 9 ADDED Requirements from the delta were synced
into openspec/specs/options-flow/spec.md as the canonical capability
spec — this is the first long-lived spec in the repo.

Result: cdit-config-redesign is 53/53 complete, archived, validated,
synced. The three remaining active changes (add-lovelace-card,
add-runtime-range-controls, house-mode-modes) sit in the roadmap.
2026-05-16 17:08:53 +02:00
Casey
a8b289240c 4.3 done: task complete + lessons from live HA deploy
Marked 4.3 (manual UI test of OptionsFlowWithReload reload-on-save +
entity-ID preservation) as complete after end-to-end verification on
the real HA install at homeassistant.onca-blenny.ts.net:

  - Created a fresh test profile via the new config flow
  - Confirmed 3 switches (master + adapt_color + adapt_brightness, no
    sleep) registered
  - Opened the options flow — 6 sections render in the order specified
    in spec R1
  - Toggled include_config_in_attributes off → on, saved
  - Entry stayed in `loaded` state, no restart prompt, all 3 entity
    IDs preserved across the reload
  - Field change landed on the master switch's attributes

Bonus: during the production run, also migrated all 6 existing AL
profiles from the upstream-shape entries (stuck in MIGRATION_ERROR
after our v1→v2 break) onto the new sectioned schema. 4/6 fully
migrated with their snapshotted config; 2/6 (dining_mvp,
ko_circadian_lights) saved with the lights that ARE currently
registered — `light.liv_lt_lights` and `light.hll_lt_main` are not
present in HA right now (offline or renamed).

The only task left in this change is 9.4 (gh repo description /
topics), which is a one-liner outside the code base.
2026-05-16 16:29:28 +02:00
Casey
03d2748cd4 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
Casey
bb3b8b81f4 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
Casey
501e3d639e 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
Casey
e4efcfa830 Add UX polish tasks to cdit-config-redesign
Three new checkboxes captured from the design-review thread:
- 1.6 better defaults (min_brightness 1→5, min_color_temp 2000→2200)
- 2.5 distinct mdi icons on each switch class
- 8.6 plain-language pass on every label, description, and error

Adds a 'polish' annotation tag for quality tasks that do not map
to a numbered requirement or design decision.
2026-05-16 13:16:43 +02:00
Casey
b9cfce5e02 Add lovelace-card change: proposal + design
Forward-looking change that adds a single-profile Lovelace card
visualizing AL state — current brightness, color temperature,
24-hour curve, the three switches, the four range sliders, and an
optional house-mode badge. Card is additive: it consumes existing
entities and never replaces them.

Stack: Lit + TypeScript + Vite, bundled alongside the integration
via HACS. Visual direction: editorial-instrument — calm typography,
restrained color, the card warms after sunset.

Specs and tasks deferred until prerequisites land (cdit-config-
redesign for the 3-switch model and sun-entity sensors,
add-runtime-range-controls for the four number entities the
sliders bind to).
2026-05-16 13:15:57 +02:00
Casey
80f99db16f Scaffold CDiT fork: planning artifacts and tooling
- openspec/ — three changes scoped:
  • cdit-config-redesign: full artifact set (proposal + design + specs +
    tasks), strict-validate green. Prunes 21 fields from the upstream
    options flow, switches sun timing to entity-driven sources, hardcodes
    a tanh curve, and breaks compat with upstream config entries.
  • add-runtime-range-controls: proposal stub for promoting the 4
    brightness/color-temp ranges to live number entities.
  • house-mode-modes: proposal stub for per-AL house-mode behavior matrix
    driving the runtime switches.
- .claude/ — opsx slash commands and openspec skill bundles for driving
  the artifact-driven workflow.
- CLAUDE.md — fork orientation, remote topology, dev commands, and the
  active change pointer.
2026-05-16 12:24:34 +02:00