Skip to content

Commit f89ca55

Browse files
authored
fix: surface dry-run, housing offset, back-rotate lag in config summary (#528)
The Configuration Summary screen described the full decision chain as if it drove covers even when dry-run was on, omitted the oscillating-awning housing offset and venetian back-rotate publish lag from their geometry blocks, and never showed position tolerance. Add a leading dry-run banner, surface the missing options, and drop an unused parameter in the priority-chain helper.
1 parent cff4068 commit f89ca55

5 files changed

Lines changed: 95 additions & 2 deletions

File tree

custom_components/adaptive_cover_pro/config_flow.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,17 @@ def _offset_str(minutes: int) -> str:
10081008

10091009
lines: list[str] = []
10101010

1011+
# Dry-run banner — surfaced first because it overrides everything below: when
1012+
# on, the full decision chain is still computed and logged but no commands are
1013+
# sent, so covers never move. Without this the summary reads as if it drives
1014+
# covers regardless of the dry-run toggle on the Debug screen.
1015+
if config.get(CONF_DRY_RUN):
1016+
lines.append(
1017+
"⚠️ **Dry-run mode is ON** — positions are computed and logged, but "
1018+
"no commands are sent and covers will NOT move."
1019+
)
1020+
lines.append("")
1021+
10111022
# =========================================================================
10121023
# Section 1: Your Cover
10131024
# =========================================================================
@@ -1464,6 +1475,9 @@ def _sun_annotation(
14641475
limit_parts.append(f"Min change: {delta_pos}%")
14651476
if delta_time is not None:
14661477
limit_parts.append(f"Min interval: {delta_time} min")
1478+
pos_tol = config.get(CONF_POSITION_TOLERANCE)
1479+
if pos_tol is not None:
1480+
limit_parts.append(f"Position tolerance: {pos_tol}%")
14671481
if config.get(CONF_INVERSE_STATE):
14681482
limit_parts.append("Inverse state")
14691483
oc_thresh = config.get(CONF_OPEN_CLOSE_THRESHOLD)
@@ -1553,7 +1567,7 @@ def _sun_annotation(
15531567
# =========================================================================
15541568
# Section 4: Decision Priority (compact reference)
15551569
# =========================================================================
1556-
def _ch(active: bool, short: str, pri: int) -> str:
1570+
def _ch(active: bool, short: str) -> str:
15571571
mark = "✅" if active else "❌"
15581572
return f"{mark}{short}"
15591573

@@ -1576,7 +1590,7 @@ def _ch(active: bool, short: str, pri: int) -> str:
15761590
_chain_entries.append((_pri, f"Custom#{_slot}({_pri})", True))
15771591
# Sort highest priority first
15781592
_chain_entries.sort(key=lambda e: e[0], reverse=True)
1579-
chain = [_ch(active, short, pri) for pri, short, active in _chain_entries]
1593+
chain = [_ch(active, short) for _pri, short, active in _chain_entries]
15801594

15811595
lines.append("")
15821596
lines.append("**Decision Priority** (highest wins, ✅ active ❌ not configured)")

custom_components/adaptive_cover_pro/cover_types/oscillating_awning.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ def summary_geometry_lines(self, config: dict[str, Any]) -> list[str]:
166166
parts.append(f"sweep {lo}°–{hi}°")
167167
if (v := config.get(CONF_HEIGHT_WIN)) is not None:
168168
parts.append(f"{v}m window height")
169+
if (v := config.get(CONF_AWNING_HOUSING_OFFSET)) is not None:
170+
parts.append(f"{v}m housing offset")
169171
return [", ".join(parts)] if parts else []
170172

171173
def cover_capability_warnings(self, known: dict[str, dict]) -> list[str]:

custom_components/adaptive_cover_pro/cover_types/venetian/policy.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ def summary_geometry_lines(self, config: dict[str, Any]) -> list[str]:
261261
CONF_VENETIAN_POST_SETTLE_HOLD, DEFAULT_VENETIAN_POST_SETTLE_HOLD_SECONDS
262262
)
263263
post_settle_line = [f"post-settle hold {round(hold, 1)}s"]
264+
lag = config.get(
265+
CONF_VENETIAN_BACKROTATE_PUBLISH_LAG,
266+
DEFAULT_VENETIAN_BACKROTATE_PUBLISH_LAG_SECONDS,
267+
)
268+
backrotate_line = [f"back-rotate publish lag {round(lag, 1)}s"]
264269
return (
265270
window_dimensions_lines(config)
266271
+ slat_line
@@ -270,6 +275,7 @@ def summary_geometry_lines(self, config: dict[str, Any]) -> list[str]:
270275
+ min_tilt_line
271276
+ max_tilt_line
272277
+ post_settle_line
278+
+ backrotate_line
273279
)
274280

275281
def cover_capability_warnings(self, known: dict[str, dict]) -> list[str]:

tests/test_config_flow_summary.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,27 @@ def test_empty_config_returns_string():
211211
assert len(summary) > 0
212212

213213

214+
def test_dry_run_banner_shown_when_enabled():
215+
"""Dry-run banner is surfaced (and first) when dry_run is on."""
216+
from custom_components.adaptive_cover_pro.const import CONF_DRY_RUN
217+
218+
summary = _build_config_summary({CONF_DRY_RUN: True}, CoverType.BLIND)
219+
assert "Dry-run mode is ON" in summary
220+
assert "covers will NOT move" in summary
221+
# Must lead the summary, above the Your Cover section.
222+
assert summary.index("Dry-run mode is ON") < summary.index("**Your Cover**")
223+
224+
225+
def test_dry_run_banner_absent_when_disabled():
226+
"""No dry-run banner when the flag is off or unset."""
227+
from custom_components.adaptive_cover_pro.const import CONF_DRY_RUN
228+
229+
assert "Dry-run mode" not in _build_config_summary(
230+
{CONF_DRY_RUN: False}, CoverType.BLIND
231+
)
232+
assert "Dry-run mode" not in _build_config_summary({}, CoverType.BLIND)
233+
234+
214235
def test_entity_included_in_your_cover():
215236
"""Cover entity ID appears in the Your Cover line."""
216237
cfg = {CONF_ENTITIES: ["cover.living_room"]}
@@ -337,6 +358,38 @@ def test_geometry_venetian_shows_post_settle_hold_custom():
337358
assert "post-settle hold 5.5s" in summary
338359

339360

361+
def test_geometry_venetian_shows_backrotate_lag_default():
362+
"""Venetian summary includes back-rotate publish lag at the default (45.0 s)."""
363+
summary = _build_config_summary({}, CoverType.VENETIAN)
364+
assert "back-rotate publish lag 45.0s" in summary
365+
366+
367+
def test_geometry_venetian_shows_backrotate_lag_custom():
368+
"""Venetian summary reflects a custom back-rotate publish lag value."""
369+
from custom_components.adaptive_cover_pro.const import (
370+
CONF_VENETIAN_BACKROTATE_PUBLISH_LAG,
371+
)
372+
373+
cfg = {CONF_VENETIAN_BACKROTATE_PUBLISH_LAG: 60.0}
374+
summary = _build_config_summary(cfg, CoverType.VENETIAN)
375+
assert "back-rotate publish lag 60.0s" in summary
376+
377+
378+
def test_geometry_oscillating_awning_shows_housing_offset():
379+
"""Oscillating-awning summary renders the housing offset when configured."""
380+
from custom_components.adaptive_cover_pro.const import CONF_AWNING_HOUSING_OFFSET
381+
382+
cfg = {CONF_AWNING_HOUSING_OFFSET: 0.25}
383+
summary = _build_config_summary(cfg, CoverType.OSCILLATING_AWNING)
384+
assert "0.25m housing offset" in summary
385+
386+
387+
def test_geometry_oscillating_awning_housing_offset_omitted_when_unset():
388+
"""Housing offset line is absent when the field is not configured."""
389+
summary = _build_config_summary({}, CoverType.OSCILLATING_AWNING)
390+
assert "housing offset" not in summary
391+
392+
340393
# ---------------------------------------------------------------------------
341394
# Section 2: How It Decides — Sun Tracking
342395
# ---------------------------------------------------------------------------
@@ -991,6 +1044,22 @@ def test_position_inverse_state_hidden_when_false():
9911044
assert "Inverse state" not in summary
9921045

9931046

1047+
def test_position_tolerance_shown_when_configured():
1048+
"""Position tolerance appears in Position Limits when set."""
1049+
from custom_components.adaptive_cover_pro.const import CONF_POSITION_TOLERANCE
1050+
1051+
cfg = {CONF_DEFAULT_HEIGHT: 60, CONF_POSITION_TOLERANCE: 5}
1052+
summary = _build_config_summary(cfg, CoverType.BLIND)
1053+
assert "Position tolerance: 5%" in summary
1054+
1055+
1056+
def test_position_tolerance_hidden_when_unset():
1057+
"""Position tolerance line is absent when the field is not configured."""
1058+
cfg = {CONF_DEFAULT_HEIGHT: 60}
1059+
summary = _build_config_summary(cfg, CoverType.BLIND)
1060+
assert "Position tolerance" not in summary
1061+
1062+
9941063
def test_position_interp_shown():
9951064
"""Position calibration flag appears in Position Limits."""
9961065
cfg = {CONF_INTERP: True}

tests/test_cover_types/test_config_flow_dispatch.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ def test_venetian_renders_window_then_slat_block(self):
119119
"min tilt 0%",
120120
"max tilt 100%",
121121
"post-settle hold 3.0s",
122+
"back-rotate publish lag 45.0s",
122123
]
123124

124125
def test_empty_config_renders_nothing(self):
@@ -133,6 +134,7 @@ def test_venetian_empty_config_renders_threshold_default(self):
133134
"min tilt 0%",
134135
"max tilt 100%",
135136
"post-settle hold 3.0s",
137+
"back-rotate publish lag 45.0s",
136138
]
137139

138140
def test_venetian_summary_shows_inverse_tilt_when_set(self):

0 commit comments

Comments
 (0)