-
Notifications
You must be signed in to change notification settings - Fork 1
Releases: raouldekezel/NavimowHA
- NavimowHA v1.1.0-raoul.25 — BUG-19 step 2: ceiling-only vestige signature (absorbs BUG-16)
- NavimowHA v1.1.0-raoul.24 — BUG-19 vestige guard widened to post-close states
- NavimowHA v1.1.0-raoul.23 — BUG-17 bypass diag
- NavimowHA v1.1.0-raoul.22 — BUG-17 vestige guard
- NavimowHA v1.1.0-raoul.21 — BUG-18 followup + BUG-17 diag corrections
- NavimowHA v1.1.0-raoul.20 — BUG-18 fix + BUG-17 diag
- NavimowHA-v1.1.0-raoul.19
- NavimowHA v1.1.0-raoul.18 — 5 PRs: FEAT-09, HARD-07/08/16/17
- NavimowHA v1.1.0-raoul.17 — HARD-15: zone_name attribute
- NavimowHA v1.1.0-raoul.16
NavimowHA v1.1.0-raoul.25 — BUG-19 step 2: ceiling-only vestige signature (absorbs BUG-16)
1c97e6f BUG-19 step 2 (#118) — simplifie la signature de rejet du vestige task-end à la signature-plafond seule :
if mp == MP_TASK_END and (cmp_ or 0) >= CMP_ZONE_COMPLETE_THRESHOLD:Le terme sub est retiré : il ne portait pas la décision de drop (il ne distinguait que les deux sous-formes du vestige, zéro vs gelé — le même fantôme). Tester sub < 0.5 est exactement pourquoi raoul.22 attrapait la forme zero-sub (BUG-17) mais ratait la forme frozen-sub (BUG-16). La signature-plafond absorbe BUG-16 (#92) dans le même guard.
Sûreté catégorique : un run ne peut pas ouvrir à cmp = 10000 (état de frontière terminée) ; un vrai démarrage montre cmp grimpant depuis le bas (cmp = 104 le 20-07). Combiné à la fenêtre sombre (run semé en tonte, où vivent les vrais paquets de complétion), aucun paquet honnête n'est rejeté.
Assets 2
NavimowHA v1.1.0-raoul.24 — BUG-19 vestige guard widened to post-close states
8072bdb What's new
BUG-19 fix — vestige guard now covers post-close states (#116)
The real fix for the pathology raoul.22 tried to solve. raoul.22's _gate_run_start_vestige guard enumerated the armed states (STATE_IDLE ∨ open-run-with-zones == []) and left STATE_COMPLETED / STATE_INTERRUPTED dark by design — which turned out to be the operator's dominant real-world entry state (Store persistence rehydrates a post-close state on every HA restart since FEAT-05 c landed 2026-07-04). The 2026-07-20 diag (PR #113) documented the bypass end-to-end: vestige poisons _open_run, zones[0].cmp_max = 10000 seeded, current_zone_progress stuck at 100 % for the whole mow.
The fix inverts the arming logic — name the single dark state (mowing with a real zone already seeded) and be armed everywhere else:
mowing_with_zone = (
self.state in (STATE_RUNNING, STATE_PAUSED_DOCKED)
and self.current_run is not None
and bool(self.current_run.get("zones"))
)
armed = not mowing_with_zoneAn enumeration of armed states can miss one — it did; the complement of the single dark state cannot.
Drop signature is untouched: mp = MP_TASK_END ∧ cmp ≥ 10000 ∧ area_session < 0.5. No genuine packet ever carries this shape — it's a physical contradiction ("task 100 % complete ∧ zero m² mowed this session").
Effect on the operator's typical mow, per the 2026-07-20 wire replay:
11:21:54.382 vestige (mp=100 cmp=10000 sub=0.0 wk=357.63) in STATE_COMPLETED
→ armed → signature matches → DROPPED, one DEBUG line
11:22:50.383 real packet (mp=0 cmp=104 sub=2.53 wk=360.12)
→ armed but signature fails (mp ≠ 100) → ACCEPTED
→ zones[0].cmp_max = 104
→ current_zone_progress gauge starts at 1.04 %, not 100 %
What does not change
- BUG-14 fast-path completion (
mp = 100 ∧ cmp = 10000 ∧ sub ≈ 232 m²): dark (zone seeded), and even if armedsubfails the signature. - BUG-16 (#92) frozen-
subvariant: fails the sub tolerance; #92 remains separately necessary. - Genuine post-close re-mow start (
mp = 0, cmp small, sub small): armed but signature fails → accepted normally. - Fail-open None handling,
MP_TASK_ENDdecoupling, observability line: unchanged.
Test coverage
19 dedicated tests in tests/test_bug_17_run_start_vestige_guard.py:
- 4 new: post-close
STATE_COMPLETEDdrop, post-closeSTATE_INTERRUPTEDdrop (via sustained-timer close path), genuine post-close re-mow start accepted, and the deterministic 2026-07-20 wire replay end-to-end. - 1 retired:
test_post_close_vestige_shape_not_caught_by_this_guard(its behaviour inverts by design; BUG-13 synthetic-repro documentation now lives on #86). - 15 unchanged from raoul.22, all still green.
Full suite: 412 passed (19 vestige-guard + 393 pre-existing).
Also in this release
_gate_run_start_vestigedocstring rewritten to reflect the inverted logic; call-site comment updated with both entry-state pathologies.- #92's promised observability hook explicitly marked as future work in the docstring (per #114 open question 4).
- No schema change, no persistence change, no impact on any packet outside the vestige signature.
Validation
On-site: a routine post-close mow (any prior close persisted in Store, HA left running or restarted). Expected behaviour post-BUG-19:
sensor.<slug>_current_zone_progressstarts near 1 %, not 100 %.home-assistant.logcarries onerun_tracker: type-2 rejected — run-start vestige (mp=100 cmp=10000 sub=0.0 ...)DEBUG line at the firsttype-2after thedocked → mowingtransition.sensor.<slug>_last_run_startedanchors on the first genuine packet (~56 s after the app-side start), which is expected given the corrected narrative — not a regression.- The next honest close on Prunier will overwrite the currently re-poisoned
Store.last_cmp_max = 10000with the real value.
Refs
- #116 PR (merged 2026-07-20)
- #114 BUG-19 — the canonical issue this release closes.
- #113 diag — the wire capture that motivated the widening.
- #105 BUG-17 — the sibling; the guard now protects its full scope.
- Unchanged by this PR: #92 BUG-16, #86 BUG-13.
Backward compatibility
No schema change, no persistence change. HACS Redownload picks up the release; on next mow the widened guard fires immediately with no prior configuration needed. Existing runs mid-flight at upgrade time are unaffected (they already have non-empty zones, so the guard remains dark for them by design).
Assets 2
NavimowHA v1.1.0-raoul.23 — BUG-17 bypass diag
02c8574 What's new
BUG-17 bypass diag (#113)
Docs only. First on-site validation of raoul.22 (BUG-17 fix) landed a full recurrence of the raoul.19 pathology: sensor.<slug>_current_zone_progress stuck at 100 % for the entire mow session, run anchored on a vestige packet's fields, zones[0].cmp_max = 10000 persisted to history[].
Root cause: the tracker was restored from Store in STATE_COMPLETED (not STATE_IDLE) at HA startup — the 2026-07-19 close had persisted. The BUG-17 guard's arming window is deliberately dark in post-close states (per #105's fifth-edit seam-map, delegated to #86). Packet flowed through the post-close is_reset branch → _open_run(vestige).
Since Store persistence has been live since 2026-07-04 (FEAT-05 c, 571b8eb), every next-mow post-close has been on this vulnerable code path. This is the dominant real-world scenario, not an edge case.
Triage settled on #114 (BUG-19): widen the BUG-17 guard's arming window to include STATE_COMPLETED / STATE_INTERRUPTED. Fix PR pending.
Bonus persisted evidence for #86 and #92
The Store dump captured by this diag contains two rows worth calling out (both pre-existing at HA start, unrelated to the 2026-07-20 event):
history[]row 5 —duration_ms = 0,session_area = 0.0,zones = [{boundary_id: 1, sub_entry: sub_exit: 0.0, cmp_max: 10000}]. Classic BUG-13 (#86) zero-subphantom, now corroborated by a real Store dump instead of just a wire hypothesis.history[]row 10 — same 0-second phantom shape but withsub_entry = sub_exit = 231.77(frozen at the 2026-07-09 mini-run'ssub). BUG-16 (#92) frozen-subsibling.
Cross-comments posted on both issues.
Row 11 vs #92's prediction
Also flagged: history[] row 11 does not match #92's fifth-edit "net effect" narrative (predicted start_time = 16:11:44Z, zones = [1, 3]; persisted start_time = 16:13:08Z, zones = [3], sub_entry = 286.87, interrupted). Cross-comment on #92 recommending reconciliation before its fix PR.
Files
docs/diag/2026-07-20_bug-17_bypass-in-state-completed/findings.md— full narrative01_vestige_and_first_run.mqtt.log— HA log 11:21:50 → 11:25:35 CEST02_run_close.mqtt.log—run_finishedevent03_store_after_event.json— Store dump, 14history[]entries
PII redacted (robot serial + MQTT client id scrubbed throughout; MQTT credentials elided by HA at the source).
Refs
- #113 PR (merged 2026-07-20)
- #114 BUG-19 — triage resolution; canonical owner of the fix
- #105 BUG-17 — the fix this diag validates (and stress-tests)
- #86 BUG-13 / #92 BUG-16 — sibling issues cross-referenced with dump evidence
Backward compatibility
Docs-only release. Zero code / schema / persistence change. HACS Redownload picks the release up, but nothing in the integration behaves differently versus raoul.22.
Assets 2
NavimowHA v1.1.0-raoul.22 — BUG-17 vestige guard
49e38ef What's new
BUG-17 — drop run-start task-end vestige packet (#111)
Fix. On the very first type-2 of a run, the firmware occasionally replays the previous task's closing packet — signature mp = 100 ∧ cmp = 10000 ∧ subtotalArea = 0. Left untouched (raoul.19 and earlier) the packet:
- was what
_open_runread in the IDLE order — anchoringstart_time,sub₀,mow_start_typeon the vestige's fields; - seeded
zones[0].cmp_max = 10000via_update_zone, stickingsensor.<slug>_current_zone_progressat 100 % for the whole zone segment (~1 h 50 min in the operator's 2026-07-19 trace); - flashed
sensor.<slug>_current_run_progressto 100 for one cadence (visible as100 → 0 → 1 → …); - stamped
zones[0].first_timeat the vestige packet'stime, misdating FEAT-08'slast_complete_pass_at; - over-stated
Store.last_cmp_maxon an interrupted run — mis-triggering BUG-14's fast-path completion on subsequent runs.
The fix drops the packet at the top of process_type2, before any state transition or write, inside a state-keyed arming window with "armed until the first zone-carrying type-2 is accepted" semantics:
if self.state == STATE_IDLE:
armed = True
elif (
self.state in (STATE_RUNNING, STATE_PAUSED_DOCKED)
and self.current_run is not None
and not self.current_run.get("zones")
):
armed = True- First disjunct — the 2026-07-19 wire order (nothing in
process_vehicle_stateopens a run, so the vestige arrives whilestate == STATE_IDLE). - Second disjunct — the BUG-06 sentinel path: session-init
boundary = 0 ∧ mp = 0 ∧ cmp = 0 ∧ action = -1opens the run throughprocess_type2, but_update_zonerejectsboundary = 0sozones == []. A vestige at packet position 2 (before the real boundary lands ~60 s later) must still be dropped. STATE_COMPLETED/STATE_INTERRUPTEDdeliberately dark — that variant is BUG-13 territory (#86).
Signature: mp = MP_TASK_END ∧ cmp ≥ 10000 ∧ area_session < 0.5. New MP_TASK_END = 100 protocol constant, decoupled from the tunable MP_COMPLETION_THRESHOLD policy — a future re-tune of the completion threshold can no longer drag the vestige signature with it. area_session is checked explicitly for None (fail-open on incomplete packets). wk, action, boundary are logged in DEBUG but not gated (calendar artifacts and cross-firmware instability).
Observability: a run-start suspicious shape, not dropped DEBUG line fires inside the arming window on any area_session < 0.5 that fails the full signature — the BUG-06 sentinel lands here every session, alongside genuine low-sub first packets (Sunday first-mow, fresh session post-reset) and — the reason the line exists — any future interrupted-vestige shape.
Test coverage: 16 dedicated tests including both orders, the sentinel-then-dock STATE_PAUSED_DOCKED variant, the window-closes-on-zone-seeding invariant, and an end-to-end scenario that verifies an interrupted run before cmp = 10000 now reports the real cmp_max (3111 m² in the test) instead of the vestige's 10000. Full suite: 409 passed.
Refs issue #105, diag docs/diag/2026-07-19_bug-17_cmp-max-late-task-end/, cross-referenced with #86 (BUG-13 — the synthetic reproduction landed on comment #86-5017684307).
Validation
Manual mow on a boundary whose Store record holds last_result = completed, last_cmp_max = 10000:
- Zone gauge (
sensor.<slug>_current_zone_progress) starts near 1 %, not 100 %. - Debug log carries a
run-start vestigeline ~85 ms after thestate → mowingtransition (or at the first wire packet in the sentinel order). sensor.<slug>_last_run_startedanchors on the first genuine packet, ~56 s after the app-side start — expected under the corrected narrative, not a regression.
Files touched
custom_components/navimow/run_tracker.py— new constantsMP_TASK_END,RUN_START_SUB_TOLERANCE; new helper_gate_run_start_vestige; single call site at the top ofprocess_type2.tests/test_bug_17_run_start_vestige_guard.py— new (16 tests).
No schema change, no persistence change, no impact on packets outside the run-start window. Existing runs mid-flight at upgrade are unaffected (they already have non-empty zones).
Assets 2
NavimowHA v1.1.0-raoul.21 — BUG-18 followup + BUG-17 diag corrections
b399ef2 What's new
BUG-18 follow-up — Unicode angle brackets bypass HA's HTML tokenizer (#110)
Fix. The raoul.20 fix (wrap Zone #<id> in Markdown inline-code backticks) did not eliminate the Translation error: UNCLOSED_TAG in the rename_zone / forget_zone dialogs — operator confirmed the error still rendered on raoul.20 with the backticked strings on disk.
Root cause of the persistence: HA's Frontend applies HTML tokenization before Markdown parsing, so `<id>` was still tokenized as <code><id></code> — the inner <id> was emitted as an unclosed opening tag regardless of the surrounding backticks. Backticks defend against Markdown escaping, not against the underlying HTML tokenizer.
Swapped the ASCII angle brackets for Unicode ⟨id⟩:
- "leave the name blank to reset to `Zone #<id>`."
+ "leave the name blank to reset to Zone #⟨id⟩."
- "re-discovered as `Zone #<id>`."
+ "re-discovered as Zone #⟨id⟩."⟨ (U+27E8) and ⟩ (U+27E9) are graphically indistinguishable from ASCII < / > at dashboard font sizes but carry no HTML-tag semantics — the HTML tokenizer walks past them as regular text characters.
Six edits total, three files. Description text only. Live-verified on intel-nuc: the dialogs now render the description with Zone #⟨id⟩ cleanly instead of the error banner.
Refs issue #107.
BUG-17 diag — reviewer corrections applied (#109)
Docs only. Applies the 5 blocking + 2 non-blocking corrections from Fable's post-merge review on PR #106:
- Vestige
timefield is 163 ms before thestate → mowingtransition (not 85 ms — that's the packet delivery delay AFTER the transition; both deltas now kept distinct in the copy). - Figuier
size_estimate_updated_mstypo1784454068383→1784460068383(matches the real 11:21:08.383Z boundary transition, was off by 100 minutes). - Missing 33rd packet added to
01_manual-start-mowing.mqtt.log(10:04:57 UTC, PII redacted). 02_resume-and-close.sensors.tsvregenerated packet-synced (each row's (mp, cmp) pair comes from the sametype-2packet — replaces 6 rows that had mismatched pairs).- STATE_COMPLETED timing harmonized on 14:00:56 UTC (dock arrival + BUG-09 fast path).
- BUG-17 row in
docs/diag/README.mdnow links to issue #105. - "The 09:07 Prunier mini-run" (ambiguous clock-time reading) → "The 2026-07-09 Prunier mini-run".
2026-07-09 diag — late mp = 100 and cmp zone-persistent (#93)
Docs only. Adds docs/diag/2026-07-09_bug-14_late-mp-100/findings.md documenting two operator-captured firmware behaviours from the same 2026-07-09 session as BUG-14 (#89) with DEBUG live:
mp = 100arrives in late-delivery at task-transition (3 h 20 gap observed).cmpis zone-persistent across sessions (Figuier resumed atcmp = 4404after the 07/07 interruption).
Both findings validate PR #91's mp ≥ 99 ∧ cmp = 10000 refinement as structurally required and motivate follow-up BUG-16 (#92) and BUG-17 (#105) for the phantom-run / poisoning pathologies triggered by the late packet.
Upgrade notes
- HACS operators: pre-release channel required to see this.
- No config-flow migration, no schema change. Zone renames and per-zone entities preserved as-is across the upgrade.
- Same as raoul.20: no mitigation for BUG-17 in this release (diag only). Follow-up code PR will land the guard once the shape is agreed on the issue.
Assets 2
NavimowHA v1.1.0-raoul.20 — BUG-18 fix + BUG-17 diag
a18c4a4 What's new
BUG-18 — options-flow descriptions render "Translation error: UNCLOSED_TAG" (#108)
Fix. Opening Settings → Integrations → Navimow → Configure → Rename a zone (or Forget a zone) rendered Translation error: UNCLOSED_TAG in place of the step description. HA's Frontend parsed the literal <id> token in Zone #<id> as an unclosed HTML tag and bailed.
Wrapped the token in Markdown inline-code backticks so the renderer treats it as literal. Applied identically to English (source + translations/en.json) and French (translations/fr.json).
Six edits total, three files:
- "Rename a zone: leave the name blank to reset to Zone #<id>."
+ "Rename a zone: leave the name blank to reset to `Zone #<id>`."
- "Forget a zone: … re-discovered as Zone #<id>."
+ "Forget a zone: … re-discovered as `Zone #<id>`."Description text only — no code paths, no tests, no fallback logic. sensor.py docstring usages of Zone #<id> (reST double-backticks) unaffected.
Closes issue #107.
BUG-17 diag — current_zone_progress sticks at 100 % after a late task-end vestige packet at run start (#106)
Diagnostic session only — no code change in this release.
Full-arc live capture (2026-07-19): manual start → Prunier → transition → recharge → resume → Figuier → completed. Two dashboard gauges both flash to 100 % at run start (current_run_progress self-corrects in ~56 s because last_mp is overwritten non-monotonically; current_zone_progress stays stuck until a boundary crossing because zones[-1].cmp_max is maxed monotonically). Root cause is a firmware-replayed "task-end vestige" packet with action = -1, mp = 100, cmp = 10000, mowingWeekArea = 0.0, subtotalArea = 0.0 arriving 85 ms after the docked → mowing transition.
Same firmware mechanism as #92 BUG-16 but different pathology (poisoning zones[0].cmp_max on IDLE → RUNNING, not opening a phantom run in COMPLETED). Session 2's _reopen_run empirically confirms the vestige does not re-fire on resume — validates the scope of the proposed guard.
Widened blast radius observed at run close: the vestige's time field is also written into ZoneRecord.size_estimate_updated_ms (FEAT-08 last_complete_pass_at misdated by 85 ms → 56 s on the poisoned zone). sub_entry and Store.last_cmp_max are correct on today's run only by coincidence (first-in-run + full completion).
Fix design (guard A: reject the run's first type-2 when mp == 100 ∧ cmp == 10000 ∧ sub ≈ 0 ∧ wk ≈ 0) documented on issue #105, landing in a follow-up PR.
Upgrade notes
- HACS operators: this release is pre-release — enable "Show beta versions" in the Navimow HACS entry to see it.
- No config-flow migration, no schema change. Zone renames and per-zone entities preserved as-is across the upgrade.
- No mitigation for BUG-17 in this release (diagnostic session only). If you see
current_zone_progressstuck at 100 %, it corresponds to the case documented in this issue.
Assets 2
NavimowHA-v1.1.0-raoul.19
193afb1 What's new
FEAT-08 — zone surfaces as first-class entities + cross-cutting naming pass (#104)
Elevates the zone-size estimate from an attribute of the last-mow surface sensor to first-class entities, then applies a repo-wide naming scheme from the issue's follow-up discussion.
New entities
sensor.<slug>_zone_<id>_total_area— per-zone size (full zone area from the last complete pass). State =ceil(size_estimate_m2), attrsboundary_id,zone_name,area_precise,last_complete_pass_at.Noneuntil the first complete pass — no fake0fallback.sensor.<slug>_zones_total_area— aggregate Σ zone total area. Attrszone_ids,zone_names(parallel list, live-refreshed on renames),area_precise.sensor.<slug>_last_run_area— promoted from thesession_areaattribute oflast_run_result.ceilstate +area_preciseattr.
Renames (with entity_registry.async_migrate_entries so HA history survives)
run_state→current_run_staterun_progress→current_run_progresszone_progress→current_zone_progresszone_<id>(last-mow surface) →zone_<id>_last_areazone_<id>_duration→zone_<id>_last_duration
Uniform contract on every m² sensor
- state =
ceilint, attrarea_precise= float. Applied toweekly_area,zone_<id>_last_area,zone_<id>_total_area,zones_total_area,last_run_area.
Attribute cleanup (promoted attrs dropped from their old host)
last_run_result:session_arearemoved.zones(count aggregate):total_area+per_zoneremoved.zone_idsstays.zone_<id>_last_area:size_estimateremoved.
FR display
"Surface hebdomadaire" / "Surface de la dernière tonte" / "Zones — surface totale" / " surface" — the key scheme uses area, "surface" reads naturally in French.
Coverage
- 4 new test files / test-blocks. Migration is verified (14 unique_id patterns including the
_last_mowedfalse-positive guard). Total suite: 393/393 green.
Notes
- Migration is idempotent — a second setup after migration is a no-op.
zone_<id>_surface(the placeholder key from this PR's first commit) is also migrated tozone_<id>_total_areaas a belt-and-braces for anyone who installed the branch head before the naming pass.
Assets 2
NavimowHA v1.1.0-raoul.18 — 5 PRs: FEAT-09, HARD-07/08/16/17
030d2a8 Prerelease raoul.18 on top of deploy @ 030d2a8. Five PRs merged in one session.
Changes since raoul.17
-
HARD-16 (#97, PR #98) — FR strings rename
passage → tonteon the sixrun_*/current_run_*/last_run_*sensor names. Post-SPIKE-02 / FEAT-06 the tracker models the operator's session, and the FR label now reflects that (dashboard tab title,session_areapayload field).current_run_startedalso dropscourantforen cours— feminine agreements consistent throughout. FR-only, noen.json/ code /unique_idchange → zero entity migration. -
FEAT-09 (#96, PR #99) — New
sensor.<slug>_last_run_zones: display-ready joined zone-name string of the last closed session (fourth sibling of thelast_run_*family). Walkslast_finished_run["zones"], resolves eachboundary_idvia_zone_raw_name(HARD-15), joins with→. Fallback#<id>per boundary, no dedup (an interleaved run reads honestly asPrunier → Figuier → Prunier). Introduces the opt-in flagrefresh_on_zone_rename: bool = FalseonNavimowSensorEntityDescription— the sensor subscribes toSIGNAL_ZONE_NAMES_UPDATED_<entry_id>and re-renders on options-flow renames, no wait for the next mow. -
HARD-17 (#100, PR #101) — Propagate FEAT-09's
refresh_on_zone_renametosensor.<slug>_current_zone. Same situation:_current_zone_displayreadsconfig_entry.optionseach call. Before this change, the tile updated at the next ≤30 s coordinator tick after a rename; now it's instant, matching the per-zone entities'_refresh_namebehaviour (HARD-15). -
HARD-08 (#69, PR #102) — Declare
SensorDeviceClass.AREAon the two m² surface sensors (weekly_area,NavimowZoneSurfaceSensor). HA drives per-user unit conversion (ft², etc.) plus consistent icon/formatting and typed history graphs.native_valuestays m² — the conversion is HA-side. -
HARD-07 (#68, PR #103) — Consolidate the three byte-identical
DeviceInfo(...)constructions insensor.pyinto a single_device_info(coordinator)helper.NavimowSensor,NavimowPositionSensor, and the zone family (_NavimowZoneEntity+ aggregate) all route through it. Text and structure only —identifiersunchanged, so the device registry sees the exact same device. Companion note:binary_sensor.pyandlawn_mower.pycarry the same shape; deferred as a possible follow-up.
Operator-facing highlights
- Renaming a zone in Settings → Devices & Services → Navimow → Configure → Rename now refreshes the display instantly on both
current_zone(live) andlast_run_zones(frozen on the last close), on top of the per-zone entities that already refreshed live since HARD-15. - The dashboard's
Tonte en courstile row (_debut_/_duree_/_resultat_) gains its fourth sibling: Zones de la dernière tonte (FR) / Last run zones (EN). - FR display strings now read
Tonteinstead ofPassageon the six run/current_run/last_run entities. - The two m² surface sensors offer HA's unit picker (ft² if you want it).
Tests
359 passed locally (280 pre-existing + 60 new across the 5 PRs).
Install
HACS → Navimow → Redownload → select 1.1.0-raoul.18.
Then in HA: Settings → Devices & Services → Navimow — the new last_run_zones entity appears automatically; no reload needed.
Assets 2
NavimowHA v1.1.0-raoul.17 — HARD-15: zone_name attribute
6db35aa Prerelease raoul.17 on top of deploy @ 6db35aa.
Changes since raoul.16
- HARD-15 (#94, PR #95) — Expose the operator's raw zone name as attribute
zone_nameon the three per-zone sensors (sensor.<slug>_zone_<id>/_duration/_last_mowed). Templates now resolveboundary_id → operator namewithout regex-stripping the device prefix offfriendly_nameand without picking the surface sub-class to dodge thedurée/dernière tontesuffix leakage. Rename via Settings → Integrations → Navimow → Configure → rename zone reflects live on the attribute (via the existingSIGNAL_ZONE_NAMES_UPDATEDdispatcher, no reload). Reviewer's optional consolidation adopted:_current_zone_display(HARD-11) now also routes through the shared_zone_raw_namehelper, retiring the last duplicated options lookup.
Attribute contract
Each per-zone sensor gains two attributes common to the trio:
boundary_id(int) — stable join key, unchanged across an app-side rename.zone_name(str) — display-ready string: operator's rename orZone #<id>fallback (matches the entity title; intentional cosmetic divergence withcurrent_zone's shorter#<id>fallback — templates correlate onboundary_id, not the display string).
NavimowZoneSurfaceSensor keeps size_estimate / last_surface_precise / last_cmp_max / last_result; NavimowZoneLastMowedSensor keeps last_result. The base pair is merged in, not shadowed.
Motivation
Enables clean HA dashboard consumers (markdown templates, custom cards, notifications) to display operator-chosen zone names without brittle regex on friendly_name. Concrete out-of-tree consumer: the exterior dashboard "Historique des passages" markdown card retires its hardcoded {1: "Prunier", 3: "Figuier"} dict.
Tests
298 passed (280 pre-existing + 18 new in tests/test_hard_15_zone_name_attribute.py).
Install
HACS → Navimow → Redownload → select 1.1.0-raoul.17.
Assets 2
NavimowHA v1.1.0-raoul.16
fa56d23 BUG-14 — mp completion criterion refined with zone-cmp signal
Operator-observed on 2026-07-09: a single logical mowing session was split into two completed runs because BUG-09's completion criterion (mp >= 99 ∧ vs ∈ {1, 2, 3}) fired when the robot docked to recharge at mp = 99 with 15 % battery.
New completion rule: close on mp >= 100, OR on mp >= 99 ∧ zones[-1].cmp_max >= 10000. The zone-scoped cmp reaching 10000 is an independent firmware signal that the last active zone is 100 % mowed — distinguishes a real finish from a recharge return.
Effect on the 2026-07-09 day
- Morning mow:
mp = 99 cmp = 9901→ recharge dock → PAUSED_DOCKED (both below threshold, no false close). - Resume after recharge at 12:51 CEST:
mp = 99 cmp = 10000 sub = 231.77→ RESUME same run (FEAT-06),zones[-1].cmp_maxupdated to 10000. - Final dock arrival at 12:52 CEST → CLOSE COMPLETED via refined rule.
One session, session_area ≈ 132 m², correctly labelled.
Residual trade-offs
- Firmware tasks whose
mpplateaus at 99 AND whosecmp_maxnever reaches 10000 close via the sustained-timer path withinterrupted(2026-07-04 morning:cmppeaked at 9906). - Theoretical false positive on a multizone task where the robot finishes zone A (
cmp = 10000) and returns to dock to recharge before starting zone B. Not observed in the corpus.
280 tests passing.
PR #91 · Fixes #89 · Full timeline table in the PR description.