GitPedia
jrhubott

jrhubott/adaptive-cover-pro

Adaptive sun-tracking cover control for Home Assistant: blinds, awnings, and venetian tilts with climate-aware positioning and a priority override pipeline.

30 Releases
Latest: today
Adaptive Cover Pro ⛅ v2.30.1v2.30.1Latest
jrhubottjrhubott·today·June 28, 2026
GitHub

🐛 Fixed

  • Building profiles excluded from duplicate-from source list (#732, #735): New helper `_cover_entries(hass)` in `profile_link.py` returns only physical cover config entries — those whose policy reports `controls_cover == True`. Both `async_step_user` and `async_step_duplicate_select` now call `_cover_entries(hass)` instead of `hass.config_entries.async_entries(DOMAIN)`, so building profile entries are excluded from the duplicate source list. The "Duplicate existing" menu option is also hidden when no physical cover entries exist.
  • `duplicate_configure` field labels corrected (#733, #736): `duplicate_configure.data` in `en.json`, `de.json`, and `fr.json` used keys `entities` and `azimuth`, but the schema fields are named `group` and `set_azimuth`. Home Assistant renders the raw key as the label when the key doesn't match, so users were seeing "entities" and "azimuth" as field labels instead of the translated strings. Keys corrected to `group` and `set_azimuth` across all three translation files. New regression test `test_duplicate_configure_translation_keys_match_schema` locks this.

📋 Changed

  • Issue lifecycle (#737): Stable releases now close referenced issues immediately instead of waiting 14 days for reporter confirmation. Reporters can reopen a closed issue by commenting "still happening" or `/reopen`.

🧪 Testing

  • 5292 tests passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
  • ---
  • <!--
  • IDENTIFIER_CITATIONS:
  • Building profiles excluded from duplicate-from list (#732, #735):
  • `_cover_entries(hass)` — 2342940f · custom_components/adaptive_cover_pro/profile_link.py, config_flow.py
  • `controls_cover` — 2342940f · custom_components/adaptive_cover_pro/profile_link.py
  • `async_step_user` — 2342940f · custom_components/adaptive_cover_pro/config_flow.py
  • + 11 more
Adaptive Cover Pro ⛅ v2.30.0v2.30.0
jrhubottjrhubott·today·June 28, 2026
GitHub

Added

  • Roof/skylight window cover type (#212, #696): New cover type `"cover_roof_window"` with pitch-aware sun geometry. Configure `CONF_ROOF_PITCH` and `CONF_ROOF_HEIGHT_ABOVE` to match the physical installation; `RoofSunGeometry` handles the tilted-plane math, `RoofWindowCover` drives position calculation, and `RoofWindowPolicy` provides the policy layer. The type is registered in `ALL_COVER_TYPES` alongside the existing blind/awning/tilt types. Closes the oldest open feature request.
  • Building profile (#693, #700, #706): A new virtual entry type, created via its own config-flow steps (`async_step_create_building_profile()`, `async_step_building_profile_sensors()`), that holds shared building-level weather and climate sensors without controlling any covers. Linked covers receive live sensor propagation via `_async_profile_propagate()`; `_copy_profile_to_cover()` applies profile values and `_covers_linked_to()` tracks which entries reference a given profile. `BuildingProfilePolicy` and `SensorSource` define the data model. On deletion, `async_remove_entry()` cleans the dangling profile link from all linked covers. The standalone weather-retraction toggle was removed in the same pass (#706); the migration handles existing data transparently.
  • Building Profile overview of linked covers: `async_step_profile_overview` in `building_overview.py` presents a comparison table of every cover linked to the profile. Rows are modeled with `_CoverRecord`, `_DiffSpec`, and `_Entry` dataclasses; diff logic uses `_comparison_as_list`, `_comparison_as_table`, and `_format_diff_line`, with local overrides rendered by `_local_override_repr`. Number normalization via `_num` ensures numeric values compare correctly regardless of representation.
  • Inherit/override model for Building Profile sensors: `async_step_profile_overrides` lets a linked cover override any inherited profile sensor locally. Each override is tracked as an `OverrideRecord` dataclass, giving the options flow a structured representation of which fields are inherited and which are locally set.
  • Sun Tracking switch; glare-zone protection without sun tracking (#498, #678): A new Sun Tracking switch (`CONF_ENABLE_SUN_TRACKING`, option key `enable_sun_tracking`) exposes runtime control over whether the solar position engine drives cover movement. The switch defaults to on (`True`), so all existing installs continue to track the sun. When toggled off, `async_apply_sun_tracking_update` rebuilds the pipeline without a reload; `CONF_ENABLE_SUN_TRACKING` is listed in `_RUNTIME_APPLICABLE_OPTIONS` so the update listener applies it without triggering a config-entry reload. `GlareZoneHandler` is now aware of the tracking state: when `enable_sun_tracking` is off, glare-zone protection can still activate if its computed position is more protective than the default result, as determined by `more_protective_position`; `apply_snapshot_limits` enforces sun-dependent limits only when tracking is on. Thanks to @Zhephyr54 for filing #498 and contributing the implementation.
  • Multiple blind-spot slots with elevation modes (#701, #702, #708): Blind-spot configuration expands from one slot to three (`BLIND_SPOT_SLOTS`). Each slot carries left/right azimuth bounds plus an elevation threshold and an elevation mode: `BLIND_SPOT_ELEV_MODE_BELOW` blocks low-sun intrusion, `BLIND_SPOT_ELEV_MODE_ABOVE` blocks high-sun intrusion. `BlindSpot` is a frozen dataclass; `_make_blind_spot()` and `_extra_blind_spots_from()` build the slot list from config, `_blind_spot_slot_keys()` drives options-flow validation, `_blind_spot_step_errors()` validates bounds, and `_sun_in_blind_spot()` does the single containment check for both modes. Slot 1 reuses the legacy unsuffixed keys so existing configurations carry forward unchanged; slots 2 and 3 are optional and suffixed. Diagnostics loop over all active slots dynamically.
  • Ten custom-position slots (#703, #704): `CUSTOM_POSITION_SLOT_NUMBERS` now covers slots 1-10, up from 1-5. Slots 6-10 behave identically to existing slots and respect the same priority range (1-100, default 77).
  • Endpoint-aware open/close (#697, #699): When a cover supports `set_position`, open and close commands now drive the carriage to its physical endpoints rather than issuing only a generic service call. `apply_user_position_endpoint_open()` and `apply_user_position_endpoint_close()` handle the axis-aware dispatch; `_POSITION_AXIS_SERVICES` maps each axis to its service.
  • + 9 more

🐛 Fixed

  • Venetian tilt back-rotated after close (#694): On hardware where the motor drags the slats shut during a close, the integration previously left the verified-tilt flag set and never re-asserted the commanded angle. `_tilt_targets_verified.discard()` is now called after any position move so the sequencer re-reads actual tilt on the next update and re-asserts the correct angle. Thanks to @vaind for reporting and fixing this edge case.
  • Venetian coupled-tilt recovery after `set_cover_position` (#679, #680): On mechanically coupled venetians, a `set_cover_position` command back-drives the tilt actuator, leaving the stored tilt target stale. The previous stored-target dedup silently discarded the next intended tilt command even when the actuator had drifted. `_target_already_satisfied` now compares the stored target against the live actuator reading via `_resolve_tilt_anchor`; it returns `True` only when the stored target matches the live reading within `VENETIAN_TILT_VERIFY_TOLERANCE`. When the live reading has drifted, the dedup returns `False` and the tilt command is re-sent. This fix is always on and is independent of `CONF_ENFORCE_DELTA_AT_ENDPOINTS`. Thanks to @elmakus (#679) for reporting this.
  • Proxy tilt drove the carriage, not the slats (#684, #685): The venetian proxy's `async_set_cover_tilt_position` was calling `async_apply_user_position` with the requested tilt value, which moved the carriage and left the slats at whatever angle they held. It now calls `async_apply_user_tilt`, which dispatches through `VenetianPolicy.apply_user_tilt` to `DualAxisSequencer.update_tilt_only` with `force=True`.
  • `solar_calculation` attributes blank outside the solar window (#682): When the sun was outside the tracking window, `_last_calc_details` was `None` and the sensor read `unknown` with empty attributes. `DiagnosticsBuilder._build_position_calc_details` now emits a minimal fallback trace (`sol_elev_deg`, `gamma_deg`, `position_pct=None`) whenever no engine trace was recorded, with a `status` field drawn from `control_state_reason`.
  • Roof-window FOV gate now accounts for cover tilt (#212, #728): The field-of-view gate for roof/skylight covers was computing sun containment from the raw azimuth without accounting for how the cover's own tilt affects the effective field of view. The gate now applies tilt-aware geometry, preventing false exclusions where a tilted roof cover would incorrectly discard sun positions that should be tracked.
  • Per-day predicted sun window now accounts for roof cover tilt (#729, #731): The per-day sun-window prediction used for scheduling was computing the sun-in-FOV interval without adjusting for a roof cover's pitch, leading to incorrectly wide or narrow tracking windows. The prediction now uses the same tilt-aware geometry as the real-time gate.
  • Building Profile step missing from cover create flow (#727): The Building Profile setup step was not appearing when creating a new cover entry. The create flow now correctly routes to the profile step.
  • Building Profile options flow routed to sensor-only step (#715, #717): The Building Profile options flow now routes to `async_step_profile_sensors` rather than the full cover options flow, so profile entries can only configure shared sensors.
  • + 4 more

📦 Upgrade Notes

  • Weather override toggle: The v3.5 to v3.6 migration sets `CONF_WEATHER_ENABLED = True` for all pre-existing entries, preserving existing behavior. The off-by-default applies only to new installs.
  • Blind-spot slots: Slot 1 reuses the legacy unsuffixed keys. Slots 2-3 are optional and additive; existing single-slot configurations are unaffected.
  • Custom-position slots: Slots 6-10 are additive; existing slots 1-5 are unchanged.

🧪 Testing

  • 5288 tests passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
  • ---
  • <!--
  • IDENTIFIER_CITATIONS:
  • Roof/skylight window cover type:
  • `"cover_roof_window"` — 2dff2e84 · cover_types/roof_window.py, const.py
  • `CONF_ROOF_PITCH` — 2dff2e84 · config_types.py, const.py
  • `CONF_ROOF_HEIGHT_ABOVE` — 2dff2e84 · config_types.py, const.py
  • + 107 more
Adaptive Cover Pro ⛅ v2.30.0-beta.4v2.30.0-beta.4Pre-release
jrhubottjrhubott·yesterday·June 27, 2026
GitHub

Added

  • Roof/skylight window cover type (#212, #696): New cover type `"cover_roof_window"` with pitch-aware sun geometry. Configure `CONF_ROOF_PITCH` and `CONF_ROOF_HEIGHT_ABOVE` to match the physical installation; `RoofSunGeometry` handles the tilted-plane math, `RoofWindowCover` drives position calculation, and `RoofWindowPolicy` provides the policy layer. The type is registered in `ALL_COVER_TYPES` alongside the existing blind/awning/tilt types. Closes the oldest open feature request.
  • Building profile (#693, #700, #706): A new virtual entry type — created via its own config-flow steps (`async_step_create_building_profile()`, `async_step_building_profile_sensors()`) — that holds shared building-level weather and climate sensors without controlling any covers. Linked covers receive live sensor propagation via `_async_profile_propagate()`; `_copy_profile_to_cover()` applies profile values and `_covers_linked_to()` tracks which entries reference a given profile. `BuildingProfilePolicy` and `SensorSource` define the data model. On deletion, `async_remove_entry()` cleans the dangling profile link from all linked covers. The standalone weather-retraction toggle was removed in the same pass (#706); the migration handles existing data transparently.
  • Building Profile overview of linked covers: `async_step_profile_overview` in the new `building_overview.py` presents a comparison table of every cover linked to the profile. Rows are modeled with the `_CoverRecord`, `_DiffSpec`, and `_Entry` dataclasses; diff logic uses `_comparison_as_list`, `_comparison_as_table`, and `_format_diff_line`, with local overrides rendered by `_local_override_repr`. Number normalization via `_num` ensures numeric values compare correctly regardless of representation.
  • Inherit/override model for Building Profile sensors: `async_step_profile_overrides` lets a linked cover override any inherited profile sensor locally. Each override is tracked as an `OverrideRecord` dataclass, giving the options flow a structured representation of which fields are inherited and which are locally set.
  • Multiple blind-spot slots with elevation modes (#701, #702, #708): Blind-spot configuration expands from one slot to three (`BLIND_SPOT_SLOTS`). Each slot carries left/right azimuth bounds plus an elevation threshold and an elevation mode: `BLIND_SPOT_ELEV_MODE_BELOW` blocks low-sun intrusion, `BLIND_SPOT_ELEV_MODE_ABOVE` blocks high-sun intrusion. `BlindSpot` is a frozen dataclass; `_make_blind_spot()` and `_extra_blind_spots_from()` build the slot list from config, `_blind_spot_slot_keys()` drives options-flow validation, `_blind_spot_step_errors()` validates bounds, and `_sun_in_blind_spot()` does the single containment check for both modes. Slot 1 reuses the legacy unsuffixed keys so existing configurations carry forward unchanged; slots 2 and 3 are optional and suffixed. Diagnostics loop over all active slots dynamically.
  • Ten custom-position slots (#703, #704): `CUSTOM_POSITION_SLOT_NUMBERS` now covers slots 1–10, up from 1–5. Slots 6–10 behave identically to existing slots and respect the same priority range (1–100, default 77).
  • Endpoint-aware open/close (#697, #699): When a cover supports `set_position`, open and close commands now drive the carriage to its physical endpoints rather than issuing only a generic service call. `apply_user_position_endpoint_open()` and `apply_user_position_endpoint_close()` handle the axis-aware dispatch; `_POSITION_AXIS_SERVICES` maps each axis to its service.
  • On/off master toggle for weather override (#719): `CONF_WEATHER_ENABLED` gates the entire weather handler. New installs default to off (`DEFAULT_WEATHER_ENABLED = False`); the v3.5→v3.6 migration sets `CONF_WEATHER_ENABLED = True` for all existing entries, so upgrading changes nothing for installs that already have weather sensors configured.
  • + 2 more

🐛 Fixed

  • Venetian tilt back-rotated after close (#694): On hardware where the motor drags the slats shut during a close, the integration previously left the verified-tilt flag set and never re-asserted the commanded angle. `_tilt_targets_verified.discard()` is now called after any position move so the sequencer re-reads actual tilt on the next update and re-asserts the correct angle. Thanks to @vaind for reporting and fixing the edge case.
  • Building Profile options flow routed to sensor-only step (#715, #717): The Building Profile options flow now routes to `async_step_profile_sensors` — a sensor-only step — rather than the full cover options flow, so profile entries can only configure shared sensors.
  • Building Profile template fields scoped and surfaced in UI (#720, #722): Template fields in the Building Profile options flow are now correctly scoped to the profile entry and visible in the UI; previously they could leak across entries or not appear.
  • Building Profile entries skipped in `loaded_coordinators` (#712): Building profile entries are now excluded from `loaded_coordinators` so coordinator lookups intended for cover entries don't inadvertently resolve to profile entries.
  • `async_unload_entry` guarded for non-cover building profiles (#712, #714): `async_unload_entry` now guards against non-cover building profile entries, preventing an unhandled path when unloading a profile entry.
  • German translation drift resolved (#709, #710): Several config and options flow strings were out of sync between English and German. The drift is resolved; no string keys changed.

🧪 Testing

  • 5183 tests passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
  • ---
  • <!--
  • IDENTIFIER_CITATIONS:
  • `"cover_roof_window"` — beta.3 (carried forward)
  • `CONF_ROOF_PITCH` — beta.3 (carried forward)
  • `CONF_ROOF_HEIGHT_ABOVE` — beta.3 (carried forward)
  • `RoofSunGeometry` — beta.3 (carried forward)
  • + 44 more
Adaptive Cover Pro ⛅ v2.30.0-beta.3v2.30.0-beta.3Pre-release
jrhubottjrhubott·yesterday·June 27, 2026
GitHub

Added

  • Roof/skylight window cover type (#212, #696): New cover type `"cover_roof_window"` with pitch-aware sun geometry. Configure `CONF_ROOF_PITCH` and `CONF_ROOF_HEIGHT_ABOVE` to match the physical installation; `RoofSunGeometry` handles the tilted-plane math, `RoofWindowCover` drives position calculation, and `RoofWindowPolicy` provides the policy layer. The type is registered in `ALL_COVER_TYPES` alongside the existing blind/awning/tilt types. Closes the oldest open feature request.
  • Building profile (#693, #700, #706): A new virtual entry type — created via its own config-flow steps (`async_step_create_building_profile()`, `async_step_building_profile_sensors()`) — that holds shared building-level weather and climate sensors without controlling any covers. Linked covers receive live sensor propagation via `_async_profile_propagate()`; `_copy_profile_to_cover()` applies profile values and `_covers_linked_to()` tracks which entries reference a given profile. `BuildingProfilePolicy` and `SensorSource` define the data model. On deletion, `async_remove_entry()` cleans the dangling profile link from all linked covers. The standalone weather-retraction toggle was removed in the same pass (#706); the migration handles existing data transparently.
  • Multiple blind-spot slots with elevation modes (#701, #702, #708): Blind-spot configuration expands from one slot to three (`BLIND_SPOT_SLOTS`). Each slot carries left/right azimuth bounds plus an elevation threshold and an elevation mode: `BLIND_SPOT_ELEV_MODE_BELOW` blocks low-sun intrusion, `BLIND_SPOT_ELEV_MODE_ABOVE` blocks high-sun intrusion. `BlindSpot` is a frozen dataclass; `_make_blind_spot()` and `_extra_blind_spots_from()` build the slot list from config, `_blind_spot_slot_keys()` drives options-flow validation, `_blind_spot_step_errors()` validates bounds, and `_sun_in_blind_spot()` does the single containment check for both modes. Slot 1 reuses the legacy unsuffixed keys so existing configurations carry forward unchanged; slots 2 and 3 are optional and suffixed. Diagnostics loop over all active slots dynamically.
  • Ten custom-position slots (#703, #704): `CUSTOM_POSITION_SLOT_NUMBERS` now covers slots 1–10, up from 1–5. Slots 6–10 behave identically to existing slots and respect the same priority range (1–100, default 77).
  • Endpoint-aware open/close (#697, #699): When a cover supports `set_position`, open and close commands now drive the carriage to its physical endpoints rather than issuing only a generic service call. `apply_user_position_endpoint_open()` and `apply_user_position_endpoint_close()` handle the axis-aware dispatch; `_POSITION_AXIS_SERVICES` maps each axis to its service.

🐛 Fixed

  • Venetian tilt back-rotated after close (#694): On hardware where the motor drags the slats shut during a close, the integration previously left the verified-tilt flag set and never re-asserted the commanded angle. `_tilt_targets_verified.discard()` is now called after any position move so the sequencer re-reads actual tilt on the next update and re-asserts the correct angle. Thanks to @vaind for reporting and fixing the edge case.

🧪 Testing

  • 5114 tests passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
  • ---
  • <!--
  • IDENTIFIER_CITATIONS
  • `"cover_roof_window"` 2dff2e84 cover_types/roof_window.py, const.py
  • `CONF_ROOF_PITCH` 2dff2e84 config_types.py, const.py
  • `CONF_ROOF_HEIGHT_ABOVE` 2dff2e84 config_types.py, const.py
  • `RoofSunGeometry` 2dff2e84 engine/sun_geometry.py
  • + 26 more
Adaptive Cover Pro ⛅ v2.30.0-beta.2v2.30.0-beta.2Pre-release
jrhubottjrhubott·2d ago·June 26, 2026
GitHub

Added

  • Live `solar_calculation` diagnostic sensor for all cover types (#682, #683): Each cover now gets a `solar_calculation` sensor whose state is the per-cycle computed `position_pct` and whose attributes carry the full geometric trace: sun elevation, gamma, engine-specific intermediates, and for venetian covers a nested `tilt` sub-trace. The calc engines (vertical, horizontal, tilt, venetian) record their raw inputs and outputs in `_last_calc_details` each cycle. `DiagnosticsBuilder._round_trace` and `_round_trace_value` round every numeric leaf at the presentation boundary and stamp `cover_type` and `status` before the data reaches the sensor or the diagnostics download. The sensor state is numeric and reads `unknown` when there is no solar target (see the fix below for when that happens).
  • Configurable Venetian drift-reset direction (#686) (#690): A new per-instance option `CONF_VENETIAN_TILT_RESET_DIRECTION` (key `venetian_tilt_reset_direction`, select: `open` / `close`) controls which mechanical endpoint the drift-reset drives the slats through before re-sending the target. The default is `VENETIAN_TILT_RESET_OPEN` (`DEFAULT_VENETIAN_TILT_RESET_DIRECTION`), which preserves the existing behavior — drive fully open then back. Set to `VENETIAN_TILT_RESET_CLOSE` on hardware that rests near-closed during sun tracking (quieter, faster reset) or that re-zeroes the slat actuator on a close command. The direction is wired through the same live-lambda path as the threshold so an options change takes effect without a reload; valid values are listed in `VENETIAN_TILT_RESET_DIRECTIONS`. Upgrading from beta.1 changes nothing without reconfiguration. Thanks to @elmakus (#686) for requesting this.
  • Manual override detection from external input sensors (#688) (#691): A new per-instance option `CONF_MANUAL_OVERRIDE_INPUT_ENTITIES` (key `manual_override_input_entities`, multi-entity selector) accepts a list of binary sensors whose off→on edge engages manual override on every cover in the instance. Intended for a physical wall switch wired to an input (e.g. a Shelly 2PM exposing `binary_sensor.*_cover_input_0`): pressing it pauses automatic control for the configured override duration instead of inferring a manual move from the cover's reported position. The edge filter is strict — on→on, None→on (startup restore), and unavailable/unknown states all do nothing. Each press re-arms the full override duration. Combine with "Only engage manual override from Adaptive Cover Pro commands" to make these sensors the sole override trigger. Defaults to an empty list (feature disabled) — upgrading from beta.1 changes nothing. Thanks to @elmakus (#688) for requesting this.
  • `set_tilt` service for the slat/tilt axis (#684) (#685): A new `adaptive_cover_pro.set_tilt` service lets automations drive the venetian slat axis without touching the carriage. The service delegates to `async_apply_user_tilt`, which routes through the `apply_user_tilt` policy hook: `VenetianPolicy` drives only the slats via the sequencer; other cover types fall back to the position path. A `force` parameter (default `false`) governs manual-override engagement, matching `set_position` semantics. Implemented in `set_tilt_service.py` via `async_handle_set_tilt`.

🐛 Fixed

  • Proxy tilt drove the carriage, not the slats (#684) (#685): The venetian proxy's `async_set_cover_tilt_position` was calling `async_apply_user_position` with the requested tilt value, which moved the carriage to that number and left the slats at whatever angle they held. It now calls `async_apply_user_tilt`, which dispatches through `VenetianPolicy.apply_user_tilt` to `DualAxisSequencer.update_tilt_only` with `force=True` — the carriage is never commanded. The `force` flag on `update_tilt_only` bypasses the target-unchanged dedup so a user re-requesting the current tilt value still fires.
  • `solar_calculation` attributes blank outside the solar window (#682): When the sun was outside the tracking window the solar handler never ran, so `_last_calc_details` was `None` and the sensor read `unknown` with empty attributes. `DiagnosticsBuilder._build_position_calc_details` now emits a minimal fallback trace — `sol_elev_deg`, `gamma_deg`, `position_pct=None` — whenever no engine trace was recorded. The `status` field, drawn from `control_state_reason`, is stamped on every trace (full or fallback) to explain why no solar target was computed.
  • Deploy script stamped raw sha, blocking integration load: The deploy script was writing a bare 7-character git sha as the manifest `version` field. HA's `AwesomeVersion` parser rejects shas that contain hex letters, silently blocking the integration from loading. The script now carries the sha as SemVer build metadata (e.g. `2.30.0-beta.1+873727b`) and strips any prior metadata on re-deploy.

🧪 Testing

  • 4888 tests passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
Adaptive Cover Pro ⛅ v2.30.0-beta.1v2.30.0-beta.1Pre-release
jrhubottjrhubott·2d ago·June 25, 2026
GitHub

Added

  • Venetian drift-reset threshold (#663, #681): A new per-instance option `CONF_VENETIAN_TILT_RESET_THRESHOLD` (key `venetian_tilt_reset_threshold`) sets an accumulated commanded-tilt-% threshold that triggers a mechanical drift-reset cycle. Each tilt command sent to the hardware — after dedup, delta gating, and dry-run checks — adds the absolute change from the prior anchor to a per-entity `_accumulated_tilt` counter tracked by `DualAxisSequencer`. When the counter reaches the threshold, the sequencer drives the slats fully open (to `POSITION_OPEN`, bypassing any configured tilt max) and back to the original target position, flushing mechanical actuator drift. A `_reset_in_progress` guard prevents the two re-zero sends from themselves re-accumulating. The default is `DEFAULT_VENETIAN_TILT_RESET_THRESHOLD` = `0`, which disables the feature entirely; range is 0–5000 accumulated %. The threshold can be changed without a reload and takes effect on the next tilt cycle. Thanks to @x4N70pHyLL (#663) for surfacing this.
  • Sun Tracking switch; glare-zone protection without sun tracking (#678): A new "Sun Tracking" switch (`CONF_ENABLE_SUN_TRACKING`, option key `enable_sun_tracking`) exposes runtime control over whether the solar position engine drives cover movement. The switch defaults to on (`True`), so all existing installs continue to track the sun exactly as before. When toggled off, `async_apply_sun_tracking_update` rebuilds the pipeline in place without a reload; `CONF_ENABLE_SUN_TRACKING` is listed in `_RUNTIME_APPLICABLE_OPTIONS` so the update listener applies it without triggering a config-entry reload. The `GlareZoneHandler` (priority 45, vertical covers only) is now aware of the tracking state: when `enable_sun_tracking` is off, glare-zone protection can still override if its computed position is more protective than the `compute_default_position` result, as determined by `more_protective_position`; `apply_snapshot_limits` enforces sun-dependent limits only when tracking is on. Thanks to @Zhephyr54 (#678) for contributing this feature and for filing issue #498.
  • Opt-in endpoint delta enforcement for coupled covers (`enforce_delta_at_endpoints`, #679, #680): A new boolean option `CONF_ENFORCE_DELTA_AT_ENDPOINTS` (key `enforce_delta_at_endpoints`, default `False`) brings the min-change delta gate to endpoint commands. By default (`DEFAULT_ENFORCE_DELTA_AT_ENDPOINTS` = `False`), the always-send-to-0/100 guarantee introduced in #629 is preserved byte-for-byte: `build_special_positions` returns `[0, 100]` and the tilt special-position bypass is in effect. When opted in, `build_special_positions` returns an empty list and `check_position_delta` runs for endpoint targets too — useful on mechanically coupled covers where sending a full-open or full-close command disturbs the tilt axis and the actual hardware endpoint is unreachable (e.g. the cover parks at 3 rather than 0). Enabled via the config flow (Boolean selector, default off) and added to `SYNC_CATEGORIES`.

🐛 Fixed

  • Venetian coupled-tilt recovery after `set_cover_position` (#679, #680): On mechanically coupled venetians, a `set_cover_position` command back-drives the tilt actuator, leaving the stored tilt target stale. The previous stored-target dedup (`tilt_target == self._tilt_targets.get(entity_id)`) silently discarded the next intended tilt command even when the actuator had drifted. `_target_already_satisfied` now compares the stored target against the live actuator reading via `_resolve_tilt_anchor`; it returns `True` only when the stored target matches the live reading within `VENETIAN_TILT_VERIFY_TOLERANCE`. When the live reading has drifted out of tolerance, the dedup returns `False` and the tilt command is re-sent, restoring the correct slat angle. This fix is always on — it is not gated by `CONF_ENFORCE_DELTA_AT_ENDPOINTS`. Thanks to @elmakus (#679) for reporting this.

🧪 Testing

  • 4792 tests passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
Adaptive Cover Pro ⛅ v2.29.0v2.29.0
jrhubottjrhubott·3d ago·June 25, 2026
GitHub

Added

  • Config flow restructured into a 4-layer pipeline setup UI (#613, #614): The configuration flow is now organized into a four-layer pipeline setup, replacing the previous flat form with a staged layout that groups related settings by pipeline layer.
  • Runtime-configurable built-in handler priorities (#651): The seven built-in pipeline handler priorities — weather, manual override, motion timeout, cloud suppression, climate, glare zone, and solar — can now be reconfigured per instance rather than being fixed constants. Existing installs default to the same values as before; no change in behavior when left unset.
  • Daytime gate for sunset/sunrise automation (#632, #641): Sunset and sunrise position changes can now be gated on a binary sensor or a Jinja template. When the gate reads dark, the automation holds. Both options default to None; existing installs are unaffected.
  • Jinja template option for is_sunny, presence, and weather sensors (#639, #646): The is_sunny, presence, weather-is-raining, and weather-is-windy inputs now accept Jinja2 templates in addition to entity IDs. All template-mode keys default to None; existing entity-based configurations continue to work without change.
  • End-of-window close position (#625, #647): A new optional setting lets you configure a fixed close position applied when the automation window ends, independently of sunset behavior. Leaving it unset preserves existing behavior exactly.
  • Sun-tracking min position honored in summer climate close (#631, #648): The summer climate close path now respects the configured sun-tracking minimum position before returning a final position, preventing over-close on installs with a raised floor.
  • Active tilt-only custom slot surfaced in Control Status (#667, #671): When a custom-position slot contributes a tilt overlay without winning the position axis, the Control Status tracker entity now shows which slot is actively fixing the tilt angle. The position explanation and control state reason both gain a "tilt fixed by Custom #N" annotation.
  • Awning pivot-to-pane offset modeled; monotone low-sun shade corrected (#586, #661): A new optional `CONF_AWNING_PIVOT_OFFSET` setting captures the horizontal standoff of the arm housing from the window glass. At low sun the dropped fabric now shades further down the pane on wall-mounted awnings. A monotone-coverage constraint is also applied to the solver, eliminating a false mid-sweep optimum that previously drove some installs to a partial angle at very low sun instead of full extension. Existing installs are unaffected — `CONF_AWNING_PIVOT_OFFSET` defaults to 0.0 m, reproducing the prior model exactly.

🐛 Fixed

  • Tilt min-delta gate bypassed for special positions 0 and 100 (#629, #672): Tilt commands targeting fully-open (100) or fully-closed (0) now bypass the min-delta drift-suppression gate. Previously a venetian blind in tilt-only mode could silently discard an explicit 0 or 100 tilt command when the actuator was already within the configured delta threshold.
  • Lux and irradiance switches now gated on cloud suppression as well as climate mode (#668, #670): The predicates that control whether lux/irradiance toggle switches appear now check for cloud suppression in addition to climate mode. Previously a configuration using cloud suppression without climate mode did not surface these switches even though the lux/irradiance entities were actively used.
  • ACP-owned non-cover entity service targets resolved correctly (#665, #666): `_resolve_targets()` now includes an entity registry fallback so diagnostic sensors, switches, and buttons owned by an ACP config entry can be used as service call targets. Previously targeting a diagnostic entity (e.g. a decision-trace sensor) silently no-opped because the entity was not in the coordinator's cover-entity list.
  • Override engaged on context-less remote move with no recorded command target (#654, #662): Position delta detection now carries old-position context into the no-recorded-target path, distinguishing a genuine physical-remote move from a resting-position republish. A genuine move — where the position changed between old and new state by more than the effective threshold — now correctly engages manual override.
  • Diagnostics download triggers coordinator refresh when data is empty (#660): Downloading diagnostics now forces a coordinator refresh when data is empty, so the export is never blank on first request.
  • Daytime-gate sources tracked for immediate reposition (#632, #659): Daytime-gate template and sensor sources are now registered so a gate state change triggers an immediate cover reposition rather than waiting for the next scheduled cycle.
  • Night and custom positions dispatched when gate is dark but clock window is open (#656, #658): Night and custom positions are now dispatched correctly when the daytime gate reads dark but the automation clock window is still open.
  • SunData cache primed off the event loop to avoid tzdata blocking at boot (#655, #657): The SunData cache is now primed off the event loop, preventing a blocking tzdata import from stalling the event loop during startup.
  • + 2 more

🧪 Testing

  • 4735 tests passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
Adaptive Cover Pro ⛅ v2.29.0-beta.3v2.29.0-beta.3Pre-release
jrhubottjrhubott·4d ago·June 24, 2026
GitHub

Added

  • Runtime-configurable built-in handler priorities (#651): The built-in pipeline handler priorities (weather, manual override, motion timeout, cloud suppression, climate, glare zone, solar) can now be reconfigured rather than being fixed constants.
  • Config flow restructured into a 4-layer pipeline setup UI (#613, #614): The configuration flow is now organized into a four-layer pipeline setup for clearer, staged configuration.
  • End-of-window close position (#625, #647): A new optional setting lets you configure a fixed close position that applies when the automation window ends, independently of sunset behavior. Leaving it unset preserves existing behavior exactly.
  • Daytime gate for sunset/sunrise automation (#632, #641): Sunset and sunrise position changes can now be gated on a binary sensor or a Jinja template. Both options default to None; existing installs are unaffected.
  • Jinja template option for is_sunny, presence, and weather sensors (#639, #646): The is_sunny, presence, weather-is-raining, and weather-is-windy inputs now accept Jinja2 templates in addition to entity IDs. All keys default to None; existing entity-based configurations continue to work without change.
  • Sun-tracking min position honored in summer climate close (#631, #648): The summer climate close path now respects the configured sun-tracking minimum position before returning a position.
  • Active tilt-only custom slot surfaced in Control Status (#667, #671): The Control Status tracker entity now shows which custom-position slot is actively fixing the tilt angle via tilt-only contributions. When a slot applies its tilt overlay to the position winner, `PipelineResult.tilt_only_slot` is set and the position explanation and control state reason both gain a "tilt fixed by Custom #N" annotation. The field is None when no tilt-only slot fired or when the slot was deferred because the position winner already set tilt (`TiltAxisContribution.slot` carries the slot identity through the registry).

🐛 Fixed

  • Diagnostics download triggers coordinator refresh when data is empty (#660): Downloading diagnostics now forces a coordinator refresh when data is empty, so the export isn't blank.
  • Daytime-gate sources tracked for immediate reposition (#632, #659): Daytime-gate sources are now tracked so a gate change repositions the cover immediately.
  • Night and custom positions dispatched when gate is dark but clock window is open (#656, #658): Night and custom positions are now dispatched correctly when the daytime gate reads dark but the clock window is still open.
  • SunData cache primed off the event loop to avoid tzdata blocking at boot (#655, #657): The SunData cache is primed off the event loop, avoiding a blocking tzdata import during startup.
  • Template keys for is_sunny/presence/weather now registered in SYNC_CATEGORIES (#649): The four template-mode config keys are now registered so the template-mode selections sync correctly across related config entries.
  • Oscillating awning arm_length UI cap raised to 6 m (#636, #640): The arm_length config UI cap is now 6.0 m. Thanks to @muhamedfazalps (#637) for surfacing this.
  • Resolved options used on apply-user-position path (#643, #644): The manual-control apply-user-position path now uses the resolved options cache, consistent with the scheduled update path.
  • Tilt min-delta gate bypassed for special positions 0 and 100 (#629, #672): Tilt commands targeting fully-open (100) or fully-closed (0) now bypass the min-delta drift-suppression gate via `_TILT_SPECIAL_POSITIONS`. Previously a venetian blind in tilt-only mode could silently swallow an explicit 0 or 100 tilt command when the actuator was already within the configured delta threshold — mirroring the bypass that the position axis already applied.
  • + 4 more

🧪 Testing

  • 4735 tests passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
Adaptive Cover Pro ⛅ v2.29.0-beta.2v2.29.0-beta.2Pre-release
jrhubottjrhubott·5d ago·June 23, 2026
GitHub

Added

  • Runtime-configurable built-in handler priorities (#651): Each built-in pipeline handler's priority can now be overridden at the config-entry level without editing source code. The new `resolve_handler_priority` function reads per-handler overrides from options and falls back to the compiled-in default; `pipeline_priorities_schema` defines the options-service schema for validation. Existing installs get no change in behavior — all handlers continue to run at their current default priorities unless an override is explicitly set. Advanced users can lower or raise individual handlers (e.g. promote `cloud_suppression` above `manual_override`) without patching the integration.
  • Config flow restructured into a 4-layer pipeline setup UI (#613, #614): The config and options flows are reorganized around four explicit steps — behavior, position, automation, and diagnostics — driven by `async_step_behavior` and assembled via `build_priority_chain` and `class PriorityChainEntry`. The change eliminates the flat single-step form that became unwieldy as options grew, and gives each concern its own screen. Existing config entries are migrated transparently; no user action is required. Thanks to @zigomatichub (#613) for surfacing this.
  • End-of-window close position (#625, #647): A new optional setting lets you configure a fixed close position that applies when the automation window ends, independently of sunset behavior. Previously the only options were the sunset position or the default. Setting `CONF_END_OF_WINDOW_POS` makes the cover move to a specific position at end-of-window; leaving it unset (`None`) preserves existing behavior exactly. Existing installs need no action.
  • Daytime gate for sunset/sunrise automation (#632, #641): Sunset and sunrise position changes can now be gated on a binary sensor (`CONF_GATE_ENTITY`) or a Jinja template (`CONF_GATE_TEMPLATE`). When the gate is inactive, the integration skips the sunset/sunrise position push — useful for installs where the sun schedule should not apply on cloudy days or during occupancy patterns. Both options default to `None`; existing installs are unaffected.
  • Jinja template option for is_sunny, presence, and weather sensors (#639, #646): The is_sunny, presence, weather-is-raining, and weather-is-windy inputs now accept Jinja2 templates in addition to entity IDs. Four new template keys (`CONF_IS_SUNNY_TEMPLATE`, `CONF_PRESENCE_TEMPLATE`, `CONF_WEATHER_IS_RAINING_TEMPLATE`, `CONF_WEATHER_IS_WINDY_TEMPLATE`) and corresponding template-mode toggles (`CONF_IS_SUNNY_TEMPLATE_MODE`, `CONF_PRESENCE_TEMPLATE_MODE`, `CONF_WEATHER_IS_RAINING_TEMPLATE_MODE`, `CONF_WEATHER_IS_WINDY_TEMPLATE_MODE`) are available. Templates are resolved via `render_condition_or_none()` and `fold_condition_template()`. All keys default to `None`; existing entity-based configurations continue to work without change.
  • Sun-tracking min position honored in summer climate close (#631, #648): The summer climate close path in `ClimateCoverState.get_state()` now calls `apply_snapshot_limits()` before returning a position, which means the configured sun-tracking minimum position is respected. Previously, a summer-close command could move the cover below the sun-tracking min. No configuration change is needed; the behavior refinement applies automatically.

🐛 Fixed

  • Diagnostics download triggers coordinator refresh when data is empty (#660): The diagnostics export endpoint now calls `async def _refresh()` before building the payload when coordinator data is `None`. Previously, downloading diagnostics immediately after HA restart — before the first scheduled update — returned an empty or incomplete response. The two new tests (`test_data_none_triggers_refresh_then_returns_full_diagnostics`, `test_data_present_does_not_trigger_refresh`) confirm the refresh fires only when needed and is skipped on subsequent calls.
  • Daytime-gate sources tracked for immediate reposition (#632, #659): Gate entity and template registrations are now consolidated through `_register_template_tracker`, and state changes are routed through `async_check_daytime_gate_template_change`. Previously, a gate state change did not reliably trigger an immediate reposition — the cover would wait for the next scheduled cycle. The consolidated tracking path fires a coordinator refresh as soon as the gate opens or closes. Thanks to @MSL-DA (#632) for surfacing this.
  • Night and custom positions dispatched when gate is dark but clock window is open (#656, #658): When the daytime gate is inactive (dark) but the clock-based automation window is still open, the coordinator was skipping the night and custom-position dispatch entirely. The fix introduces `clock_window_open` as a distinct flag in `_compute_data_window`, separating "is it daytime?" from "is the automation window active?" so that night and custom positions are correctly applied regardless of gate state. Thanks to @isi07 (#656) for surfacing this.
  • SunData cache primed off the event loop to avoid tzdata blocking at boot (#655, #657): The call to `prime_cache` in the coordinator's startup path was issued on the event loop, causing a blocking tzdata file import that could stall HA startup under certain Python/tzdata combinations. The cache is now primed in an executor to keep the event loop free. Thanks to @MSL-DA (#655) for surfacing this.
  • Template keys for is_sunny/presence/weather now registered in `SYNC_CATEGORIES` (#649): The four template-mode config keys (`CONF_IS_SUNNY_TEMPLATE_MODE`, `CONF_PRESENCE_TEMPLATE_MODE`, `CONF_WEATHER_IS_RAINING_TEMPLATE_MODE`, `CONF_WEATHER_IS_WINDY_TEMPLATE_MODE`) added in #639 were missing from `SYNC_CATEGORIES` in `config_flow.py`, which controls which option keys propagate across related config entries. This fix ensures the template-mode selections sync correctly.
  • Oscillating awning arm_length UI cap raised to 6 m (#636, #640): The `_RANGE_ARM_LENGTH` upper bound in the config UI was capped below the real-world maximum, blocking users with longer arms from entering accurate values. The cap is now 6.0 m. Thanks to @muhamedfazalps (#637) for surfacing this.
  • Resolved options used on apply-user-position path (#643, #644): `async_apply_user_position()` was reading raw config-entry options rather than `_resolved_options`, so any option that requires rendering (such as template-based thresholds) arrived unrendered on the manual-control path. The fix ensures the resolved cache is used, consistent with the scheduled update path.

🧪 Testing

  • Full suite at 4710 passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
Adaptive Cover Pro ⛅ v2.29.0-beta.1v2.29.0-beta.1Pre-release
jrhubottjrhubott·6d ago·June 22, 2026
GitHub

Added

  • End-of-window close position (#625, #647): A new optional setting lets you configure a fixed close position that applies when the automation window ends, independently of sunset behavior. Previously the only options were the sunset position or the default. Setting `CONF_END_OF_WINDOW_POS` makes the cover move to a specific position at end-of-window; leaving it unset (`None`) preserves existing behavior exactly. Existing installs need no action.
  • Daytime gate for sunset/sunrise automation (#632, #641): Sunset and sunrise position changes can now be gated on a binary sensor (`CONF_GATE_ENTITY`) or a Jinja template (`CONF_GATE_TEMPLATE`). When the gate is inactive, the integration skips the sunset/sunrise position push — useful for installs where the sun schedule should not apply on cloudy days or during occupancy patterns. Both options default to `None`; existing installs are unaffected.
  • Jinja template option for is_sunny, presence, and weather sensors (#639, #646): The is_sunny, presence, weather-is-raining, and weather-is-windy inputs now accept Jinja2 templates in addition to entity IDs. Four new template keys (`CONF_IS_SUNNY_TEMPLATE`, `CONF_PRESENCE_TEMPLATE`, `CONF_WEATHER_IS_RAINING_TEMPLATE`, `CONF_WEATHER_IS_WINDY_TEMPLATE`) and corresponding template-mode toggles (`CONF_IS_SUNNY_TEMPLATE_MODE`, `CONF_PRESENCE_TEMPLATE_MODE`, `CONF_WEATHER_IS_RAINING_TEMPLATE_MODE`, `CONF_WEATHER_IS_WINDY_TEMPLATE_MODE`) are available. Templates are resolved via `render_condition_or_none()` and `fold_condition_template()`. All keys default to `None`; existing entity-based configurations continue to work without change.
  • Sun-tracking min position honored in summer climate close (#631, #648): The summer climate close path in `ClimateCoverState.get_state()` now calls `apply_snapshot_limits()` before returning a position, which means the configured sun-tracking minimum position is respected. Previously, a summer-close command could move the cover below the sun-tracking min. No configuration change is needed; the behavior refinement applies automatically.

🐛 Fixed

  • Template keys for is_sunny/presence/weather now registered in `SYNC_CATEGORIES` (#649): The four template-mode config keys (`CONF_IS_SUNNY_TEMPLATE_MODE`, `CONF_PRESENCE_TEMPLATE_MODE`, `CONF_WEATHER_IS_RAINING_TEMPLATE_MODE`, `CONF_WEATHER_IS_WINDY_TEMPLATE_MODE`) added in #639 were missing from `SYNC_CATEGORIES` in `config_flow.py`, which controls which option keys propagate across related config entries. This fix ensures the template-mode selections sync correctly.
  • Oscillating awning arm_length UI cap raised to 6 m (#636, #640): The `_RANGE_ARM_LENGTH` upper bound in the config UI was capped below the real-world maximum, blocking users with longer arms from entering accurate values. The cap is now 6.0 m. Thanks to @muhamedfazalps (#637) for surfacing this.
  • Resolved options used on apply-user-position path (#643, #644): `async_apply_user_position()` was reading raw config-entry options rather than `_resolved_options`, so any option that requires rendering (such as template-based thresholds) arrived unrendered on the manual-control path. The fix ensures the resolved cache is used, consistent with the scheduled update path.

🧪 Testing

  • Full suite at 4639 passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. The companion Lovelace card ([jrhubott/adaptive-cover-pro-card](https://github.com/jrhubott/adaptive-cover-pro-card)) is a separate repo with its own release cycle.
Adaptive Cover Pro ⛅ v2.28.2v2.28.2
jrhubottjrhubott·1w ago·June 21, 2026
GitHub

🐛 Bug Fixes

  • Climate threshold templates are now resolved before the first update cycle (#643, #644) — v2.28.0 migrated the climate temperature max/min thresholds to Jinja2 templates (#577), but the coordinator seeded its resolved-options cache with the _raw_, still-templated options at construction. Moving a cover through the card (the `set_position` path) before the first `_async_update_data` cycle had run passed an unrendered template string where the climate pipeline expected a number, raising an `Unexpected exception` in the WebSocket service handler. The coordinator now renders the threshold templates once at construction, and `async_apply_user_position` reads `_resolved_options` instead of the raw config entry, so manual control sees numeric thresholds from the very first command. Existing installs need no action — no data migration is required.

🧪 Testing

  • `test_apply_user_position_renders_string_thresholds` — asserts the apply-user position path renders templated string thresholds to numbers.
  • `test_apply_user_position_explicit_options_not_overridden_by_resolved` — confirms caller-supplied explicit options still take precedence over the resolved cache.
  • Full suite at 4543 passing.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+.
Adaptive Cover Pro ⛅ v2.28.1v2.28.1
jrhubottjrhubott·1w ago·June 21, 2026
GitHub

📦 Highlights

  • 🎯 Oscillating awning arm length cap corrected — the config-flow selector now accepts arm lengths up to 6.0 m, matching the range the service validator already permitted.

🐛 Bug Fixes

  • Oscillating awning: arm_length UI cap lifted from 3 m to 6 m (#636, #640, #642) — users entering a value above 3 m (e.g., 3.6 m) were rejected in the config flow even though the underlying `_RANGE_ARM_LENGTH` constant permits up to 6.0 m. The selector hardcoded `max_m=3` instead of reading the shared constant. The fix replaces the literal with `_RANGE_ARM_LENGTH[1]` so the UI cap and the service validator share a single source of truth. Existing installs are unaffected — no data migration is needed.

🧪 Testing

  • `test_geometry_schema_accepts_arm_length_up_to_6m` — asserts the geometry schema accepts 3.6 m and 6.0 m, and rejects 6.1 m.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+.
Adaptive Cover Pro ⛅ v2.28.0v2.28.0
jrhubottjrhubott·1w ago·June 21, 2026
GitHub

📦 ⚠️ Upgrade Notes

  • ---

📦 🎯 Highlights

  • Force Override absorbed into Custom Positions ([#563], [#584]): slot 5 at priority 100 replaces the standalone step, adds multi-sensor OR logic and Jinja2 template triggers, and existing configs auto-migrate.
  • Jinja2 templates in threshold fields ([#577], [#578]): temperature, lux, irradiance, cloud, wind, and rain thresholds accept templates resolved each coordinator cycle, not just static numbers.
  • Generate FOV from measurements ([#565], [#588], [#595]): a single button computes `fov_left`/`fov_right` from window width and reveal depth. FOV sliders stay visible and editable. A formula bug that halved the angle is fixed ([#583]).
  • Opt-in position matching ([#591], [#592]): `enable_position_matching` toggle (default OFF) controls retry-on-settle. Off means command once; an off-target delta engages manual override.
  • Climate status sensor gains typed inactive reasons ([#589], [#590]): `inactive_reason` slug (`active`, `mode_off`, `outside_time_window`, `thresholds_not_met`, `other_mode_active`, `readings_unavailable`) plus one-decimal setpoint attributes.
  • Low-sun and solar edge cases corrected ([#559], [#562], [#598], [#599]): full-open on low sun is fixed; vertical extreme-gamma is now elevation-aware; redundant backwards edge cases dropped.
  • ---

📦 Held position in manual override trace ([#608], [#609])

  • The manual override sensor's `decision_trace` attribute now includes the held position, making it straightforward to read what position the integration is locked to without navigating diagnostics.

📦 Set-position covers reach true 0% ([#569], [#572])

  • The lower clamp on solar-tracking positions is removed for set-position covers, so a 0% computed position closes the cover fully instead of stopping at 1%.

📦 Configuration Summary in your HA language ([#258])

  • The narrative summary shown during config and options flow renders in English, German, or French depending on the HA UI language setting.
  • ---

📦 Low-sun returned full-open ([#559], [#562])

  • An edge case in the low-sun path returned the full-open position instead of the closed position when the sun was below the valid elevation range. The correct return is now the configured default.

📦 Legacy custom-position sensor key migrated to list ([#563], [#597])

  • Older configs stored the custom-position trigger as a bare entity ID string. The migration now converts that to a list so multi-sensor logic works correctly on upgraded installs.

📦 Normalize string `entity_id` in service target resolution ([#570], [#571])

  • Service calls that passed `entity_id` as a plain string rather than a list were silently dropped. The resolver now coerces the string to a list before matching.

📦 Always show computed FOV preview at reveal depth 0 ([#596])

  • The FOV preview was suppressed when reveal depth was set to 0. It now shows the computed value regardless.

📦 Simplified Cover Geometry step description ([#564], [#581])

  • The Cover Geometry config step had a long description that duplicated information shown in the UI itself. Trimmed to the information that is not otherwise visible.
  • ---

📦 CI updates

  • Black bumped to ~=26.5. Dependabot now targets `develop` instead of `main`. Workflow run times reduced.
  • ---

📦 Compatibility

  • Home Assistant 2026.3.0+
  • ---

📦 References

  • [#258]: https://github.com/jrhubott/adaptive-cover-pro/issues/258
  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#563]: https://github.com/jrhubott/adaptive-cover-pro/issues/563
  • [#564]: https://github.com/jrhubott/adaptive-cover-pro/issues/564
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
  • [#567]: https://github.com/jrhubott/adaptive-cover-pro/issues/567
  • + 37 more
Adaptive Cover Pro ⛅ v2.28.0-beta.14v2.28.0-beta.14Pre-release
jrhubottjrhubott·1w ago·June 17, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • If you have a climate-mode cover with numeric temperature thresholds, open the climate status sensor's attributes and confirm the threshold values display correctly. This fix targets setups where thresholds were configured after #577 introduced the TemplateSelector — those entries can persist the value as a string like `"20"` or `"20,5"` rather than a float, which previously caused the sensor to crash on startup.
  • Confirm the sensor also handles Jinja2 template strings in threshold fields without crashing — template strings should now surface as no threshold value rather than raising.
  • All other cover behavior is unchanged. The `runtime_data` refactor is internal plumbing; verify that cover entities, sensors, switches, buttons, and numbers all load and update normally after a restart.

📦 Previously in this beta line

  • Vertical-drop shade model for the oscillating awning, anticipatory solar positioning across the throttle window (beta.13, #586, #616)
  • Position matching preserved for covers upgraded from earlier in the beta line; held position surfaced in the manual-override decision trace (beta.12, #591, #606, #608)

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#619]: https://github.com/jrhubott/adaptive-cover-pro/issues/619
  • [#620]: https://github.com/jrhubott/adaptive-cover-pro/pull/620
Adaptive Cover Pro ⛅ v2.28.0-beta.13v2.28.0-beta.13Pre-release
jrhubottjrhubott·1w ago·June 16, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • If you have an oscillating or drop awning, test at low sun elevations (morning/late afternoon, sun near the horizon). The cover should extend further to keep the window shaded — the new model solves actual lip-shadow geometry against the window bottom, so protection should improve noticeably at shallow sun angles where the old reach approximation fell short.
  • At high sun elevations, confirm the awning does not over-extend — the arc scan finds the smallest angle that reaches the protected boundary, so it should stay conservative when elevation is steep.
  • For solar anticipation, set a larger `CONF_DELTA_TIME` (e.g. 10–15 minutes) and watch a tracking cover during a period of fast sun movement. The cover should move to a position that stays protective through the end of the throttle window, not just at the moment of the command.
  • Confirm that covers with a 2-minute `CONF_DELTA_TIME` (the default minimum) still track normally — the anticipation window is narrow at the minimum and should behave identically to the previous logic.
  • Verify the decision trace reflects the anticipated position, not the raw current-sun position, when anticipation changes the commanded value.

📦 Vertical-drop shade model for the oscillating awning (#586)

  • Two new constants in `const.py`: `OSCILLATING_PROTECTED_BOUNDARY_DEFAULT` sets the reference height, and `OSCILLATING_ARC_SCAN_SAMPLES` (1801) controls scan resolution.

📦 Previously in this beta line

  • Position matching preserved for covers upgraded from earlier in the beta line, held position surfaced in the manual-override decision trace (beta.12, #591, #606, #608)
  • Custom-position sensor migration gate fixed (beta.11, #563, #607)
  • Solar-tracking position forecast in diagnostics, vertical extreme-gamma/high-elevation edge cases removed, legacy custom-position sensor migration added (beta.10, #563, #597, #598, #599, #600, #601)
  • FOV generate-from-measurements button replaces two-mode selector, position matching opt-in with disable-retry option, climate status sensor always exposes threshold setpoints and typed `inactive_reason` slugs, FOV preview shown at depth 0 (beta.9, #565, #589, #590, #591, #592, #595, #596)
  • Measurements-mode FOV sliders visible with editable suggested value, config summary shows [template] for Jinja2 threshold values (beta.8, #565, #577, #587, #588)
  • Force Override merged into Custom Positions, FOV formula corrected (beta.7, #563, #565, #584, #583)
  • Jinja2 templates in threshold fields and optional occupancy template (beta.6, #577, #578)
  • Configuration Summary translated to user's language (beta.4/beta.5, #258, #575, #576)
  • + 7 more

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#586]: https://github.com/jrhubott/adaptive-cover-pro/issues/586
  • [#616]: https://github.com/jrhubott/adaptive-cover-pro/issues/616
  • [#618]: https://github.com/jrhubott/adaptive-cover-pro/pull/618
  • [#617]: https://github.com/jrhubott/adaptive-cover-pro/pull/617
Adaptive Cover Pro ⛅ v2.28.0-beta.12v2.28.0-beta.12Pre-release
jrhubottjrhubott·1w ago·June 16, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • If you're upgrading from beta.11 or earlier with existing covers, restart Home Assistant and confirm that position matching is still enabled for those covers. Open each cover's config entry and verify `enable_position_matching` is `true` — the migration should have set it automatically.
  • Confirm that covers freshly added after the upgrade still default to position matching OFF (the new-install default is unchanged).
  • With a manual override active, open the decision trace in diagnostics and confirm the step shows both the held position and what solar would have computed — e.g. `manual override active — holding 35% (solar would-be 60%)`.
  • Verify that a held position of 0% is surfaced correctly (not suppressed or treated as absent).

📦 Previously in this beta line

  • Custom-position sensor migration gate fixed (beta.11, #563, #607)
  • Solar-tracking position forecast in diagnostics, vertical extreme-gamma/high-elevation edge cases removed, legacy custom-position sensor migration added (beta.10, #563, #597, #598, #599, #600, #601)
  • FOV generate-from-measurements button replaces two-mode selector, position matching opt-in with disable-retry option, climate status sensor always exposes threshold setpoints and typed `inactive_reason` slugs, FOV preview shown at depth 0 (beta.9, #565, #589, #590, #591, #592, #595, #596)
  • Measurements-mode FOV sliders visible with editable suggested value, config summary shows [template] for Jinja2 threshold values (beta.8, #565, #577, #587, #588)
  • Force Override merged into Custom Positions, FOV formula corrected (beta.7, #563, #565, #584, #583)
  • Jinja2 templates in threshold fields and optional occupancy template (beta.6, #577, #578)
  • Configuration Summary translated to user's language (beta.4/beta.5, #258, #575, #576)
  • Set-position covers can reach true 0% in solar tracking (beta.3/beta.4, #569, #572)
  • + 6 more

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#591]: https://github.com/jrhubott/adaptive-cover-pro/issues/591
  • [#606]: https://github.com/jrhubott/adaptive-cover-pro/issues/606
  • [#608]: https://github.com/jrhubott/adaptive-cover-pro/issues/608
Adaptive Cover Pro ⛅ v2.28.0-beta.11v2.28.0-beta.11Pre-release
jrhubottjrhubott·1w ago·June 15, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • If you had a custom-position slot configured before beta.7 (#563), upgrade to beta.11 and confirm your previously-configured sensor now appears in the multi-select "Sensors" field for that slot.
  • Verify no custom-position data is lost: the migration is additive — the legacy `custom_position_sensor_N` key is kept alongside the new list key.
  • Confirm that a rollback to beta.10 leaves the config entry intact and functional.
  • Check that custom-position slots configured after beta.7 (which already use the list key) are unchanged by the migration.

📦 Previously in this beta line

  • Solar-tracking position forecast in diagnostics, vertical extreme-gamma/high-elevation edge cases removed, legacy custom-position sensor migration added (beta.10, #563, #597, #598, #599, #600, #601)
  • FOV generate-from-measurements button replaces two-mode selector, position matching opt-in with disable-retry option, climate status sensor always exposes threshold setpoints and typed `inactive_reason` slugs, FOV preview shown at depth 0 (beta.9, #565, #589, #590, #591, #592, #595, #596)
  • Measurements-mode FOV sliders visible with editable suggested value, config summary shows [template] for Jinja2 threshold values (beta.8, #565, #577, #587, #588)
  • Force Override merged into Custom Positions, FOV formula corrected (beta.7, #563, #565, #584, #583)
  • Jinja2 templates in threshold fields and optional occupancy template (beta.6, #577, #578)
  • Configuration Summary translated to user's language (beta.4/beta.5, #258, #575, #576)
  • Set-position covers can reach true 0% in solar tracking (beta.3/beta.4, #569, #572)
  • Same-position gate reverted to exact equality, restoring 1–3% tracking moves (beta.3, #567, #574)
  • + 5 more

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#563]: https://github.com/jrhubott/adaptive-cover-pro/issues/563
  • [#607]: https://github.com/jrhubott/adaptive-cover-pro/issues/607
Adaptive Cover Pro ⛅ v2.28.0-beta.10v2.28.0-beta.10Pre-release
jrhubottjrhubott·1w ago·June 15, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • Confirm the diagnostics panel includes a `position_forecast` section and that the forecast steps change as the sun moves through the day.
  • Verify the `position_forecast` description field explicitly identifies it as a solar-tracking projection (not the live decision).
  • Check that vertical blinds no longer briefly snap fully closed when the sun enters the FOV edge at high elevation and extreme horizontal angle.
  • Confirm covers configured before the multi-sensor custom-position update (pre-#563) still show their legacy sensor in the slot after upgrading.
  • Verify `set_position_limits` and custom-position options behave identically to beta.9 for existing configs.
  • Confirm the diagnostics `decision_trace` section is unaffected by the new forecast field.

🗑️ Vertical extreme-gamma and high-elevation edge cases removed (#598, #599, #600, #601)

  • A spurious fully-closed position sample appeared right at the FOV-entry edge for a high-elevation sun at an extreme horizontal angle (gamma). The fix shipped in two steps.

🧪 🧪 Testing

  • 4,496 tests passing.

📦 Previously in this beta line

  • FOV generate-from-measurements button replaces two-mode selector, position matching opt-in with disable-retry option, climate status sensor always exposes threshold setpoints and typed `inactive_reason` slugs, FOV preview shown at depth 0 (beta.9, #565, #589, #590, #591, #592, #595, #596)
  • Measurements-mode FOV sliders visible with editable suggested value, config summary shows [template] for Jinja2 threshold values (beta.8, #565, #577, #587, #588)
  • Force Override merged into Custom Positions, FOV formula corrected (beta.7, #563, #565, #584, #583)
  • Jinja2 templates in threshold fields and optional occupancy template (beta.6, #577, #578)
  • Configuration Summary translated to user's language (beta.4/beta.5, #258, #575, #576)
  • Set-position covers can reach true 0% in solar tracking (beta.3/beta.4, #569, #572)
  • Same-position gate reverted to exact equality, restoring 1–3% tracking moves (beta.3, #567, #574)
  • String entity_id in service targets now normalizes correctly (beta.3, #570, #571)
  • + 4 more

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements. The pandas dependency bumps to ~=2.3 but introduces no new minimum HA version.

📦 References

  • [#347]: https://github.com/jrhubott/adaptive-cover-pro/issues/347
  • [#479]: https://github.com/jrhubott/adaptive-cover-pro/issues/479
  • [#480]: https://github.com/jrhubott/adaptive-cover-pro/issues/480
  • [#481]: https://github.com/jrhubott/adaptive-cover-pro/issues/481
  • [#563]: https://github.com/jrhubott/adaptive-cover-pro/issues/563
  • [#573]: https://github.com/jrhubott/adaptive-cover-pro/issues/573
  • [#597]: https://github.com/jrhubott/adaptive-cover-pro/issues/597
  • [#598]: https://github.com/jrhubott/adaptive-cover-pro/issues/598
  • + 4 more
Adaptive Cover Pro ⛅ v2.28.0-beta.9v2.28.0-beta.9Pre-release
jrhubottjrhubott·2w ago·June 13, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • Open the config flow for a vertical blind or venetian. Confirm the "Generate field of view from measurements" button appears instead of the old mode dropdown. Tick it, submit, and verify that `fov_left` and `fov_right` populate from your window dimensions. Confirm the toggle clears after submit so you can adjust either angle freely.
  • At reveal depth 0 (the default), open the measurements step and confirm the `{computed_fov}` preview renders in the toggle's help text rather than being blank.
  • If you have an existing config that saved a `fov_mode` value: edit and re-save the entry; verify no error and that the legacy key is silently dropped.
  • In Cover Settings, locate "Enable position matching" (default off). Leave it off, command the cover, and verify it is sent once and not retried if it settles slightly off — and that a noticeable delta triggers manual override. Then enable it and confirm retry behaviour resumes.
  • Move "Position match tolerance" to the Position Settings step and confirm it appears there rather than its previous location.
  • Open Developer Tools → States for `sensor.climate_status`. Confirm the attributes always include `temp_low`, `temp_high`, `temp_summer_outside` (rounded to one decimal) and `inactive_reason`, even when the climate handler is not active.
  • Verify `inactive_reason` cycles through meaningful slugs (`active`, `mode_off`, `outside_time_window`, `thresholds_not_met`, `other_mode_active`, `readings_unavailable`) as you change conditions.
  • + 1 more

📦 🔧 Internal

  • `_as_optional(marker)` moved from `cover_types/blind.py` to `cover_types/base.py` — available to all cover type policies.
  • `supports_fov_compute` replaces the removed `supports_fov_mode` ClassVar on the base policy.
  • `inactive_reason` and `inactive_reason_from_result` added to `pipeline/handlers/climate.py`; `_SLUG_TO_PROSE` and `_PROSE_TO_SLUG` are the single source of truth for slug-to-description mapping.
  • `_enable_position_matching` instance attribute and `enable_position_matching` property/setter added to `managers/cover_command/__init__.py`.
  • `CONF_ENABLE_POSITION_MATCHING` added to `FIELD_VALIDATORS` (as `_bool_v()`) and `_SECTION_POSITION_LIMITS` in `services/options_service.py`.

🧪 🧪 Testing

  • 4,489 tests passing.

📦 Previously in this beta line

  • String entity_id in service targets now normalizes correctly (beta.3, #570, #571): Service calls with a plain string `entity_id` target (rather than a list) were not normalizing correctly; fixed.
  • FOV mode persisted on re-render, fixing save loop (beta.2/beta.3, #565): The FOV mode selection was lost on re-render, causing users to get stuck in a loop; it is now persisted correctly.
  • Low-sun edge case returns closed position (beta.1, #559, #562): When the sun is below the geometry horizon the cover returns closed rather than an undefined position.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#258]: https://github.com/jrhubott/adaptive-cover-pro/issues/258
  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#563]: https://github.com/jrhubott/adaptive-cover-pro/issues/563
  • [#564]: https://github.com/jrhubott/adaptive-cover-pro/issues/564
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
  • [#567]: https://github.com/jrhubott/adaptive-cover-pro/issues/567
  • + 20 more
Adaptive Cover Pro ⛅ v2.28.0-beta.8v2.28.0-beta.8Pre-release
jrhubottjrhubott·2w ago·June 12, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • If you have a cover configured with Measurements mode (reveal width and depth), re-open the Cover Geometry step. Confirm the `CONF_FOV_LEFT` and `CONF_FOV_RIGHT` sliders are now visible, pre-populated with the derived angle. Type a different value into one slider and save — confirm the override is stored; leave the other slider untouched and confirm the derived value is used.
  • With a 2 m wide window and 0.5 m reveal depth the suggested angle should be approximately 76°. Verify that value appears.
  • If you use Jinja2 templates in any threshold field (`CONF_LUX_THRESHOLD`, `CONF_IRRADIANCE_THRESHOLD`, `CONF_CLOUD_COVERAGE_THRESHOLD`, `CONF_WEATHER_WIND_SPEED_THRESHOLD`, `CONF_WEATHER_RAIN_THRESHOLD`, `CONF_TEMP_LOW`, `CONF_TEMP_HIGH`, `CONF_OUTSIDE_THRESHOLD`), open the Configuration Summary and confirm the entry reads `[template]` rather than the raw template source.
  • If you had Force Override configured, open the Custom Positions step and confirm slot 5 is populated at priority 100 with your previous trigger sensor and target position. Activate the trigger sensor and confirm the cover moves to the configured position; deactivate it and confirm the cover returns to its calculated position.
  • Confirm that a priority-100 custom position slot commands the cover even when the current time is outside the configured start/end window — this is the safety-bypass behaviour carried over from Force Override.
  • Configure two sensors on a single custom position slot and confirm that either sensor alone is sufficient to trigger the slot (OR logic).
  • Configure a Jinja2 condition template on a custom position slot and confirm the slot activates when the template renders truthy.
  • + 1 more

📦 Measurements-mode FOV sliders now visible with editable suggested value (#565, #588)

  • Typed overrides win: if the user changes either slider, that value is stored and used instead of the derived angle. The two sides can be overridden independently.

📦 🔧 Internal

  • `_get_sun_tracking_schema` gained a `source_config` parameter forwarded to `CoverTypePolicy.fov_mode_schema`; `BlindPolicy.fov_mode_schema` uses it to derive and expose the `suggested_value` for both FOV sliders in `FovMode.MEASUREMENTS`.
  • `_thresh_display` helper in `_build_config_summary` centralises the template-detection guard; `fragments.template_value` added to `_SUMMARY_LABELS_EN` and all three `summary_i18n` files.
  • `CUSTOM_POSITION_SLOT_NUMBERS` expanded from `(1, 2, 3, 4)` to `(1, 2, 3, 4, 5)`; priority slider ceiling raised from 99 to 100.
  • `mirror_legacy_slot_sensor_keys` mirrors the first sensor from the multi-sensor list back into the legacy single-sensor key on every save for rollback fidelity.
  • `DEFAULT_TEMPLATE_COMBINE_MODE` replaces the motion-specific constant as the shared default for all combine-mode config fields.
  • `async_prune_legacy_sensor_entities_v2` prunes the orphaned "Force Override Triggers" diagnostic sensor entity on upgrade.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#258]: https://github.com/jrhubott/adaptive-cover-pro/issues/258
  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#563]: https://github.com/jrhubott/adaptive-cover-pro/issues/563
  • [#564]: https://github.com/jrhubott/adaptive-cover-pro/issues/564
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
  • [#567]: https://github.com/jrhubott/adaptive-cover-pro/issues/567
  • + 16 more
Adaptive Cover Pro ⛅ v2.28.0-beta.7v2.28.0-beta.7Pre-release
jrhubottjrhubott·2w ago·June 12, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • If you had Force Override configured, open the Custom Positions step and confirm slot 5 is populated at priority 100 with your previous trigger sensor and target position. Activate the trigger sensor and confirm the cover moves to the configured position; deactivate it and confirm the cover returns to its calculated position.
  • Confirm that a priority-100 custom position slot commands the cover even when the current time is outside the configured start/end window — this is the safety-bypass behaviour carried over from Force Override.
  • Configure two sensors on a single custom position slot and confirm that either sensor alone is sufficient to trigger the slot (OR logic).
  • Configure a Jinja2 condition template on a custom position slot and confirm the slot activates when the template renders truthy.
  • Call the `set_force_override` service from an existing automation and confirm the cover still moves to the configured position; check the log for the deprecation warning.
  • If you have a cover configured with Measurements mode (reveal width and depth), re-open the Cover Geometry step and verify the displayed FOV angle looks correct — the old formula gave roughly half the intended value.

📦 🔧 Internal

  • `CUSTOM_POSITION_SLOT_NUMBERS` expanded from `(1, 2, 3, 4)` to `(1, 2, 3, 4, 5)`; priority slider ceiling raised from 99 to 100.
  • `mirror_legacy_slot_sensor_keys` mirrors the first sensor from the multi-sensor list back into the legacy single-sensor key on every save for rollback fidelity.
  • `DEFAULT_TEMPLATE_COMBINE_MODE` replaces the motion-specific constant as the shared default for all combine-mode config fields.
  • `async_prune_legacy_sensor_entities_v2` prunes the orphaned "Force Override Triggers" diagnostic sensor entity on upgrade.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#258]: https://github.com/jrhubott/adaptive-cover-pro/issues/258
  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#563]: https://github.com/jrhubott/adaptive-cover-pro/issues/563
  • [#564]: https://github.com/jrhubott/adaptive-cover-pro/issues/564
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
  • [#567]: https://github.com/jrhubott/adaptive-cover-pro/issues/567
  • + 14 more
Adaptive Cover Pro ⛅ v2.28.0-beta.6v2.28.0-beta.6Pre-release
jrhubottjrhubott·2w ago·June 11, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • Configure a temperature threshold (e.g. `CONF_TEMP_HIGH`) as a Jinja2 template — `{{ states('sensor.outdoor_temp') | float }}` — save, and confirm the cover responds to the rendered value rather than a hard-coded number.
  • Configure a lux or wind-speed threshold as a template and confirm it resolves each coordinator cycle.
  • Enter a malformed template and confirm the cover falls back to the field's default and that no error is logged on every cycle — only once per failure transition.
  • Open Diagnostics for a cover with templated thresholds and confirm the payload shows the raw template string alongside its last-resolved value.
  • Add an occupancy condition template to Motion Override; confirm a truthy render counts as presence and a falsy render correctly starts the motion timeout rather than holding the cover open.
  • Create a new cover, fill every field including lux threshold, wind direction tolerance, and custom positions, save, and confirm nothing is silently dropped.

📦 Jinja2 templates in numeric threshold fields (#577, #578)

  • All three threshold-template flavours ship English, German, and French translations.

📦 🔧 Internal

  • Single-source `TEMPLATABLE_KEYS`: the frozenset in `config_fields.py` is the only place the templatable key set is defined; config-flow, service validators, and `TemplateResolver` all consume it — no parallel list to drift.
  • `_num_or` snapshot guard: `config_types.py`'s `_num_or` helper coerces any non-numeric raw value (including an unrendered template string) to the field's numeric default at `RuntimeConfig.from_options` time, so the calculation engine always receives a float.
  • Once-per-transition failure logging: `TemplateResolver` tracks previously failing keys and logs only on the transition from success to failure (and on recovery), not every coordinator cycle.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#258]: https://github.com/jrhubott/adaptive-cover-pro/issues/258
  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
  • [#567]: https://github.com/jrhubott/adaptive-cover-pro/issues/567
  • [#569]: https://github.com/jrhubott/adaptive-cover-pro/issues/569
  • [#570]: https://github.com/jrhubott/adaptive-cover-pro/issues/570
  • + 9 more
Adaptive Cover Pro ⛅ v2.28.0-beta.5v2.28.0-beta.5Pre-release
jrhubottjrhubott·2w ago·June 11, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • Configure a temperature threshold (e.g. `CONF_TEMP_HIGH`) as a Jinja2 template — `{{ states('sensor.outdoor_temp') | float }}` — save, and confirm the cover responds to the rendered value rather than a hard-coded number.
  • Configure a lux or wind-speed threshold as a template and confirm it resolves each coordinator cycle.
  • Enter a malformed template (e.g. `{{ states('sensor.missing') | bad_filter }}`) and confirm the cover falls back to the field's default rather than breaking or logging an error every cycle.
  • Open Diagnostics for a cover with templated thresholds and confirm the diagnostic payload shows the raw template string alongside its last-resolved value.
  • Add an occupancy condition template to Motion Override and confirm a truthy render counts as presence — the cover should behave as if a motion sensor is active.
  • Confirm a falsy occupancy template correctly starts the motion timeout rather than holding the cover open.

📦 Jinja2 templates in numeric threshold fields (#577)

  • All three threshold-template flavours ship English/German/French translations.

📦 🔧 Internal

  • Single-source `TEMPLATABLE_KEYS`: the frozenset in `config_fields.py` is the only place the set of templatable keys is defined; config-flow, service validators, and `TemplateResolver` all consume it.
  • `_num_or` snapshot guard: `config_types.py`'s `_num_or` helper coerces any non-numeric raw value (including an unrendered template string) to the field's numeric default at `RuntimeConfig.from_options` time, so the calculation engine is always handed a float.
  • Once-per-transition failure logging: `TemplateResolver._render` tracks the set of previously failing keys and logs only on the transition from success to failure (and recovery back), not every cycle.

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#258]: https://github.com/jrhubott/adaptive-cover-pro/issues/258
  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
  • [#567]: https://github.com/jrhubott/adaptive-cover-pro/issues/567
  • [#569]: https://github.com/jrhubott/adaptive-cover-pro/issues/569
  • [#570]: https://github.com/jrhubott/adaptive-cover-pro/issues/570
  • + 6 more
Adaptive Cover Pro ⛅ v2.28.0-beta.4v2.28.0-beta.4Pre-release
jrhubottjrhubott·2w ago·June 11, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • German/French users: switch your HA language and open the config or options flow for any cover. Confirm the Configuration Summary renders in your language — priority rules, position limits, warnings, headers, the cover-type label, and dimension lines should all appear translated.
  • Confirm the English fallback works: if a translation key is missing (e.g. a custom HA locale), the summary should fall back to English rather than showing a blank or an error.
  • Configure a window using `MEASUREMENTS` mode and confirm the mode sticks — previous betas had a save loop where the mode selection didn't persist on re-render. This is now fixed.
  • Verify small solar tracking adjustments (1–3%) are sent to the cover. beta.2 silently swallowed these moves; they should now go through.
  • If you have a set-position cover with a geometry path that previously stopped short of fully closed, confirm it reaches 0% during solar tracking.
  • Try the FOV-mode selector end-to-end: switch between `ANGLES` and `MEASUREMENTS` mode, save, reload, and confirm the saved mode is the one that loads.
  • Verify service calls that pass a string `entity_id` target resolve correctly, especially for sunset/sunrise time entity services.

📦 Configuration Summary translated to user's language (#258, #575, #576)

  • The narrative Configuration Summary shown in the config/options flow is now fully translated. This landed in two steps:

📦 Write-gating, geometry caching, and sun availability guard (#543)

  • Shipped in beta.1, carried here for completeness:
  • Write-gating: `_acp_render_signature()` detects when a calculated position matches the last written value and skips the HA state write, reducing redundant entity updates.
  • Geometry cache: Pure geometry helpers decorated with `@lru_cache(maxsize=512)` eliminate repeated identical calculations across update cycles.
  • Sun availability guard: A guard on `sun.sun` suppresses solar tracking when the sun entity is unavailable, preventing stale or error-state data from driving cover commands.

🧪 🧪 Testing

  • 4,327 tests passing (up from 4,316 at beta.3).

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#258]: https://github.com/jrhubott/adaptive-cover-pro/issues/258
  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
  • [#567]: https://github.com/jrhubott/adaptive-cover-pro/issues/567
  • [#569]: https://github.com/jrhubott/adaptive-cover-pro/issues/569
  • [#570]: https://github.com/jrhubott/adaptive-cover-pro/issues/570
  • + 5 more
Adaptive Cover Pro ⛅ v2.28.0-beta.3v2.28.0-beta.3Pre-release
jrhubottjrhubott·2w ago·June 10, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • Configure a window using `MEASUREMENTS` mode and confirm the mode sticks — previous betas had a save loop where the mode selection didn't persist on re-render. This is now fixed.
  • Verify small solar tracking adjustments (1–3%) are sent to the cover. beta.2 silently swallowed these moves; they should now go through.
  • If you have a set-position cover with a geometry path that previously stopped short of fully closed, confirm it reaches 0% during solar tracking.
  • Try the FOV-mode selector end-to-end: switch between `ANGLES` and `MEASUREMENTS` mode, save, reload, and confirm the saved mode is the one that loads.
  • Verify service calls that pass a string `entity_id` target resolve correctly, especially for sunset/sunrise time entity services.

📦 Write-gating, geometry caching, and sun availability guard (#543)

  • Shipped in beta.1, carried here for completeness:
  • Write-gating: `_acp_render_signature()` detects when a calculated position matches the last written value and skips the HA state write, reducing redundant entity updates.
  • Geometry cache: Pure geometry helpers decorated with `@lru_cache(maxsize=512)` eliminate repeated identical calculations across update cycles.
  • Sun availability guard: A guard on `sun.sun` suppresses solar tracking when the sun entity is unavailable, preventing stale or error-state data from driving cover commands.

🧪 🧪 Testing

  • 4,316 tests passing (up from ~4,276 at beta.2).

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
  • [#567]: https://github.com/jrhubott/adaptive-cover-pro/issues/567
  • [#569]: https://github.com/jrhubott/adaptive-cover-pro/issues/569
  • [#570]: https://github.com/jrhubott/adaptive-cover-pro/issues/570
  • [#571]: https://github.com/jrhubott/adaptive-cover-pro/issues/571
  • + 2 more
Adaptive Cover Pro ⛅ v2.28.0-beta.2v2.28.0-beta.2Pre-release
jrhubottjrhubott·2w ago·June 10, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • Try the new FOV-mode selector: configure a window using `MEASUREMENTS` mode (reveal depth → `fov_from_reveal()`) and confirm the computed angles match your physical window.
  • Verify you can now switch to and save Measurements mode without the frontend blocking the save — this was broken in beta.1 and is fixed in beta.2.
  • If your installation uses imperial units, toggle FOV mode and confirm the shaded-area depth value stays stable (does not compound) across rerenders.
  • Verify the low-sun behavior change: at very low sun elevations the cover now returns 0 (fully closed) rather than the previous h_win value (fully open). Confirm this is correct for your installation.
  • Geometry caching and write-gating are internal changes — normal operation should be unchanged. Flag any unexpected behavior.

📦 Measurements mode config-flow save blocked and imperial values compounding (#565)

  • Two bugs found during testing of the beta.1 FOV-mode selector, both in the config flow:

📦 Write-gating, geometry caching, and sun availability guard (#543)

  • Three robustness and performance improvements shipped together:
  • Write-gating: `_acp_render_signature()` detects when a calculated position is identical to the last written value and skips the HA state write, reducing redundant entity updates.
  • Geometry cache: Pure geometry helpers are decorated with `@lru_cache(maxsize=512)`, eliminating repeated identical calculations across update cycles.
  • Sun availability guard: A guard on `sun.sun` suppresses solar tracking when the sun entity is unavailable, preventing stale or error-state data from driving cover commands.

🧪 🧪 Testing

  • Test suite grows from 4,214 to 4,276 (+62 net new tests across both betas). New modules:
  • `test_config_flow_fov_mode.py` / `test_fov_mode.py` — FOV-mode selector and `fov_from_reveal()` calculation
  • `test_entity_write_gating.py` — write-gating behavior
  • `test_geometry_cache.py` — cache correctness and hit behavior
  • `test_sun_unavailable_guard.py` — solar tracking suppression when sun entity unavailable
  • `test_blind_fov_fields_are_optional`, `test_awning_fov_fields_stay_required`, `test_measurements_mode_submittable_without_fov`, `test_imperial_shaded_area_stable_across_mode_switch_rerender` — config-flow correctness for the beta.2 fixes

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
Adaptive Cover Pro ⛅ v2.28.0-beta.1v2.28.0-beta.1Pre-release
jrhubottjrhubott·2w ago·June 10, 2026
GitHub

📦 🎯 Highlights

  • Beta — please test and report back.
  • Try the new FOV-mode selector: configure a window using `MEASUREMENTS` mode (reveal depth → `fov_from_reveal()`) and confirm the computed angles match your physical window.
  • Verify the low-sun behavior change: at very low sun elevations the cover now returns 0 (fully closed) rather than the previous h_win value (fully open). Confirm this is correct for your installation.
  • Geometry caching and write-gating are internal changes — normal operation should be unchanged. Flag any unexpected behavior.

📦 Write-gating, geometry caching, and sun availability guard (#543)

  • Three robustness and performance improvements shipped together:
  • Write-gating: `_acp_render_signature()` detects when a calculated position is identical to the last written value and skips the HA state write, reducing redundant entity updates.
  • Geometry cache: Pure geometry helpers are decorated with `@lru_cache(maxsize=512)`, eliminating repeated identical calculations across update cycles.
  • Sun availability guard: A guard on `sun.sun` suppresses solar tracking when the sun entity is unavailable, preventing stale or error-state data from driving cover commands.

🧪 🧪 Testing

  • Test suite grows from 4,214 to 4,271 (+57 net new tests). New modules:
  • `test_config_flow_fov_mode.py` / `test_fov_mode.py` — FOV-mode selector and `fov_from_reveal()` calculation
  • `test_entity_write_gating.py` — write-gating behavior
  • `test_geometry_cache.py` — cache correctness and hit behavior
  • `test_sun_unavailable_guard.py` — solar tracking suppression when sun entity unavailable

📦 Compatibility

  • Requires Home Assistant 2026.3.0+. No new coupled-component requirements.

📦 References

  • [#543]: https://github.com/jrhubott/adaptive-cover-pro/issues/543
  • [#559]: https://github.com/jrhubott/adaptive-cover-pro/issues/559
  • [#562]: https://github.com/jrhubott/adaptive-cover-pro/issues/562
  • [#565]: https://github.com/jrhubott/adaptive-cover-pro/issues/565
Adaptive Cover Pro ⛅ v2.27.0v2.27.0
jrhubottjrhubott·2w ago·June 9, 2026
GitHub

📦 🎯 Highlights

  • Forecast/runtime parity ([#556]): the position forecast now calls the same snapshot-free primitives as the live pipeline, so min/max limits, floor-at-1%, rounding, and movement minimization all match runtime by construction.
  • Opt-in movement minimization ([#555]): a new `CONF_MINIMIZE_MOVEMENTS` option quantizes sun-tracking positions to a configurable number of coverage steps, reducing small cover movements without changing average coverage.
  • Sun-state classification ([#552], [#553], [#554]): a `SunState` enum (`HITTING`, `IN_FOV_NOT_VALID`, `OUTSIDE_FOV`) is computed per cycle, surfaced in diagnostics, and exposed as `sun_state` on the `decision_trace` sensor attributes.
  • False manual override suppressed ([#546], [#551]): covers returning from unavailable no longer trigger a spurious manual override when no prior command target exists.
  • ---

📦 Forecast/runtime parity ([#556])

  • `forecast.py` — now calls shared primitives instead of duplicating position math
  • `pipeline/helpers.py` — `solar_position_from_geometry`, `default_position_with_limits`, `apply_config_limits` extracted here

📦 Opt-in movement minimization ([#555])

  • The feature applies to tilt, blind, and awning cover types. It is surfaced in the config flow options UI, in `services.yaml` for service calls, and in all three translation files.
  • `position_utils.py` — `quantize_to_coverage_steps()`
  • `pipeline/handlers/solar.py` — applies quantization when `CONF_MINIMIZE_MOVEMENTS` is enabled
  • `services.yaml` — `CONF_MINIMIZE_MOVEMENTS`, `CONF_MAX_COVERAGE_STEPS` exposed
  • `translations/en.json`, `translations/de.json`, `translations/fr.json` — labels and descriptions

📦 Sun-state classification in diagnostics and decision trace ([#552], [#553], [#554])

  • `engine/sun_geometry.py` — `SunState` enum, `in_fov()` method
  • `diagnostics/builder.py` — classification included in diagnostics output
  • `sensor.py` — `sun_state` attribute on `decision_trace` sensor
  • ---

🐛 🐛 Fixes

  • ---

📦 🔧 Internal

  • ---

🧪 🧪 Testing

  • 4214 tests passing
  • ---

📦 Compatibility

  • Home Assistant 2026.3.0+
  • ---

📦 References

  • [#544]: https://github.com/jrhubott/adaptive-cover-pro/issues/544
  • [#545]: https://github.com/jrhubott/adaptive-cover-pro/issues/545
  • [#546]: https://github.com/jrhubott/adaptive-cover-pro/issues/546
  • [#551]: https://github.com/jrhubott/adaptive-cover-pro/issues/551
  • [#552]: https://github.com/jrhubott/adaptive-cover-pro/issues/552
  • [#553]: https://github.com/jrhubott/adaptive-cover-pro/issues/553
  • [#554]: https://github.com/jrhubott/adaptive-cover-pro/issues/554
  • [#555]: https://github.com/jrhubott/adaptive-cover-pro/issues/555
  • + 1 more
Adaptive Cover Pro ⛅ v2.26.0v2.26.0
jrhubottjrhubott·3w ago·June 7, 2026
GitHub

📦 🎯 Highlights

  • New oscillating (drop-arm) awning cover type ([#412]): arm-sweep geometry, three new config fields, appears automatically in the cover-type picker.
  • Declarative config-field registry (`config_fields.py` + `FieldSpec`): every field's type, range, selector, and default defined once; `OPTION_RANGES` is derived from it rather than being defined separately in `const.py`.
  • `CoverTypePolicy` section API: `build_section_schema()`, `section_order()`, `live_option_keys()`, `extra_field_keys()`, `disabled_config_keys()`, plus `__init_subclass__` auto-registration — cover types opt in with `register=True` and appear in `SENSOR_TYPE_MENU` without touching `config_flow.py`.
  • Schedule window exposed: `schedule_start` and `schedule_end` now published as attributes on the `control_status` sensor ([#538]).
  • Sunset/sunrise boundary comparisons fixed for non-UTC timezones, including a follow-up re-anchoring fix for next-event calculations ([#531]).
  • ---

📦 Oscillating (drop-arm) awning cover type ([#412])

  • `OscillatingAwningPolicy` sets `register=True`, so `CoverType.OSCILLATING_AWNING` appears in the cover-type picker the moment the policy is imported. No manual entry in `SENSOR_TYPE_MENU` required.
  • `engine/covers/oscillating.py` — arm-sweep geometry and position mapping
  • `cover_types/oscillating_awning.py` — `OscillatingAwningPolicy`, `OscillatingConfig`
  • `services/options_service.py` — validators for the new fields
  • `translations/en.json`, `translations/de.json`, `translations/fr.json` — labels and descriptions for all four new fields

📦 Schedule window attributes ([#538], [#539])

  • ---

🐛 🐛 Fixes

  • ---

📦 🔧 Internal

  • ---

🧪 🧪 Testing

  • 3938 tests passing
  • ---

📦 Compatibility

  • Home Assistant 2026.3.0+
  • ---

📦 References

  • [#412]: https://github.com/jrhubott/adaptive-cover-pro/issues/412
  • [#525]: https://github.com/jrhubott/adaptive-cover-pro/pull/525
  • [#528]: https://github.com/jrhubott/adaptive-cover-pro/pull/528
  • [#530]: https://github.com/jrhubott/adaptive-cover-pro/issues/530
  • [#531]: https://github.com/jrhubott/adaptive-cover-pro/issues/531
  • [#532]: https://github.com/jrhubott/adaptive-cover-pro/pull/532
  • [#533]: https://github.com/jrhubott/adaptive-cover-pro/pull/533
  • [#534]: https://github.com/jrhubott/adaptive-cover-pro/issues/534
  • + 4 more
Adaptive Cover Pro ⛅ v2.25.3v2.25.3
jrhubottjrhubott·3w ago·June 6, 2026
GitHub

📦 Sunset/sunrise boundary now compared in UTC ([#531], [#533], [#535])

  • `helpers.py` gains `_local_naive_to_utc_naive`, which converts a naive local wall-clock time to naive UTC before the boundary comparison.
  • `compute_effective_default` now calls this helper, so both sides of the comparison are in the same frame.
  • Regression tests cover positive and negative UTC offsets.
  • ---

📦 Compatibility

  • Home Assistant 2026.3.0+
  • ---

📦 References

  • [#531]: https://github.com/jrhubott/adaptive-cover-pro/issues/531
  • [#533]: https://github.com/jrhubott/adaptive-cover-pro/pull/533
  • [#535]: https://github.com/jrhubott/adaptive-cover-pro/pull/535