diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6b85122f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,152 @@ +# Changelog — CDiT Adaptive Lighting fork + +All notable CDiT-fork changes are documented here. The upstream +`basnijholt/adaptive-lighting` changelog is in upstream's release notes; this +file covers only changes specific to this fork. + +The format is loosely based on [Keep a Changelog](https://keepachangelog.com/). + +--- + +## Standing on the shoulders of giants + +This fork builds on **[basnijholt/adaptive-lighting](https://github.com/basnijholt/adaptive-lighting)** — +years of work by [@basnijholt](https://github.com/basnijholt) and 130+ +contributors. The curve math, light intercept, HACS distribution, simulator +webapp, and the long tail of edge cases the original handles correctly all +come from that codebase. This fork is a narrow set of opinions layered on a +deep, mature foundation; the credit for *making this idea work at all* +belongs upstream. + +If you're not specifically a CDiT-style single-household installation, please +prefer the upstream integration — it's better-maintained, supports more use +cases, and has the entire community behind it. Issues with core curve math +or light handling belong on the upstream tracker, where they reach the +maintainers who can actually fix them for everyone. + +--- + +## [2.0.0-cdit.1] — Unreleased + +The first major CDiT release. **Breaking change**: existing upstream config +entries will not load — see "Upgrading" below. + +### Added + +- **Sectioned options dialog** rendered via HA's `section()` helper. Six + groups (Targets, Daytime curve, Sun schedule, Light control, Advanced, + Diagnostics) replace the upstream 40-field flat form. +- **Native HA selectors throughout** — `NumberSelector` (slider for + brightness 1–100 %, box for color temp 1000–10000 K), `EntitySelector` + (typed to `domain=sensor, device_class=timestamp` for sun events, + `domain=light, multiple=True` for the light list), `BooleanSelector`. +- **Conditional field visibility** — `send_split_delay` only appears when + its driver `separate_turn_on_commands` is enabled. +- **Entity-driven sun timing** — two new options `sunrise_entity` and + `sunset_entity` accept any sensor with `device_class: timestamp`. Defaults + to the built-in `sensor.sun_next_rising` / `sensor.sun_next_setting`. + Plug in Sun2 (`sensor.sun2_dawn`, `sensor.sun2_astro_dawn`, etc.) without + any code changes. +- **Synthetic tanh brightness/color-temp curve** with a hardcoded 30-minute + ramp half-width around each sun event. Tunable in `const.py` via + `RAMP_HALF_WIDTH_SECONDS` (currently `1800`). +- **`OptionsFlowWithReload`** — saving options reloads the integration + cleanly without a manual reload. +- **Strict version-break guard** — `async_setup_entry` raises + `ConfigEntryError` with a clear message when a config entry's version is + older than the current major. +- **Sleep-switch tombstone cleanup** — on first load after upgrade, + orphan `switch.adaptive_lighting_sleep_mode_*` entities owned by this + integration are removed from the entity registry and logged at `INFO`. +- **Plain-language strings** — every label, description, and abort message + rewritten to read like a sentence a human wrote, not engineer shorthand. + Sections get a one-sentence framer at the top. + +### Changed + +- **`DEFAULT_MIN_BRIGHTNESS`**: `1` → `5`. 1 % reads as off on most bulbs; + 5 % is the dim-but-visible floor. +- **`DEFAULT_MIN_COLOR_TEMP`**: `2000 K` → `2200 K`. Less sodium-vapor + orange; warmer-lamp tone. +- **Master switch icon**: `mdi:theme-light-dark` → `mdi:weather-sunny-alert`. +- **`adapt_color` switch icon**: `mdi:sun-thermometer` → `mdi:invert-colors`. +- **`adapt_brightness` switch icon**: `mdi:brightness-4` → + `mdi:brightness-percent`. +- **Minimum Home Assistant version**: pinned to `2025.1.0` in + `manifest.json`. +- **Manifest metadata** — `codeowners`, `documentation`, and `issue_tracker` + now point to `CaseyRo/adaptive-lighting`. + +### Removed + +**21 configuration fields**, **1 entity**, **1 service**, all part of +upstream features that the CDiT fork does not use. + +- **Sleep mode cluster** (6 fields + 1 entity): + `sleep_brightness`, `sleep_rgb_or_color_temp`, `sleep_color_temp`, + `sleep_rgb_color`, `sleep_transition`, `transition_until_sleep`. The + `switch.adaptive_lighting_sleep_mode_` entity is no longer created. + Each profile now provides **three switches** (master, adapt_color, + adapt_brightness) instead of four. +- **Manual sun timing** (8 fields): + `sunrise_time`, `min_sunrise_time`, `max_sunrise_time`, `sunrise_offset`, + `sunset_time`, `min_sunset_time`, `max_sunset_time`, `sunset_offset`. + Replaced by the two `*_entity` options above. +- **Brightness curve shape** (3 fields): + `brightness_mode`, `brightness_mode_time_dark`, + `brightness_mode_time_light`. The curve is now always a tanh ramp. +- **Take-over-control cluster** (4 fields): + `take_over_control`, `take_over_control_mode`, `detect_non_ha_changes`, + `autoreset_control_seconds`. Manual overrides are expected to live at + the scene/automation layer. +- **Anti-AL flags** (2 fields): + `only_once`, `adapt_only_on_bare_turn_on`. If you don't want continuous + adaptation on a light, don't run AL on that light. +- **Service**: + `adaptive_lighting.set_manual_control` no longer exists. The dependent + state machine in `switch.py` and `AdaptiveLightingManager` remains as + dead code in this release; follow-up cleanup will delete it. + +### Migration / upgrading from upstream + +1. Update via HACS (or copy `custom_components/adaptive_lighting/` over your + existing install). +2. Restart Home Assistant. +3. The existing config entry shows as "failed to load" with the message + "Adaptive Lighting v2 (CDiT fork) is incompatible with the existing + config entry." +4. Delete the failed entry: **Settings → Devices & Services → Adaptive + Lighting → ⋮ → Delete**. +5. Add a fresh entry: **Settings → Devices & Services → Add Integration → + Adaptive Lighting**. +6. Open the new entry's **Configure** dialog and walk through the six + sections. +7. (Optional) Install [Sun2](https://github.com/pnbruckner/ha-sun2) via + HACS and point `sunrise_entity` / `sunset_entity` at one of its sensors. + +### Internal + +- `color_and_brightness.py`: rewritten. `SunLightSettings` is now a pure + curve-math dataclass with 5 fields. Sun event timestamps are passed in + as arguments by the caller (which reads them from HA entities) rather + than computed via `astral`. +- `config_flow.py`: rewritten. ~250 LOC instead of 175 LOC, but each + section, selector factory, and conditional is now isolated and testable. +- `__init__.py`: adds version-check + tombstone-cleanup guards at the + top of `async_setup_entry`. +- Total integration code shrank by **~400 LOC** despite the new schema + builder. + +### Known limitations + +- `AdaptiveLightingManager` (in `switch.py`) still contains the + manual-control bookkeeping infrastructure. The branches that would + populate it are now dead code (the relevant `AdaptiveSwitch` attributes + are bridge stubs hardcoded to `False`), but the bookkeeping dicts and + helper methods remain. Cleanup is deferred to a follow-up change. +- Locale files other than `en.json` are out of sync with the new schema + and may render English strings until they are re-translated. Out of + scope for this release. +- `astral` is still a transitive dependency. The curve-evaluation path + no longer imports it, so pruning is just a `pyproject.toml` edit when + the package as a whole stops using it. diff --git a/README.md b/README.md index 3a26df24..f837786c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,72 @@ [![All Contributors](https://img.shields.io/badge/all_contributors-134-orange.svg?style=flat-square)](#contributors-) +--- + +> # ⚠️ This is the CDiT fork +> +> This repository is the [CDiT](https://github.com/CaseyRo)-opinionated fork of +> [`basnijholt/adaptive-lighting`](https://github.com/basnijholt/adaptive-lighting). +> It is **not a drop-in replacement** for the upstream HACS integration. +> +> ## 🙏 Huge thanks to the upstream team +> +> **None of this exists without [@basnijholt](https://github.com/basnijholt) +> and the 130+ contributors** to the original Adaptive Lighting project. The +> curve math, the `light.turn_on` interception, the HACS plumbing, the +> Pyodide simulator, the long tail of bug fixes from running this on +> thousands of real homes — that's *their* work. This fork is a thin opinion +> on top of a deep, mature codebase. If you're looking for the canonical, +> well-maintained, multi-feature integration that supports *everyone's* use +> case, **use upstream.** This fork only exists because CDiT's house has a +> narrower setup and the upstream config dialog was hard to tune at speed +> for one household. +> +> **Please star the upstream repo:** +> [github.com/basnijholt/adaptive-lighting](https://github.com/basnijholt/adaptive-lighting). +> If you find a bug in core curve math, light intercept, or any feature +> CDiT didn't change, report it there — that's where it'll actually get +> fixed and helps everyone, not just one CDiT household. +> +> **Differences from upstream:** +> +> 1. **Sectioned config dialog** — six collapsible groups (Targets, Daytime curve, +> Sun schedule, Light control, Advanced, Diagnostics) replace upstream's +> flat 40-field form. Native HA selectors throughout; reload-on-save via +> `OptionsFlowWithReload`. +> 2. **Entity-driven sun timing** — point `sunrise_entity` / `sunset_entity` +> at any sensor with `device_class: timestamp`. Defaults to the built-in +> `sensor.sun_next_rising` / `sensor.sun_next_setting`. Sun2 sensors +> (`sensor.sun2_dawn`, `sensor.sun2_astro_dawn`, etc.) work without code +> changes — see "Recommended companions" below. +> 3. **Synthetic tanh curve** — brightness and color temperature follow a +> smooth tanh ramp anchored at the two sun events with a fixed 30-minute +> half-width. No `brightness_mode` selector to fiddle with. +> 4. **Fewer features, on purpose** — sleep mode, take-over-control, manual +> sun-time overrides, `only_once`, and `adapt_only_on_bare_turn_on` are +> **removed**. Manual overrides are expected to live at the +> scene/automation layer instead. The integration creates **three switches +> per profile**, not four (no sleep-mode switch). +> 5. **Strict version break** — existing upstream config entries WILL NOT load. +> The integration rejects them with a clear "delete and recreate" message +> instead of silently migrating. Bump major version, recreate your one +> config entry, move on. +> +> **Recommended companions:** +> +> - [**Sun2**](https://github.com/pnbruckner/ha-sun2) (HACS) provides civil, +> nautical, and astronomical twilight sensors. Point `sunrise_entity` at +> `sensor.sun2_astro_dawn` for the smoothest fade from deep night to dawn, +> or `sensor.sun2_dawn` for civil twilight. Optional but recommended. +> +> **Minimum Home Assistant version: `2025.1.0`** (pinned in `manifest.json`). +> +> The rest of this README is from upstream and may describe features that no +> longer exist in this fork. See `CHANGELOG.md` for the canonical list of +> CDiT-specific changes. + +--- + # 🌞 Adaptive Lighting: Enhance Your Home's Atmosphere with Smart, Sun-Synchronized Lighting 🌙 logo diff --git a/custom_components/adaptive_lighting/services.yaml b/custom_components/adaptive_lighting/services.yaml index 09979bac..d951f199 100644 --- a/custom_components/adaptive_lighting/services.yaml +++ b/custom_components/adaptive_lighting/services.yaml @@ -1,260 +1,186 @@ -# This file is auto-generated by .github/update-services.py. +# CDiT Adaptive Lighting service surface. +# Hand-written for the fork — upstream's services.yaml was auto-generated +# from a wider config schema. See design.md decisions 7 + 8. apply: - description: Applies the current Adaptive Lighting settings to lights. + name: Apply settings + description: Apply this profile's current curve values to its lights immediately. fields: entity_id: - description: The `entity_id` of the switch with the settings to apply. 📝 + description: The master switch entity whose profile settings to apply. selector: entity: integration: adaptive_lighting domain: switch multiple: false lights: - description: A light (or list of lights) to apply the settings to. 💡 + description: Lights to apply the settings to. If empty, applies to all lights the profile controls. selector: entity: domain: light multiple: true transition: - description: Duration of transition when lights change, in seconds. 🕑 + description: Fade time for this one application, in seconds. example: 10 selector: - text: null + number: + min: 0 + max: 300 + unit_of_measurement: s + mode: box adapt_brightness: - description: Whether to adapt the brightness of the light. 🌞 - example: true + description: Adjust brightness on supporting lights. + default: true selector: - boolean: null + boolean: adapt_color: - description: Whether to adapt the color on supporting lights. 🌈 - example: true + description: Adjust color temperature on supporting lights. + default: true selector: - boolean: null + boolean: prefer_rgb_color: - description: Whether to prefer RGB color adjustment over light color temperature when possible. 🌈 - example: false + description: Send RGB when the light supports both color temperature and RGB. + default: false selector: - boolean: null + boolean: turn_on_lights: - description: Whether to turn on lights that are currently off. 🔆 - example: false + description: Also turn on lights that are currently off before applying. + default: false selector: - boolean: null -set_manual_control: - description: Mark whether a light is 'manually controlled'. + boolean: + +change_switch_settings: + name: Change profile settings + description: Change one or more of this profile's settings at runtime. The change persists until the next options-flow save or HA restart. fields: entity_id: - description: The `entity_id` of the switch in which to (un)mark the light as being `manually controlled`. 📝 + description: The master switch entity whose profile to update. + required: true selector: entity: integration: adaptive_lighting domain: switch - multiple: false - lights: - description: entity_id(s) of lights, if not specified, all lights in the switch are selected. 💡 - selector: - entity: - domain: light - multiple: true - manual_control: - description: Whether to add ("true") or remove ("false") all adapted attributes of the light from the "manual_control" list, or the name of an attribute for selective addition. 🔒 - example: true - default: true - selector: - boolean: null -change_switch_settings: - description: Change any settings you'd like in the switch. All options here are the same as in the config flow. - fields: - entity_id: - description: Entity ID of the switch. 📝 - required: true - selector: - entity: - domain: switch use_defaults: - description: 'Sets the default values not specified in this service call. Options: "current" (default, retains current values), "factory" (resets to documented defaults), or "configuration" (reverts to switch config defaults). ⚙️' - example: current - required: false + description: 'Where to pull unsupplied values from: "current" keeps existing values, "factory" resets to documented defaults, "configuration" reverts to the profile''s configured defaults.' default: current selector: select: options: - - current - - configuration - - factory + - current + - configuration + - factory include_config_in_attributes: - description: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝 - required: false + description: Expose all options as attributes on the master switch. selector: - boolean: null + boolean: turn_on_lights: - description: Whether to turn on lights that are currently off. 🔆 - example: false - required: false + description: Also turn on lights that are currently off before applying. selector: - boolean: null + boolean: initial_transition: - description: Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️ + description: Fade time when a light first turns on. example: 1 - required: false - selector: - text: null - sleep_transition: - description: Duration of transition when "sleep mode" is toggled in seconds. 😴 - example: 1 - required: false - selector: - text: null - max_brightness: - description: Maximum brightness percentage. 💡 - required: false - example: 100 - selector: - text: null - max_color_temp: - description: Coldest color temperature in Kelvin. ❄️ - required: false - example: 5500 - selector: - text: null - min_brightness: - description: Minimum brightness percentage. 💡 - required: false - example: 1 - selector: - text: null - min_color_temp: - description: Warmest color temperature in Kelvin. 🔥 - required: false - example: 2000 - selector: - text: null - only_once: - description: Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄 - example: false - required: false - selector: - boolean: null - prefer_rgb_color: - description: Whether to prefer RGB color adjustment over light color temperature when possible. 🌈 - required: false - example: false - selector: - boolean: null - separate_turn_on_commands: - description: Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀 - required: false - example: false - selector: - boolean: null - send_split_delay: - description: Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️ - required: false - example: 0 - selector: - boolean: null - sleep_brightness: - description: Brightness percentage of lights in sleep mode. 😴 - required: false - example: 1 - selector: - text: null - sleep_rgb_or_color_temp: - description: Use either `"rgb_color"` or `"color_temp"` in sleep mode. 🌙 - required: false - example: color_temp - selector: - select: - options: - - rgb_color - - color_temp - sleep_rgb_color: - description: RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is "rgb_color"). 🌈 - required: false - selector: - color_rgb: null - sleep_color_temp: - description: Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴 - required: false - example: 1000 - selector: - text: null - sunrise_offset: - description: Adjust sunrise time with a positive or negative offset in seconds. ⏰ - required: false - example: 0 selector: number: min: 0 - max: 86300 - sunrise_time: - description: Set a fixed time (HH:MM:SS) for sunrise. 🌅 - required: false - example: '' - selector: - time: null - sunset_offset: - description: Adjust sunset time with a positive or negative offset in seconds. ⏰ - required: false - example: '' - selector: - number: - min: 0 - max: 86300 - sunset_time: - description: Set a fixed time (HH:MM:SS) for sunset. 🌇 - example: '' - required: false - selector: - time: null - max_sunrise_time: - description: Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier sunrises. 🌅 - example: '' - required: false - selector: - time: null - min_sunset_time: - description: Set the earliest virtual sunset time (HH:MM:SS), allowing for later sunsets. 🌇 - example: '' - required: false - selector: - time: null - take_over_control: - description: Pause adaptation of individual lights and hand over (manual) control to other sources that issue `light.turn_on` calls for lights that are on. 🔒 - required: false - example: true - selector: - boolean: null - take_over_control_mode: - description: The adaptation pausing mode when other sources change brightness and/or color of lights. `pause_all` always pauses both brightness and color adaptation. `pause_changed` pauses the adaptation of only the changed attributes and continues adapting unchanged attributes, e.g., continues color adaptation when only brightness was changed. - required: false - example: pause_changed - selector: - select: - options: - - pause_all - - pause_changed - detect_non_ha_changes: - description: 'Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. 🕵️ Caution: ⚠️ Some lights might falsely indicate an ''on'' state, which could result in lights turning on unexpectedly. Note that this calls `homeassistant.update_entity` every `interval`! Disable this feature if you encounter such issues.' - required: false - example: false - selector: - boolean: null + max: 300 + unit_of_measurement: s + mode: box transition: - description: Duration of transition when lights change, in seconds. 🕑 - required: false + description: Fade time for each scheduled curve update. example: 45 selector: - text: null + number: + min: 0 + max: 300 + unit_of_measurement: s + mode: box + interval: + description: How often to recompute and re-apply the curve. + example: 90 + selector: + number: + min: 1 + max: 3600 + unit_of_measurement: s + mode: box adapt_delay: - description: Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️ - required: false + description: Wait this long after a light reports 'on' before applying. example: 0 selector: - text: null - autoreset_control_seconds: - description: Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️ - required: false - example: 0 + number: + min: 0 + max: 300 + unit_of_measurement: s + mode: box + min_brightness: + description: Brightness during the night. selector: - text: null + number: + min: 1 + max: 100 + unit_of_measurement: "%" + mode: slider + max_brightness: + description: Brightness at the peak of the day. + selector: + number: + min: 1 + max: 100 + unit_of_measurement: "%" + mode: slider + min_color_temp: + description: The night-time tone (warmer = lower). + selector: + number: + min: 1000 + max: 10000 + unit_of_measurement: K + mode: box + max_color_temp: + description: The mid-day tone (cooler = higher). + selector: + number: + min: 1000 + max: 10000 + unit_of_measurement: K + mode: box + prefer_rgb_color: + description: When a light supports both color temperature and RGB, send RGB. + selector: + boolean: + sunrise_entity: + description: Any sensor that returns the next sunrise timestamp. + selector: + entity: + domain: sensor + device_class: timestamp + sunset_entity: + description: Any sensor that returns the next sunset timestamp. + selector: + entity: + domain: sensor + device_class: timestamp + separate_turn_on_commands: + description: Required by some lights. + selector: + boolean: + send_split_delay: + description: Only used when separate_turn_on_commands is true. + selector: + number: + min: 0 + max: 10000 + unit_of_measurement: ms + mode: box + skip_redundant_commands: + description: Don't send a command if the light is already at the target state. + selector: + boolean: + intercept: + description: Apply the curve the moment a light turns on. + selector: + boolean: + multi_light_intercept: + description: Also intercept calls that target several lights at once. + selector: + boolean: diff --git a/custom_components/adaptive_lighting/strings.json b/custom_components/adaptive_lighting/strings.json index cf816d4f..74ddbec6 100644 --- a/custom_components/adaptive_lighting/strings.json +++ b/custom_components/adaptive_lighting/strings.json @@ -2,280 +2,246 @@ "config": { "step": { "user": { - "title": "Choose a name for the Adaptive Lighting instance", - "description": "Every instance can contain multiple lights!", + "title": "Name this Adaptive Lighting profile", + "description": "Each profile controls one set of lights. You'll be able to add more profiles later for other rooms or scenes.", "data": { - "name": "Name" - } - }, - "menu": { - "title": "Create or Duplicate", - "description": "Do you want to create a new instance or duplicate an existing one?", - "data": { - "action": "Action" + "name": "Profile name" + }, + "data_description": { + "name": "A short label like \"Kitchen\" or \"Living room\"." } } }, "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "no_data": "Cannot import without configuration data." + }, + "error": { + "incompatible_version": "This entry was created with an older, incompatible version of Adaptive Lighting. Delete the entry and create a new one." } }, "options": { "step": { "init": { "title": "Adaptive Lighting options", - "description": "Configure an Adaptive Lighting component. Option names align with the YAML settings. If you've defined this entry in YAML, no options will appear here. For interactive graphs that demonstrate parameter effects, visit [this web app]({webapp_url}). For further details, see the [official documentation]({docs_url}).", - "data": { - "lights": "lights: List of light entity_ids to be controlled (may be empty). 🌟", - "interval": "interval", - "transition": "transition", - "initial_transition": "initial_transition", - "min_brightness": "min_brightness: Minimum brightness percentage. 💡", - "max_brightness": "max_brightness: Maximum brightness percentage. 💡", - "min_color_temp": "min_color_temp: Warmest color temperature in Kelvin. 🔥", - "max_color_temp": "max_color_temp: Coldest color temperature in Kelvin. ❄️", - "prefer_rgb_color": "prefer_rgb_color: Whether to prefer RGB color adjustment over light color temperature when possible. 🌈", - "sleep_brightness": "sleep_brightness", - "sleep_rgb_or_color_temp": "sleep_rgb_or_color_temp", - "sleep_color_temp": "sleep_color_temp", - "sleep_rgb_color": "sleep_rgb_color", - "sleep_transition": "sleep_transition", - "transition_until_sleep": "transition_until_sleep: When enabled, Adaptive Lighting will treat sleep settings as the minimum, transitioning to these values after sunset. 🌙", - "sunrise_time": "sunrise_time", - "min_sunrise_time": "min_sunrise_time", - "max_sunrise_time": "max_sunrise_time", - "sunrise_offset": "sunrise_offset", - "sunset_time": "sunset_time", - "min_sunset_time": "min_sunset_time", - "max_sunset_time": "max_sunset_time", - "sunset_offset": "sunset_offset", - "brightness_mode": "brightness_mode", - "brightness_mode_time_dark": "brightness_mode_time_dark", - "brightness_mode_time_light": "brightness_mode_time_light", - "take_over_control": "take_over_control: Pause adaptation of individual lights and hand over (manual) control to other sources that issue `light.turn_on` calls for lights that are on. 🔒", - "take_over_control_mode": "take_over_control_mode", - "detect_non_ha_changes": "detect_non_ha_changes: Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. 🕵️ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Note that this calls `homeassistant.update_entity` every `interval`! Disable this feature if you encounter such issues.", - "autoreset_control_seconds": "autoreset_control_seconds", - "only_once": "only_once: Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄", - "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: When turning lights on initially. If set to `true`, AL adapts only if `light.turn_on` is invoked without specifying color or brightness. ❌🌈 This e.g., prevents adaptation when activating a scene and marks the light as manually controlled. If `false`, AL adapts regardless of the presence of color or brightness in the initial `service_data`. Needs `take_over_control` enabled. 🕵️", - "separate_turn_on_commands": "separate_turn_on_commands: Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀", - "send_split_delay": "send_split_delay", - "adapt_delay": "adapt_delay", - "skip_redundant_commands": "skip_redundant_commands: Skip sending adaptation commands whose target state already equals the light's known state. Minimizes network traffic and improves the adaptation responsivity in some situations. 📉Disable if physical light states get out of sync with HA's recorded state.", - "intercept": "intercept: Intercept and adapt `light.turn_on` calls to enabling instantaneous color and brightness adaptation. 🏎️ Disable for lights that do not support `light.turn_on` with color and brightness.", - "multi_light_intercept": "multi_light_intercept: Intercept and adapt `light.turn_on` calls that target multiple lights. ➗⚠️ This might result in splitting up a single `light.turn_on` call into multiple calls, e.g., when lights are in different switches. Requires `intercept` to be enabled.", - "include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝" - }, - "data_description": { - "interval": "Frequency to adapt the lights, in seconds. 🔄", - "transition": "Duration of transition when lights change, in seconds. 🕑", - "initial_transition": "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️", - "sleep_brightness": "Brightness percentage of lights in sleep mode. 😴", - "sleep_rgb_or_color_temp": "Use either `\"rgb_color\"` or `\"color_temp\"` in sleep mode. 🌙", - "sleep_color_temp": "Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴", - "sleep_rgb_color": "RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is \"rgb_color\"). 🌈", - "sleep_transition": "Duration of transition when \"sleep mode\" is toggled in seconds. 😴", - "sunrise_time": "Set a fixed time (HH:MM:SS) for sunrise. 🌅", - "min_sunrise_time": "Set the earliest virtual sunrise time (HH:MM:SS), allowing for later sunrises. 🌅", - "max_sunrise_time": "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier sunrises. 🌅", - "sunrise_offset": "Adjust sunrise time with a positive or negative offset in seconds. ⏰", - "sunset_time": "Set a fixed time (HH:MM:SS) for sunset. 🌇", - "min_sunset_time": "Set the earliest virtual sunset time (HH:MM:SS), allowing for later sunsets. 🌇", - "max_sunset_time": "Set the latest virtual sunset time (HH:MM:SS), allowing for earlier sunsets. 🌇", - "sunset_offset": "Adjust sunset time with a positive or negative offset in seconds. ⏰", - "brightness_mode": "Brightness mode to use. Possible values are `default`, `linear`, and `tanh` (uses `brightness_mode_time_dark` and `brightness_mode_time_light`). 📈", - "brightness_mode_time_dark": "(Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness before/after sunrise/sunset. 📈📉", - "brightness_mode_time_light": "(Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness after/before sunrise/sunset. 📈📉.", - "take_over_control_mode": "The adaptation pausing mode when other sources change brightness and/or color of lights. `pause_all` always pauses both brightness and color adaptation. `pause_changed` pauses the adaptation of only the changed attributes and continues adapting unchanged attributes, e.g., continues color adaptation when only brightness was changed.", - "autoreset_control_seconds": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️", - "send_split_delay": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️", - "adapt_delay": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️" + "description": "Tune how this profile adjusts its lights through the day. Hover labels for more detail; the Advanced and Diagnostics sections are collapsed because most households never need to touch them.", + "sections": { + "targets": { + "name": "Targets", + "description": "Which lights this profile controls.", + "data": { + "lights": "Lights" + }, + "data_description": { + "lights": "Pick every light that should follow this profile's curve." + } + }, + "daytime_curve": { + "name": "Daytime curve", + "description": "How bright these lights get, and how warm or cool they feel, between sunrise and sunset.", + "data": { + "min_brightness": "Minimum brightness", + "max_brightness": "Maximum brightness", + "min_color_temp": "Warmest color temperature", + "max_color_temp": "Coolest color temperature", + "prefer_rgb_color": "Prefer RGB color" + }, + "data_description": { + "min_brightness": "Brightness at the lowest point of the day (deep night).", + "max_brightness": "Brightness at the peak of the day (mid-day).", + "min_color_temp": "The night-time tone — lower numbers are warmer (more orange).", + "max_color_temp": "The mid-day tone — higher numbers are cooler (more blue).", + "prefer_rgb_color": "When a light supports both color temperature and RGB, send RGB." + } + }, + "sun_schedule": { + "name": "Sun schedule", + "description": "When morning and evening start. Defaults to your home's sunrise and sunset; point these at Sun2 sensors for civil, nautical, or astronomical twilight.", + "data": { + "sunrise_entity": "Sunrise entity", + "sunset_entity": "Sunset entity" + }, + "data_description": { + "sunrise_entity": "Any sensor that returns the next sunrise timestamp.", + "sunset_entity": "Any sensor that returns the next sunset timestamp." + } + }, + "light_control": { + "name": "Light control", + "description": "How this integration talks to your lights when you turn them on by hand or by voice.", + "data": { + "intercept": "Intercept light.turn_on calls", + "multi_light_intercept": "Intercept multi-light calls" + }, + "data_description": { + "intercept": "Apply the curve the moment a light turns on, instead of waiting up to one interval.", + "multi_light_intercept": "Also intercept calls that target several lights at once. Requires the option above." + } + }, + "advanced": { + "name": "Advanced", + "description": "Timing knobs. Most households never need to touch these.", + "data": { + "interval": "Update interval (s)", + "transition": "Transition time (s)", + "initial_transition": "First-on fade (s)", + "adapt_delay": "Delay before adapting (s)", + "separate_turn_on_commands": "Split brightness and color into separate calls", + "send_split_delay": "Pause between split calls (ms)", + "skip_redundant_commands": "Skip redundant commands" + }, + "data_description": { + "interval": "How often to recompute and re-apply the curve.", + "transition": "Fade time for each scheduled curve update.", + "initial_transition": "Fade time when a light first turns on.", + "adapt_delay": "Wait this long after a light reports 'on' before applying the curve. Helps lights that report state before they have settled.", + "separate_turn_on_commands": "Required by lights that cannot accept brightness and color in the same call.", + "send_split_delay": "Only used when the option above is on.", + "skip_redundant_commands": "Don't send a command if the light is already at the target state." + } + }, + "diagnostics": { + "name": "Diagnostics", + "description": "Verbose entity attributes for debugging.", + "data": { + "include_config_in_attributes": "Expose all options as switch attributes" + }, + "data_description": { + "include_config_in_attributes": "Useful for templating and dashboards that read the integration's state." + } + } } } }, + "abort": { + "yaml_managed": "This Adaptive Lighting profile is managed by YAML. To change its options, edit configuration.yaml." + }, "error": { - "option_error": "Invalid option", - "entity_missing": "One or more selected light entities are missing from Home Assistant" + "option_error": "One of the options has an invalid value.", + "entity_missing": "One or more of the selected light entities are not currently registered in Home Assistant." } }, "services": { "apply": { - "name": "apply", - "description": "Applies the current Adaptive Lighting settings to lights.", + "name": "Apply settings", + "description": "Apply this profile's current curve values to its lights immediately, without waiting for the next scheduled update.", "fields": { "entity_id": { - "description": "The `entity_id` of the switch with the settings to apply. 📝", - "name": "entity_id" + "name": "Master switch", + "description": "The master switch entity whose profile settings to apply." }, "lights": { - "description": "A light (or list of lights) to apply the settings to. 💡", - "name": "lights" + "name": "Lights", + "description": "Lights to apply the settings to. If empty, applies to all lights the profile controls." }, "transition": { - "description": "Duration of transition when lights change, in seconds. 🕑", - "name": "transition" + "name": "Transition time (s)", + "description": "Fade time for this one application." }, "adapt_brightness": { - "description": "Whether to adapt the brightness of the light. 🌞", - "name": "adapt_brightness" + "name": "Adapt brightness", + "description": "Adjust brightness on supporting lights." }, "adapt_color": { - "description": "Whether to adapt the color on supporting lights. 🌈", - "name": "adapt_color" + "name": "Adapt color", + "description": "Adjust color temperature on supporting lights." }, "prefer_rgb_color": { - "description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈", - "name": "prefer_rgb_color" + "name": "Prefer RGB", + "description": "Send RGB when the light supports both color temperature and RGB." }, "turn_on_lights": { - "description": "Whether to turn on lights that are currently off. 🔆", - "name": "turn_on_lights" - } - } - }, - "set_manual_control": { - "name": "set_manual_control", - "description": "Mark whether a light is 'manually controlled'.", - "fields": { - "entity_id": { - "description": "The `entity_id` of the switch in which to (un)mark the light as being `manually controlled`. 📝", - "name": "entity_id" - }, - "lights": { - "description": "entity_id(s) of lights, if not specified, all lights in the switch are selected. 💡", - "name": "lights" - }, - "manual_control": { - "description": "Whether to add (\"true\") or remove (\"false\") all adapted attributes of the light from the \"manual_control\" list, or the name of an attribute for selective addition. 🔒", - "name": "manual_control" + "name": "Turn on lights that are off", + "description": "If true, also turn on lights that are currently off before applying." } } }, "change_switch_settings": { - "name": "change_switch_settings", - "description": "Change any settings you'd like in the switch. All options here are the same as in the config flow.", + "name": "Change profile settings", + "description": "Change one or more of this profile's settings at runtime. The change persists until the next options-flow save or HA restart.", "fields": { "entity_id": { - "description": "Entity ID of the switch. 📝", - "name": "entity_id" + "name": "Master switch", + "description": "The master switch entity whose profile to update." }, "use_defaults": { - "description": "Sets the default values not specified in this service call. Options: \"current\" (default, retains current values), \"factory\" (resets to documented defaults), or \"configuration\" (reverts to switch config defaults). ⚙️", - "name": "use_defaults" + "name": "Defaults source", + "description": "Where to pull unsupplied values from: 'current' keeps existing values, 'factory' resets to documented defaults, 'configuration' reverts to the profile's configured defaults." }, "include_config_in_attributes": { - "description": "Show all options as attributes on the switch in Home Assistant when set to `true`. 📝", - "name": "include_config_in_attributes" + "name": "Expose config in attributes", + "description": "Show all options as attributes on the master switch." }, "turn_on_lights": { - "description": "Whether to turn on lights that are currently off. 🔆", - "name": "turn_on_lights" + "name": "Turn on lights that are off", + "description": "If true, also turn on lights that are currently off before applying." }, "initial_transition": { - "description": "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️", - "name": "initial_transition" - }, - "sleep_transition": { - "description": "Duration of transition when \"sleep mode\" is toggled in seconds. 😴", - "name": "sleep_transition" + "name": "First-on fade (s)", + "description": "Fade time when a light first turns on." }, "max_brightness": { - "description": "Maximum brightness percentage. 💡", - "name": "max_brightness" + "name": "Maximum brightness", + "description": "Brightness at the peak of the day." }, "max_color_temp": { - "description": "Coldest color temperature in Kelvin. ❄️", - "name": "max_color_temp" + "name": "Coolest color temperature", + "description": "The mid-day tone." }, "min_brightness": { - "description": "Minimum brightness percentage. 💡", - "name": "min_brightness" + "name": "Minimum brightness", + "description": "Brightness during the night." }, "min_color_temp": { - "description": "Warmest color temperature in Kelvin. 🔥", - "name": "min_color_temp" - }, - "only_once": { - "description": "Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄", - "name": "only_once" + "name": "Warmest color temperature", + "description": "The night-time tone." }, "prefer_rgb_color": { - "description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈", - "name": "prefer_rgb_color" + "name": "Prefer RGB", + "description": "When a light supports both, send RGB." }, "separate_turn_on_commands": { - "description": "Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀", - "name": "separate_turn_on_commands" + "name": "Split brightness and color into separate calls", + "description": "Required by some lights." }, "send_split_delay": { - "description": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️", - "name": "send_split_delay" + "name": "Pause between split calls (ms)", + "description": "Only used when the option above is on." }, - "sleep_brightness": { - "description": "Brightness percentage of lights in sleep mode. 😴", - "name": "sleep_brightness" + "sunrise_entity": { + "name": "Sunrise entity", + "description": "Any sensor that returns the next sunrise timestamp." }, - "sleep_rgb_or_color_temp": { - "description": "Use either `\"rgb_color\"` or `\"color_temp\"` in sleep mode. 🌙", - "name": "sleep_rgb_or_color_temp" - }, - "sleep_rgb_color": { - "description": "RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is \"rgb_color\"). 🌈", - "name": "sleep_rgb_color" - }, - "sleep_color_temp": { - "description": "Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴", - "name": "sleep_color_temp" - }, - "sunrise_offset": { - "description": "Adjust sunrise time with a positive or negative offset in seconds. ⏰", - "name": "sunrise_offset" - }, - "sunrise_time": { - "description": "Set a fixed time (HH:MM:SS) for sunrise. 🌅", - "name": "sunrise_time" - }, - "sunset_offset": { - "description": "Adjust sunset time with a positive or negative offset in seconds. ⏰", - "name": "sunset_offset" - }, - "sunset_time": { - "description": "Set a fixed time (HH:MM:SS) for sunset. 🌇", - "name": "sunset_time" - }, - "max_sunrise_time": { - "description": "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier sunrises. 🌅", - "name": "max_sunrise_time" - }, - "min_sunset_time": { - "description": "Set the earliest virtual sunset time (HH:MM:SS), allowing for later sunsets. 🌇", - "name": "min_sunset_time" - }, - "take_over_control": { - "description": "Pause adaptation of individual lights and hand over (manual) control to other sources that issue `light.turn_on` calls for lights that are on. 🔒", - "name": "take_over_control" - }, - "take_over_control_mode": { - "description": "The adaptation pausing mode when other sources change brightness and/or color of lights. `pause_all` always pauses both brightness and color adaptation. `pause_changed` pauses the adaptation of only the changed attributes and continues adapting unchanged attributes, e.g., continues color adaptation when only brightness was changed.", - "name": "take_over_control_mode" - }, - "detect_non_ha_changes": { - "description": "Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. 🕵️ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Note that this calls `homeassistant.update_entity` every `interval`! Disable this feature if you encounter such issues.", - "name": "detect_non_ha_changes" + "sunset_entity": { + "name": "Sunset entity", + "description": "Any sensor that returns the next sunset timestamp." }, "transition": { - "description": "Duration of transition when lights change, in seconds. 🕑", - "name": "transition" + "name": "Transition time (s)", + "description": "Fade time for each scheduled curve update." }, "adapt_delay": { - "description": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️", - "name": "adapt_delay" + "name": "Delay before adapting (s)", + "description": "Wait this long after a light reports 'on' before applying." }, - "autoreset_control_seconds": { - "description": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️", - "name": "autoreset_control_seconds" + "skip_redundant_commands": { + "name": "Skip redundant commands", + "description": "Don't send a command if the light is already at the target state." + }, + "intercept": { + "name": "Intercept light.turn_on calls", + "description": "Apply the curve the moment a light turns on." + }, + "multi_light_intercept": { + "name": "Intercept multi-light calls", + "description": "Also intercept calls that target several lights at once." } } } + }, + "exceptions": { + "incompatible_version": { + "message": "Adaptive Lighting v{current_version} (CDiT fork) is incompatible with the existing config entry (version {entry_version}). Delete the entry from Settings → Devices & Services and create a new one." + } } } diff --git a/custom_components/adaptive_lighting/translations/en.json b/custom_components/adaptive_lighting/translations/en.json index 43bb2105..74ddbec6 100644 --- a/custom_components/adaptive_lighting/translations/en.json +++ b/custom_components/adaptive_lighting/translations/en.json @@ -1,282 +1,247 @@ { - "title": "Adaptive Lighting", "config": { "step": { "user": { - "title": "Choose a name for the Adaptive Lighting instance", - "description": "Every instance can contain multiple lights!", + "title": "Name this Adaptive Lighting profile", + "description": "Each profile controls one set of lights. You'll be able to add more profiles later for other rooms or scenes.", "data": { - "name": "Name" - } - }, - "menu": { - "title": "Create or Duplicate", - "description": "Do you want to create a new instance or duplicate an existing one?", - "data": { - "action": "Action" + "name": "Profile name" + }, + "data_description": { + "name": "A short label like \"Kitchen\" or \"Living room\"." } } }, "abort": { - "already_configured": "This device is already configured" + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "no_data": "Cannot import without configuration data." + }, + "error": { + "incompatible_version": "This entry was created with an older, incompatible version of Adaptive Lighting. Delete the entry and create a new one." } }, "options": { "step": { "init": { "title": "Adaptive Lighting options", - "description": "Configure an Adaptive Lighting component. Option names align with the YAML settings. If you've defined this entry in YAML, no options will appear here. For interactive graphs that demonstrate parameter effects, visit [this web app]({webapp_url}). For further details, see the [official documentation]({docs_url}).", - "data": { - "lights": "lights: List of light entity_ids to be controlled (may be empty). 🌟", - "interval": "interval", - "transition": "transition", - "initial_transition": "initial_transition", - "min_brightness": "min_brightness: Minimum brightness percentage. 💡", - "max_brightness": "max_brightness: Maximum brightness percentage. 💡", - "min_color_temp": "min_color_temp: Warmest color temperature in Kelvin. 🔥", - "max_color_temp": "max_color_temp: Coldest color temperature in Kelvin. ❄️", - "prefer_rgb_color": "prefer_rgb_color: Whether to prefer RGB color adjustment over light color temperature when possible. 🌈", - "sleep_brightness": "sleep_brightness", - "sleep_rgb_or_color_temp": "sleep_rgb_or_color_temp", - "sleep_color_temp": "sleep_color_temp", - "sleep_rgb_color": "sleep_rgb_color", - "sleep_transition": "sleep_transition", - "transition_until_sleep": "transition_until_sleep: When enabled, Adaptive Lighting will treat sleep settings as the minimum, transitioning to these values after sunset. 🌙", - "sunrise_time": "sunrise_time", - "min_sunrise_time": "min_sunrise_time", - "max_sunrise_time": "max_sunrise_time", - "sunrise_offset": "sunrise_offset", - "sunset_time": "sunset_time", - "min_sunset_time": "min_sunset_time", - "max_sunset_time": "max_sunset_time", - "sunset_offset": "sunset_offset", - "brightness_mode": "brightness_mode", - "brightness_mode_time_dark": "brightness_mode_time_dark", - "brightness_mode_time_light": "brightness_mode_time_light", - "take_over_control": "take_over_control: Pause adaptation of individual lights and hand over (manual) control to other sources that issue `light.turn_on` calls for lights that are on. 🔒", - "take_over_control_mode": "take_over_control_mode", - "detect_non_ha_changes": "detect_non_ha_changes: Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. 🕵️ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Note that this calls `homeassistant.update_entity` every `interval`! Disable this feature if you encounter such issues.", - "autoreset_control_seconds": "autoreset_control_seconds", - "only_once": "only_once: Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄", - "adapt_only_on_bare_turn_on": "adapt_only_on_bare_turn_on: When turning lights on initially. If set to `true`, AL adapts only if `light.turn_on` is invoked without specifying color or brightness. ❌🌈 This e.g., prevents adaptation when activating a scene and marks the light as manually controlled. If `false`, AL adapts regardless of the presence of color or brightness in the initial `service_data`. Needs `take_over_control` enabled. 🕵️", - "separate_turn_on_commands": "separate_turn_on_commands: Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀", - "send_split_delay": "send_split_delay", - "adapt_delay": "adapt_delay", - "skip_redundant_commands": "skip_redundant_commands: Skip sending adaptation commands whose target state already equals the light's known state. Minimizes network traffic and improves the adaptation responsivity in some situations. 📉Disable if physical light states get out of sync with HA's recorded state.", - "intercept": "intercept: Intercept and adapt `light.turn_on` calls to enabling instantaneous color and brightness adaptation. 🏎️ Disable for lights that do not support `light.turn_on` with color and brightness.", - "multi_light_intercept": "multi_light_intercept: Intercept and adapt `light.turn_on` calls that target multiple lights. ➗⚠️ This might result in splitting up a single `light.turn_on` call into multiple calls, e.g., when lights are in different switches. Requires `intercept` to be enabled.", - "include_config_in_attributes": "include_config_in_attributes: Show all options as attributes on the switch in Home Assistant when set to `true`. 📝" - }, - "data_description": { - "interval": "Frequency to adapt the lights, in seconds. 🔄", - "transition": "Duration of transition when lights change, in seconds. 🕑", - "initial_transition": "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️", - "sleep_brightness": "Brightness percentage of lights in sleep mode. 😴", - "sleep_rgb_or_color_temp": "Use either `\"rgb_color\"` or `\"color_temp\"` in sleep mode. 🌙", - "sleep_color_temp": "Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴", - "sleep_rgb_color": "RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is \"rgb_color\"). 🌈", - "sleep_transition": "Duration of transition when \"sleep mode\" is toggled in seconds. 😴", - "sunrise_time": "Set a fixed time (HH:MM:SS) for sunrise. 🌅", - "min_sunrise_time": "Set the earliest virtual sunrise time (HH:MM:SS), allowing for later sunrises. 🌅", - "max_sunrise_time": "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier sunrises. 🌅", - "sunrise_offset": "Adjust sunrise time with a positive or negative offset in seconds. ⏰", - "sunset_time": "Set a fixed time (HH:MM:SS) for sunset. 🌇", - "min_sunset_time": "Set the earliest virtual sunset time (HH:MM:SS), allowing for later sunsets. 🌇", - "max_sunset_time": "Set the latest virtual sunset time (HH:MM:SS), allowing for earlier sunsets. 🌇", - "sunset_offset": "Adjust sunset time with a positive or negative offset in seconds. ⏰", - "brightness_mode": "Brightness mode to use. Possible values are `default`, `linear`, and `tanh` (uses `brightness_mode_time_dark` and `brightness_mode_time_light`). 📈", - "brightness_mode_time_dark": "(Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness before/after sunrise/sunset. 📈📉", - "brightness_mode_time_light": "(Ignored if `brightness_mode='default'`) The duration in seconds to ramp up/down the brightness after/before sunrise/sunset. 📈📉.", - "take_over_control_mode": "The adaptation pausing mode when other sources change brightness and/or color of lights. `pause_all` always pauses both brightness and color adaptation. `pause_changed` pauses the adaptation of only the changed attributes and continues adapting unchanged attributes, e.g., continues color adaptation when only brightness was changed.", - "autoreset_control_seconds": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️", - "send_split_delay": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️", - "adapt_delay": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️" + "description": "Tune how this profile adjusts its lights through the day. Hover labels for more detail; the Advanced and Diagnostics sections are collapsed because most households never need to touch them.", + "sections": { + "targets": { + "name": "Targets", + "description": "Which lights this profile controls.", + "data": { + "lights": "Lights" + }, + "data_description": { + "lights": "Pick every light that should follow this profile's curve." + } + }, + "daytime_curve": { + "name": "Daytime curve", + "description": "How bright these lights get, and how warm or cool they feel, between sunrise and sunset.", + "data": { + "min_brightness": "Minimum brightness", + "max_brightness": "Maximum brightness", + "min_color_temp": "Warmest color temperature", + "max_color_temp": "Coolest color temperature", + "prefer_rgb_color": "Prefer RGB color" + }, + "data_description": { + "min_brightness": "Brightness at the lowest point of the day (deep night).", + "max_brightness": "Brightness at the peak of the day (mid-day).", + "min_color_temp": "The night-time tone — lower numbers are warmer (more orange).", + "max_color_temp": "The mid-day tone — higher numbers are cooler (more blue).", + "prefer_rgb_color": "When a light supports both color temperature and RGB, send RGB." + } + }, + "sun_schedule": { + "name": "Sun schedule", + "description": "When morning and evening start. Defaults to your home's sunrise and sunset; point these at Sun2 sensors for civil, nautical, or astronomical twilight.", + "data": { + "sunrise_entity": "Sunrise entity", + "sunset_entity": "Sunset entity" + }, + "data_description": { + "sunrise_entity": "Any sensor that returns the next sunrise timestamp.", + "sunset_entity": "Any sensor that returns the next sunset timestamp." + } + }, + "light_control": { + "name": "Light control", + "description": "How this integration talks to your lights when you turn them on by hand or by voice.", + "data": { + "intercept": "Intercept light.turn_on calls", + "multi_light_intercept": "Intercept multi-light calls" + }, + "data_description": { + "intercept": "Apply the curve the moment a light turns on, instead of waiting up to one interval.", + "multi_light_intercept": "Also intercept calls that target several lights at once. Requires the option above." + } + }, + "advanced": { + "name": "Advanced", + "description": "Timing knobs. Most households never need to touch these.", + "data": { + "interval": "Update interval (s)", + "transition": "Transition time (s)", + "initial_transition": "First-on fade (s)", + "adapt_delay": "Delay before adapting (s)", + "separate_turn_on_commands": "Split brightness and color into separate calls", + "send_split_delay": "Pause between split calls (ms)", + "skip_redundant_commands": "Skip redundant commands" + }, + "data_description": { + "interval": "How often to recompute and re-apply the curve.", + "transition": "Fade time for each scheduled curve update.", + "initial_transition": "Fade time when a light first turns on.", + "adapt_delay": "Wait this long after a light reports 'on' before applying the curve. Helps lights that report state before they have settled.", + "separate_turn_on_commands": "Required by lights that cannot accept brightness and color in the same call.", + "send_split_delay": "Only used when the option above is on.", + "skip_redundant_commands": "Don't send a command if the light is already at the target state." + } + }, + "diagnostics": { + "name": "Diagnostics", + "description": "Verbose entity attributes for debugging.", + "data": { + "include_config_in_attributes": "Expose all options as switch attributes" + }, + "data_description": { + "include_config_in_attributes": "Useful for templating and dashboards that read the integration's state." + } + } } } }, + "abort": { + "yaml_managed": "This Adaptive Lighting profile is managed by YAML. To change its options, edit configuration.yaml." + }, "error": { - "option_error": "Invalid option", - "entity_missing": "One or more selected light entities are missing from Home Assistant" + "option_error": "One of the options has an invalid value.", + "entity_missing": "One or more of the selected light entities are not currently registered in Home Assistant." } }, "services": { "apply": { - "name": "apply", - "description": "Applies the current Adaptive Lighting settings to lights.", + "name": "Apply settings", + "description": "Apply this profile's current curve values to its lights immediately, without waiting for the next scheduled update.", "fields": { "entity_id": { - "description": "The `entity_id` of the switch with the settings to apply. 📝", - "name": "entity_id" + "name": "Master switch", + "description": "The master switch entity whose profile settings to apply." }, "lights": { - "description": "A light (or list of lights) to apply the settings to. 💡", - "name": "lights" + "name": "Lights", + "description": "Lights to apply the settings to. If empty, applies to all lights the profile controls." }, "transition": { - "description": "Duration of transition when lights change, in seconds. 🕑", - "name": "transition" + "name": "Transition time (s)", + "description": "Fade time for this one application." }, "adapt_brightness": { - "description": "Whether to adapt the brightness of the light. 🌞", - "name": "adapt_brightness" + "name": "Adapt brightness", + "description": "Adjust brightness on supporting lights." }, "adapt_color": { - "description": "Whether to adapt the color on supporting lights. 🌈", - "name": "adapt_color" + "name": "Adapt color", + "description": "Adjust color temperature on supporting lights." }, "prefer_rgb_color": { - "description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈", - "name": "prefer_rgb_color" + "name": "Prefer RGB", + "description": "Send RGB when the light supports both color temperature and RGB." }, "turn_on_lights": { - "description": "Whether to turn on lights that are currently off. 🔆", - "name": "turn_on_lights" - } - } - }, - "set_manual_control": { - "name": "set_manual_control", - "description": "Mark whether a light is 'manually controlled'.", - "fields": { - "entity_id": { - "description": "The `entity_id` of the switch in which to (un)mark the light as being `manually controlled`. 📝", - "name": "entity_id" - }, - "lights": { - "description": "entity_id(s) of lights, if not specified, all lights in the switch are selected. 💡", - "name": "lights" - }, - "manual_control": { - "description": "Whether to add (\"true\") or remove (\"false\") all adapted attributes of the light from the \"manual_control\" list, or the name of an attribute for selective addition. 🔒", - "name": "manual_control" + "name": "Turn on lights that are off", + "description": "If true, also turn on lights that are currently off before applying." } } }, "change_switch_settings": { - "name": "change_switch_settings", - "description": "Change any settings you'd like in the switch. All options here are the same as in the config flow.", + "name": "Change profile settings", + "description": "Change one or more of this profile's settings at runtime. The change persists until the next options-flow save or HA restart.", "fields": { "entity_id": { - "description": "Entity ID of the switch. 📝", - "name": "entity_id" + "name": "Master switch", + "description": "The master switch entity whose profile to update." }, "use_defaults": { - "description": "Sets the default values not specified in this service call. Options: \"current\" (default, retains current values), \"factory\" (resets to documented defaults), or \"configuration\" (reverts to switch config defaults). ⚙️", - "name": "use_defaults" + "name": "Defaults source", + "description": "Where to pull unsupplied values from: 'current' keeps existing values, 'factory' resets to documented defaults, 'configuration' reverts to the profile's configured defaults." }, "include_config_in_attributes": { - "description": "Show all options as attributes on the switch in Home Assistant when set to `true`. 📝", - "name": "include_config_in_attributes" + "name": "Expose config in attributes", + "description": "Show all options as attributes on the master switch." }, "turn_on_lights": { - "description": "Whether to turn on lights that are currently off. 🔆", - "name": "turn_on_lights" + "name": "Turn on lights that are off", + "description": "If true, also turn on lights that are currently off before applying." }, "initial_transition": { - "description": "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️", - "name": "initial_transition" - }, - "sleep_transition": { - "description": "Duration of transition when \"sleep mode\" is toggled in seconds. 😴", - "name": "sleep_transition" + "name": "First-on fade (s)", + "description": "Fade time when a light first turns on." }, "max_brightness": { - "description": "Maximum brightness percentage. 💡", - "name": "max_brightness" + "name": "Maximum brightness", + "description": "Brightness at the peak of the day." }, "max_color_temp": { - "description": "Coldest color temperature in Kelvin. ❄️", - "name": "max_color_temp" + "name": "Coolest color temperature", + "description": "The mid-day tone." }, "min_brightness": { - "description": "Minimum brightness percentage. 💡", - "name": "min_brightness" + "name": "Minimum brightness", + "description": "Brightness during the night." }, "min_color_temp": { - "description": "Warmest color temperature in Kelvin. 🔥", - "name": "min_color_temp" - }, - "only_once": { - "description": "Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄", - "name": "only_once" + "name": "Warmest color temperature", + "description": "The night-time tone." }, "prefer_rgb_color": { - "description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈", - "name": "prefer_rgb_color" + "name": "Prefer RGB", + "description": "When a light supports both, send RGB." }, "separate_turn_on_commands": { - "description": "Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀", - "name": "separate_turn_on_commands" + "name": "Split brightness and color into separate calls", + "description": "Required by some lights." }, "send_split_delay": { - "description": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️", - "name": "send_split_delay" + "name": "Pause between split calls (ms)", + "description": "Only used when the option above is on." }, - "sleep_brightness": { - "description": "Brightness percentage of lights in sleep mode. 😴", - "name": "sleep_brightness" + "sunrise_entity": { + "name": "Sunrise entity", + "description": "Any sensor that returns the next sunrise timestamp." }, - "sleep_rgb_or_color_temp": { - "description": "Use either `\"rgb_color\"` or `\"color_temp\"` in sleep mode. 🌙", - "name": "sleep_rgb_or_color_temp" - }, - "sleep_rgb_color": { - "description": "RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is \"rgb_color\"). 🌈", - "name": "sleep_rgb_color" - }, - "sleep_color_temp": { - "description": "Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴", - "name": "sleep_color_temp" - }, - "sunrise_offset": { - "description": "Adjust sunrise time with a positive or negative offset in seconds. ⏰", - "name": "sunrise_offset" - }, - "sunrise_time": { - "description": "Set a fixed time (HH:MM:SS) for sunrise. 🌅", - "name": "sunrise_time" - }, - "sunset_offset": { - "description": "Adjust sunset time with a positive or negative offset in seconds. ⏰", - "name": "sunset_offset" - }, - "sunset_time": { - "description": "Set a fixed time (HH:MM:SS) for sunset. 🌇", - "name": "sunset_time" - }, - "max_sunrise_time": { - "description": "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier sunrises. 🌅", - "name": "max_sunrise_time" - }, - "min_sunset_time": { - "description": "Set the earliest virtual sunset time (HH:MM:SS), allowing for later sunsets. 🌇", - "name": "min_sunset_time" - }, - "take_over_control": { - "description": "Pause adaptation of individual lights and hand over (manual) control to other sources that issue `light.turn_on` calls for lights that are on. 🔒", - "name": "take_over_control" - }, - "take_over_control_mode": { - "description": "The adaptation pausing mode when other sources change brightness and/or color of lights. `pause_all` always pauses both brightness and color adaptation. `pause_changed` pauses the adaptation of only the changed attributes and continues adapting unchanged attributes, e.g., continues color adaptation when only brightness was changed.", - "name": "take_over_control_mode" - }, - "detect_non_ha_changes": { - "description": "Detects and halts adaptations for non-`light.turn_on` state changes. Needs `take_over_control` enabled. 🕵️ Caution: ⚠️ Some lights might falsely indicate an 'on' state, which could result in lights turning on unexpectedly. Note that this calls `homeassistant.update_entity` every `interval`! Disable this feature if you encounter such issues.", - "name": "detect_non_ha_changes" + "sunset_entity": { + "name": "Sunset entity", + "description": "Any sensor that returns the next sunset timestamp." }, "transition": { - "description": "Duration of transition when lights change, in seconds. 🕑", - "name": "transition" + "name": "Transition time (s)", + "description": "Fade time for each scheduled curve update." }, "adapt_delay": { - "description": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️", - "name": "adapt_delay" + "name": "Delay before adapting (s)", + "description": "Wait this long after a light reports 'on' before applying." }, - "autoreset_control_seconds": { - "description": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️", - "name": "autoreset_control_seconds" + "skip_redundant_commands": { + "name": "Skip redundant commands", + "description": "Don't send a command if the light is already at the target state." + }, + "intercept": { + "name": "Intercept light.turn_on calls", + "description": "Apply the curve the moment a light turns on." + }, + "multi_light_intercept": { + "name": "Intercept multi-light calls", + "description": "Also intercept calls that target several lights at once." } } } + }, + "exceptions": { + "incompatible_version": { + "message": "Adaptive Lighting v{current_version} (CDiT fork) is incompatible with the existing config entry (version {entry_version}). Delete the entry from Settings → Devices & Services and create a new one." + } } } diff --git a/openspec/changes/cdit-config-redesign/tasks.md b/openspec/changes/cdit-config-redesign/tasks.md index 77d23b9d..52a0d704 100644 --- a/openspec/changes/cdit-config-redesign/tasks.md +++ b/openspec/changes/cdit-config-redesign/tasks.md @@ -75,16 +75,16 @@ Group 7-9 are tests, translations, and docs (final polish). ## 8. Strings and translations -- [ ] 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] -- [ ] 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] -- [ ] 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] -- [ ] 8.4 Remove `strings.json` keys for the 21 deleted fields and the sleep switch entity. [R1, D7] -- [ ] 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] -- [ ] 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. Examples: "Adapt Brightness" → "Adjust brightness through the day"; "Initial transition" → "Fade time when a light first turns on (seconds)"; "Send split delay" → "Pause between commands (ms)". Abort messages and error messages get the same treatment. [R1, R7, R8, polish] +- [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 -- [ ] 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] -- [ ] 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] -- [ ] 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] +- [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.)_