Adaptive Cover Pro ⛅ v2.30.0
ℹ Using release notes from: release_notes/v2.30.0.md
Highlights
v2.30.0 promotes four beta cycles of work since v2.29.0. The two headline additions are a new roof/skylight window cover type (closing the oldest open feature request, #212) and a building profile system that consolidates shared weather and climate sensors across all covers in a building without duplicating them per entry. Alongside those, blind-spot configuration expands to three slots with per-slot elevation-mode filtering, custom-position slots grow from five to ten, and a new Sun Tracking switch pauses solar tracking at runtime while keeping glare-zone protection active. The venetian engine gains a drift-reset cycle with configurable direction and threshold, endpoint-aware open/close dispatch, a live solar_calculation diagnostic sensor, and a fix for slat back-rotation on motors that drag slats shut during a close. An external input-sensor manual override, a weather override master toggle, an opt-in summer-close sun-floor bypass, and a set_tilt service for automations complete the set. Thanks to @Zhephyr54 for contributing the Sun Tracking switch (#678) and to @vaind for tracking down and fixing the venetian back-rotation bug (#694).
Added
-
Roof/skylight window cover type (#212, #696): New cover type
"cover_roof_window"with pitch-aware sun geometry. ConfigureCONF_ROOF_PITCHandCONF_ROOF_HEIGHT_ABOVEto match the physical installation;RoofSunGeometryhandles the tilted-plane math,RoofWindowCoverdrives position calculation, andRoofWindowPolicyprovides the policy layer. The type is registered inALL_COVER_TYPESalongside 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.BuildingProfilePolicyandSensorSourcedefine 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_overviewinbuilding_overview.pypresents a comparison table of every cover linked to the profile. Rows are modeled with_CoverRecord,_DiffSpec, and_Entrydataclasses; diff logic uses_comparison_as_list,_comparison_as_table, and_format_diff_line, with local overrides rendered by_local_override_repr. Number normalization via_numensures numeric values compare correctly regardless of representation. -
Inherit/override model for Building Profile sensors:
async_step_profile_overrideslets a linked cover override any inherited profile sensor locally. Each override is tracked as anOverrideRecorddataclass, 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 keyenable_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_updaterebuilds the pipeline without a reload;CONF_ENABLE_SUN_TRACKINGis listed in_RUNTIME_APPLICABLE_OPTIONSso the update listener applies it without triggering a config-entry reload.GlareZoneHandleris now aware of the tracking state: whenenable_sun_trackingis off, glare-zone protection can still activate if its computed position is more protective than the default result, as determined bymore_protective_position;apply_snapshot_limitsenforces 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_BELOWblocks low-sun intrusion,BLIND_SPOT_ELEV_MODE_ABOVEblocks high-sun intrusion.BlindSpotis 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_NUMBERSnow 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()andapply_user_position_endpoint_close()handle the axis-aware dispatch;_POSITION_AXIS_SERVICESmaps each axis to its service. -
On/off master toggle for weather override (#719):
CONF_WEATHER_ENABLEDgates the entire weather handler. New installs default to off (DEFAULT_WEATHER_ENABLED = False); the v3.5 to v3.6 migration setsCONF_WEATHER_ENABLED = Truefor all existing entries, so upgrading changes nothing for installs that already have weather sensors configured. -
Opt-in summer-close sun-floor bypass (#689):
CONF_SUMMER_CLOSE_BYPASS_SUN_FLOORlets summer climate mode close covers regardless of sun position. Default off; enabling it is additive and changes nothing for installs that leave it unset. -
Venetian drift-reset threshold (#663, #681): A new per-instance option
CONF_VENETIAN_TILT_RESET_THRESHOLDsets an accumulated commanded-tilt-% threshold that triggers a mechanical drift-reset cycle. When the accumulated counter inDualAxisSequencerreaches the threshold, the sequencer drives the slats toPOSITION_OPEN(bypassing any configured tilt max) and back to the original target, flushing mechanical actuator drift. A_reset_in_progressguard prevents the re-zero moves from accumulating against the counter. The defaultDEFAULT_VENETIAN_TILT_RESET_THRESHOLDis0(feature disabled); range is 0-5000 accumulated %. The threshold applies live without a reload. Thanks to @x4N70pHyLL (#663) for surfacing this. -
Configurable venetian drift-reset direction (#686, #690):
CONF_VENETIAN_TILT_RESET_DIRECTION(select:open/close) controls which endpoint the drift-reset drives through before re-targeting. The defaultVENETIAN_TILT_RESET_OPEN(DEFAULT_VENETIAN_TILT_RESET_DIRECTION) preserves prior behavior;VENETIAN_TILT_RESET_CLOSEsuits hardware that rests near-closed during sun tracking or re-zeroes the actuator on a close command. Valid values are listed inVENETIAN_TILT_RESET_DIRECTIONS. The direction applies live without a reload. Thanks to @elmakus (#686) for requesting this. -
Opt-in endpoint delta enforcement for coupled covers (#679, #680):
CONF_ENFORCE_DELTA_AT_ENDPOINTS(defaultDEFAULT_ENFORCE_DELTA_AT_ENDPOINTS=False) brings the min-change delta gate to endpoint commands. By default the always-send-to-0/100 guarantee is preserved:build_special_positionsreturns[0, 100]and the tilt special-position bypass is in effect. When opted in,build_special_positionsreturns an empty list andcheck_position_deltaruns for endpoint targets too, which suits mechanically coupled covers where the hardware parks at 3 rather than 0 and full-open/close commands disturb the tilt axis. -
Manual override detection from external input sensors (#688, #691):
CONF_MANUAL_OVERRIDE_INPUT_ENTITIES(multi-entity selector, default empty) accepts a list of binary sensors whose off-to-on edge engages manual override on every cover in the instance. Intended for physical wall switches such as a Shelly input sensor: pressing the switch pauses automatic control for the configured override duration without waiting for the cover to report a position change. The edge filter is strict; on-to-on, None-to-on, and unavailable/unknown states do nothing, and each press re-arms the full duration. Thanks to @elmakus (#688) for requesting this. -
set_tiltservice (#684, #685): A newadaptive_cover_pro.set_tiltservice lets automations drive the venetian slat axis without touching the carriage. The service delegates toasync_apply_user_tilt, which routes throughVenetianPolicyto drive only the slats viaDualAxisSequencer.update_tilt_only. Aforceparameter (defaultfalse) governs manual-override engagement, matchingset_positionsemantics. Implemented inset_tilt_service.pyviaasync_handle_set_tilt. -
Live
solar_calculationdiagnostic sensor (#682, #683): Each cover now gets asolar_calculationsensor whose state is the per-cycle computedposition_pctand whose attributes carry the full geometric trace: sun elevation, gamma, engine-specific intermediates, and for venetian covers a nestedtiltsub-trace. The calc engines record raw inputs and outputs in_last_calc_detailseach cycle.DiagnosticsBuilder._round_traceand_round_trace_valueround every numeric leaf at the presentation boundary and stampcover_typeandstatusbefore the data reaches the sensor. The sensor readsunknownoutside the solar window;DiagnosticsBuilder._build_position_calc_detailsemits a minimal fallback trace so attributes always carry astatusreason. -
Safety-slot warning (#711): The options flow warns when a custom-position slot is set to safety priority (100), which bypasses all gates including the time window and delta checks.
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, aset_cover_positioncommand 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_satisfiednow compares the stored target against the live actuator reading via_resolve_tilt_anchor; it returnsTrueonly when the stored target matches the live reading withinVENETIAN_TILT_VERIFY_TOLERANCE. When the live reading has drifted, the dedup returnsFalseand the tilt command is re-sent. This fix is always on and is independent ofCONF_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_positionwas callingasync_apply_user_positionwith the requested tilt value, which moved the carriage and left the slats at whatever angle they held. It now callsasync_apply_user_tilt, which dispatches throughVenetianPolicy.apply_user_tilttoDualAxisSequencer.update_tilt_onlywithforce=True. -
solar_calculationattributes blank outside the solar window (#682): When the sun was outside the tracking window,_last_calc_detailswasNoneand the sensor readunknownwith empty attributes.DiagnosticsBuilder._build_position_calc_detailsnow emits a minimal fallback trace (sol_elev_deg,gamma_deg,position_pct=None) whenever no engine trace was recorded, with astatusfield drawn fromcontrol_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_sensorsrather 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 fromloaded_coordinatorsso coordinator lookups intended for cover entries don't inadvertently resolve to profile entries. -
async_unload_entryguarded for building profile entries (#712, #714):async_unload_entrynow 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. No string keys changed.
Upgrade Notes
Upgrading from v2.29.0 requires no manual reconfiguration. All new options default to off or empty, so behavior is unchanged unless you enable them. The config schema advances from v3.4 to v3.6 across two minor-version bumps; both migrations are transparent.
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.
Venetian options: CONF_VENETIAN_TILT_RESET_THRESHOLD defaults to 0 (feature disabled), CONF_VENETIAN_TILT_RESET_DIRECTION defaults to VENETIAN_TILT_RESET_OPEN (prior behavior), and CONF_ENFORCE_DELTA_AT_ENDPOINTS defaults to False. Upgrading from v2.29.0 changes nothing without reconfiguration.
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) is a separate repo with its own release cycle.