Skip to content

Commit a75658b

Browse files
committed
chore: add v2.27.0 release notes
1 parent 8f7bb91 commit a75658b

1 file changed

Lines changed: 84 additions & 0 deletions

File tree

release_notes/v2.27.0.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2.27.0 brings forecast/runtime parity, opt-in movement minimization for sun-tracking covers, and sun-state classification exposed through diagnostics and the `decision_trace` sensor. A false manual override that fired when a cover returned from unavailable is fixed. The release tooling also gains a ZIP-structure guard to prevent a double-nested HACS install path.
2+
3+
---
4+
5+
## 🎯 Highlights
6+
7+
- **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.
8+
- **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.
9+
- **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.
10+
- **False manual override suppressed** ([#546], [#551]): covers returning from unavailable no longer trigger a spurious manual override when no prior command target exists.
11+
12+
---
13+
14+
## ✨ Features
15+
16+
### Forecast/runtime parity ([#556])
17+
18+
The position forecast in `forecast.py` previously re-implemented a subset of the live position math independently. It never applied min/max position limits, did not floor solar samples at 1% or round them, and used the raw default height rather than the sunset position during the sunset window. The result was a forecast that visually diverged from what the live pipeline would produce.
19+
20+
Three snapshot-free primitives now own the shared math in `pipeline/helpers.py`: `solar_position_from_geometry` computes the sun-tracking position from geometry alone, `default_position_with_limits` computes the default (sunset-aware) position with min/max limits applied, and `apply_config_limits` applies the configured position bounds. The live pipeline helpers in `pipeline/helpers.py` are thin adapters over these primitives. The forecast calls the same functions, so limits, floor-at-1, rounding, and movement minimization match runtime by construction.
21+
22+
`compute_effective_default` gained an `eval_time` keyword argument so the forecast can compute the sunset-aware default at each sample's projected time rather than at the current moment. A parity test locks `compute_solar_position` and `compute_default_position` against future drift between the two paths.
23+
24+
- `forecast.py` — now calls shared primitives instead of duplicating position math
25+
- `pipeline/helpers.py``solar_position_from_geometry`, `default_position_with_limits`, `apply_config_limits` extracted here
26+
27+
### Opt-in movement minimization ([#555])
28+
29+
Small positional corrections as the sun moves across the sky cause covers to jog frequently. The new `CONF_MINIMIZE_MOVEMENTS` option (default off) quantizes the computed sun-tracking position to a configurable number of evenly-spaced coverage steps via `quantize_to_coverage_steps()` in `position_utils.py`. Step count is controlled by `CONF_MAX_COVERAGE_STEPS` (default `DEFAULT_MAX_COVERAGE_STEPS`). Quantization applies after the raw solar position is computed, so it interacts correctly with min/max limits and the floor-at-1% rounding already in place.
30+
31+
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.
32+
33+
- `position_utils.py``quantize_to_coverage_steps()`
34+
- `pipeline/handlers/solar.py` — applies quantization when `CONF_MINIMIZE_MOVEMENTS` is enabled
35+
- `services.yaml``CONF_MINIMIZE_MOVEMENTS`, `CONF_MAX_COVERAGE_STEPS` exposed
36+
- `translations/en.json`, `translations/de.json`, `translations/fr.json` — labels and descriptions
37+
38+
### Sun-state classification in diagnostics and decision trace ([#552], [#553], [#554])
39+
40+
Each update cycle now classifies the sun's relationship to the cover window using the new `SunState` StrEnum in `engine/sun_geometry.py`, with three values: `HITTING` (sun is directly hitting the cover), `IN_FOV_NOT_VALID` (sun is within the field of view but not at a valid elevation or angle), and `OUTSIDE_FOV` (sun is outside the configured field of view entirely). The `in_fov()` method on sun geometry classes drives the classification.
41+
42+
`diagnostics/builder.py` reads the classification from the pipeline result and includes it in the diagnostics payload, making it visible in the HA diagnostics download. `sensor.py` surfaces `sun_state` as an attribute on the `decision_trace` sensor, so dashboards and automations can read the classification directly without downloading diagnostics. The attribute degrades gracefully when the value is absent.
43+
44+
- `engine/sun_geometry.py``SunState` enum, `in_fov()` method
45+
- `diagnostics/builder.py` — classification included in diagnostics output
46+
- `sensor.py``sun_state` attribute on `decision_trace` sensor
47+
48+
---
49+
50+
## 🐛 Fixes
51+
52+
**False manual override on cover returning from unavailable ([#546], [#551]):** The override detector in `managers/manual_override/` treated any position delta as a manual override, even when the integration had never recorded a command target — for example, when a cover came back online after being unavailable. The fix threads `has_recorded_target` through the detection path: when no target has been recorded, the position-delta branch does not mark an override. A user-context change (explicit user action) still triggers the override regardless. The new constant `_NON_POSITION_COVER_STATES` centralizes the set of cover states excluded from position-delta detection.
53+
54+
---
55+
56+
## 🔧 Internal
57+
58+
The release script in `scripts/release` now verifies the built ZIP structure before publishing, guarding against a double-nested directory layout that causes HACS to install the integration at the wrong path ([#544], [#545]). This is a distribution-only change with no runtime effect.
59+
60+
---
61+
62+
## 🧪 Testing
63+
64+
- 4214 tests passing
65+
66+
---
67+
68+
## Compatibility
69+
70+
- Home Assistant 2026.3.0+
71+
72+
---
73+
74+
## References
75+
76+
[#544]: https://github.com/jrhubott/adaptive-cover-pro/issues/544
77+
[#545]: https://github.com/jrhubott/adaptive-cover-pro/issues/545
78+
[#546]: https://github.com/jrhubott/adaptive-cover-pro/issues/546
79+
[#551]: https://github.com/jrhubott/adaptive-cover-pro/issues/551
80+
[#552]: https://github.com/jrhubott/adaptive-cover-pro/issues/552
81+
[#553]: https://github.com/jrhubott/adaptive-cover-pro/issues/553
82+
[#554]: https://github.com/jrhubott/adaptive-cover-pro/issues/554
83+
[#555]: https://github.com/jrhubott/adaptive-cover-pro/issues/555
84+
[#556]: https://github.com/jrhubott/adaptive-cover-pro/issues/556

0 commit comments

Comments
 (0)