Skip to content

Adaptive Cover Pro ⛅ v2.28.0-beta.7

Pre-release
Pre-release

Choose a tag to compare

@jrhubott jrhubott released this 12 Jun 04:05
· 156 commits to main since this release

ℹ Using release notes from: release_notes/v2.28.0-beta.7.md
Beta.7 ships the Force Override merger — Force Override no longer exists as a standalone config step; its safety semantics are now expressed through Custom Positions slot 5 at priority 100. Existing configurations migrate automatically via the v3.2 minor migration. Also in this build: a correction to the Measurements-mode FOV formula that previously produced roughly half the intended angular coverage, and a cosmetic simplification of the Cover Geometry config step description.

🎯 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.

✨ Features

Force Override merged into Custom Positions (#563, #584)

The standalone Force Override config step is removed. Its functionality — a sensor-triggered override that bypasses normal automation logic unconditionally — is now expressed as Custom Positions slot 5 at priority 100 (CUSTOM_POSITION_SAFETY_PRIORITY). Any custom position slot whose priority is set to 100 inherits the full safety semantics: it commands the cover outside the configured start/end time window and bypasses both the delta-position and delta-time send gates. The PipelineResult.is_safety flag signals this condition to the command service, which treats it identically to how the old ForceOverrideHandler was treated.

Migration is automatic. The v3.2 minor-version migration (_merge_force_override_into_slot_5) runs on first load after upgrading and copies the legacy force_override_sensors and force_override_position values into slot 5 with priority=100. The legacy keys are left intact for rollback — downgrading to the previous release will restore the old Force Override UI and behaviour without any manual intervention.

Each custom position slot now has a richer trigger model. Beyond the existing single-sensor key, each slot supports a multi-sensor list (custom_position_sensors_N) with OR logic, an optional Jinja2 condition template (custom_position_template_N), and a combine mode (custom_position_template_mode_N, OR or AND) governing how the template result is combined with the sensor states. The legacy single-sensor key (custom_position_sensor_N) is preserved as a rollback mirror — custom_position_slot_sensors reads the list and falls back to the legacy key when the list is absent. mirror_legacy_slot_sensor_keys writes the first sensor from the list back into the legacy key on every save, keeping the rollback path intact.

The set_force_override service is retained for one release as a deprecation shim. It now writes slot 5 at safety priority and logs a deprecation warning; existing automations will continue to function without changes. ControlMethod.FORCE and ControlStatus.FORCE_OVERRIDE_ACTIVE are kept as deprecated enum members so that the companion Lovelace card and any external diagnostics consumers that reference those values do not break immediately.

CUSTOM_POSITION_SLOT_NUMBERS is now (1, 2, 3, 4, 5) and the priority slider ceiling is 100. The "Force Override Triggers" diagnostic sensor entity is retired; async_prune_legacy_sensor_entities_v2 handles orphan cleanup on upgrade.

Cover Geometry step description simplified (#564, #581)

The Cover Geometry config step description is now concise. The previous text enumerated the individual fields inline, duplicating the UI labels already visible on the same screen. That redundant field list is removed in English, German, and French.

🐛 Fixes

Measurements-mode FOV was half the intended value (#565, #583)

fov_from_reveal in engine/sun_geometry.py computed the field-of-view angle as atan(half_width / depth) instead of atan(width / depth). With a 2 m wide window and 0.5 m reveal depth, the old formula returned ≈ 63° where ≈ 76° is geometrically correct. In practice, users who configured FOV via Measurements mode were tracking a window roughly half as wide as their actual opening, causing the integration to start shading too early and stop too late. After upgrading, re-open the Cover Geometry step for any cover that uses Measurements mode and verify the displayed FOV angle reflects your actual window dimensions.

🔧 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.

🧪 Testing

4,444 tests passing. New coverage in tests/test_safety_priority_custom_position.py (325 lines) — priority-100 slot safety semantics, release edge cases, min-mode behaviour, and pipeline integration.

Previously in this beta line

Jinja2 templates in threshold fields and optional occupancy template (beta.6, #577, #578): Nine numeric threshold config fields accept Jinja2 templates; TemplateResolver renders them per coordinator cycle with once-per-transition failure logging. A new optional CONF_MOTION_TEMPLATE field on Motion Override counts as occupancy when it renders truthy.

Configuration Summary translated to user's language (beta.4/beta.5, #258, #575, #576): The narrative Configuration Summary in the config/options flow renders in the user's HA language with English, German, and French shipped. The config_summary bundle (now under summary_i18n/) carries translated leaves for every summary fragment.

Set-position covers can reach true 0% in solar tracking (beta.3/beta.4, #569, #572): A geometry oversight prevented some set-position covers from closing fully during solar tracking. Covers that support arbitrary position commands now close to true 0%.

Same-position gate reverted to exact equality, restoring 1–3% tracking moves (beta.3, #567, #574): A tolerance band silently dropped small solar tracking adjustments. The gate reverts to exact equality; movement hysteresis is preserved in the reconcile path.

String entity_id in service targets now normalizes correctly (beta.3, #570, #571): Services that accept a string entity_id target normalize the value before resolution, fixing silent failures from case or spacing variations.

FOV mode persisted on re-render, fixing save loop (beta.2/beta.3, #565): The selected CONF_FOV_MODE was not written back into the stored options dict before re-render, causing an infinite re-render cycle.

Measurements-mode config-flow save and imperial shaded-area compounding fixes (beta.2, #565): FOV sliders were emitted as vol.Required, blocking the frontend from switching to Measurements mode. Imperial shaded-area values also compounded on each re-render.

Low-sun edge case returns closed position (beta.1, #559, #562): At very low sun elevations, a geometry edge case returned the fully-open position. The cover now closes when the sun is near the horizon.

Write-gating, geometry caching, and sun availability guard (beta.1, #543): _acp_render_signature() skips HA state writes when the calculated position matches the last written value. Pure geometry helpers use @lru_cache to eliminate repeated identical calculations. A guard on sun.sun suppresses solar tracking when the sun entity is unavailable.

Compatibility

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

References