Adaptive Cover Pro ⛅ v2.28.0-beta.8
Pre-releaseℹ Using release notes from: release_notes/v2.28.0-beta.8.md
Beta.8 adds two refinements to the v2.28.0 line. Measurements-mode FOV sliders are now visible in the Cover Geometry step — pre-populated with the derived angle as an editable suggested_value — so users can accept the calculated angle or override either side independently. The Configuration Summary no longer leaks raw Jinja2 source: any threshold field that holds a template now renders as [template] rather than exposing the expression. Both changes build directly on work already in this beta series (#565 FOV corrections, #577 template threshold fields).
🎯 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_LEFTandCONF_FOV_RIGHTsliders 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.
- Call the
set_force_overrideservice from an existing automation and confirm the cover still moves to the configured position; check the log for the deprecation warning.
✨ Features
Measurements-mode FOV sliders now visible with editable suggested value (#565, #588)
Previously, when CONF_FOV_MODE was set to FovMode.MEASUREMENTS, the CONF_FOV_LEFT and CONF_FOV_RIGHT sliders were dropped entirely from the Cover Geometry step. Users had no way to inspect the derived angle or override it without switching back to FovMode.ANGLES.
_get_sun_tracking_schema now accepts a source_config parameter, which it forwards to CoverTypePolicy.fov_mode_schema. BlindPolicy.fov_mode_schema uses that config to call fov_from_reveal(width, depth), rounds the result, and emits both FOV keys as vol.Optional(key, description={"suggested_value": derived}) when FovMode.MEASUREMENTS is active and depth > 0. The sliders appear pre-populated with the derived angle. When the user leaves a slider unchanged the frontend submits the suggested_value; _resolve_fov_mode_submit detects that the user did not supply an explicit override and writes the derived value rather than the typed one, so the stored angle always reflects a real number rather than a blank.
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.
This change builds on the beta.7 fix to fov_from_reveal (#565, #583) that corrected the half-angle formula. Users who re-opened the step after that fix but before this one saw the correct angle in a read-only display; they can now also adjust it without leaving Measurements mode.
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
Config summary shows [template] for Jinja2 threshold values (#577, #587)
_build_config_summary previously emitted raw threshold values directly into the rendered summary. When a threshold field contained a Jinja2 expression, the full template source appeared verbatim in the UI — a confusing leak of internal syntax that offered no useful information to the user.
A _thresh_display(value, *, placeholder) helper now wraps every threshold lookup. If is_template_string(str(value)) returns true, the helper returns the placeholder string from the new fragments.template_value label ([template] in all three shipped languages) rather than the raw expression. Applied to CONF_WEATHER_WIND_SPEED_THRESHOLD, CONF_WEATHER_RAIN_THRESHOLD, CONF_CLOUD_COVERAGE_THRESHOLD, CONF_LUX_THRESHOLD, CONF_IRRADIANCE_THRESHOLD, CONF_TEMP_LOW, CONF_TEMP_HIGH, and CONF_OUTSIDE_THRESHOLD. The fragments.template_value key was added to _SUMMARY_LABELS_EN and to all three summary_i18n language files (en/de/fr).
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
_get_sun_tracking_schemagained asource_configparameter forwarded toCoverTypePolicy.fov_mode_schema;BlindPolicy.fov_mode_schemauses it to derive and expose thesuggested_valuefor both FOV sliders inFovMode.MEASUREMENTS._thresh_displayhelper in_build_config_summarycentralises the template-detection guard;fragments.template_valueadded to_SUMMARY_LABELS_ENand all threesummary_i18nfiles.CUSTOM_POSITION_SLOT_NUMBERSexpanded from(1, 2, 3, 4)to(1, 2, 3, 4, 5); priority slider ceiling raised from 99 to 100.mirror_legacy_slot_sensor_keysmirrors the first sensor from the multi-sensor list back into the legacy single-sensor key on every save for rollback fidelity.DEFAULT_TEMPLATE_COMBINE_MODEreplaces the motion-specific constant as the shared default for all combine-mode config fields.async_prune_legacy_sensor_entities_v2prunes the orphaned "Force Override Triggers" diagnostic sensor entity on upgrade.
🧪 Testing
4,454 tests passing. New coverage in tests/test_config_flow_fov_mode.py — FOV slider visibility in Measurements mode, suggested_value pre-population, typed override precedence, and zero-depth guard. New coverage in tests/test_config_flow_summary.py — [template] placeholder for all eight threshold fields.
Previously in this beta line
Force Override merged into Custom Positions, FOV formula corrected (beta.7, #563, #565, #584, #583): Force Override no longer exists as a standalone config step; its safety semantics are expressed through Custom Positions slot 5 at priority 100. fov_from_reveal corrected from atan(half_width / depth) to atan(width / depth).
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.