Skip to content

Releases: raouldekezel/NavimowHA

NavimowHA v1.1.0-raoul.25 — BUG-19 step 2: ceiling-only vestige signature (absorbs BUG-16)

Choose a tag to compare

@raouldekezel raouldekezel released this 21 Jul 07:38
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é.

  • Suite : 415 passed.
  • Suit raoul.24 (step 1 — arming élargi aux états post-close, #116).
  • #114 / #92 restent ouvertes (à fermer par l'opérateur après validation in-vivo).

NavimowHA v1.1.0-raoul.24 — BUG-19 vestige guard widened to post-close states

Choose a tag to compare

@raouldekezel raouldekezel released this 20 Jul 16:41
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_zone

An 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 armed sub fails the signature.
  • BUG-16 (#92) frozen-sub variant: 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_END decoupling, observability line: unchanged.

Test coverage

19 dedicated tests in tests/test_bug_17_run_start_vestige_guard.py:

  • 4 new: post-close STATE_COMPLETED drop, post-close STATE_INTERRUPTED drop (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_vestige docstring 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_progress starts near 1 %, not 100 %.
  • home-assistant.log carries one run_tracker: type-2 rejected — run-start vestige (mp=100 cmp=10000 sub=0.0 ...) DEBUG line at the first type-2 after the docked → mowing transition.
  • sensor.<slug>_last_run_started anchors 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 = 10000 with the real value.

Refs

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).

NavimowHA v1.1.0-raoul.23 — BUG-17 bypass diag

Choose a tag to compare

@raouldekezel raouldekezel released this 20 Jul 16:14
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 5duration_ms = 0, session_area = 0.0, zones = [{boundary_id: 1, sub_entry: sub_exit: 0.0, cmp_max: 10000}]. Classic BUG-13 (#86) zero-sub phantom, now corroborated by a real Store dump instead of just a wire hypothesis.
  • history[] row 10 — same 0-second phantom shape but with sub_entry = sub_exit = 231.77 (frozen at the 2026-07-09 mini-run's sub). BUG-16 (#92) frozen-sub sibling.

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 narrative
  • 01_vestige_and_first_run.mqtt.log — HA log 11:21:50 → 11:25:35 CEST
  • 02_run_close.mqtt.logrun_finished event
  • 03_store_after_event.json — Store dump, 14 history[] entries

PII redacted (robot serial + MQTT client id scrubbed throughout; MQTT credentials elided by HA at the source).

Refs

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.

NavimowHA v1.1.0-raoul.22 — BUG-17 vestige guard

Choose a tag to compare

@raouldekezel raouldekezel released this 20 Jul 09:17
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_run read in the IDLE order — anchoring start_time, sub₀, mow_start_type on the vestige's fields;
  • seeded zones[0].cmp_max = 10000 via _update_zone, sticking sensor.<slug>_current_zone_progress at 100 % for the whole zone segment (~1 h 50 min in the operator's 2026-07-19 trace);
  • flashed sensor.<slug>_current_run_progress to 100 for one cadence (visible as 100 → 0 → 1 → …);
  • stamped zones[0].first_time at the vestige packet's time, misdating FEAT-08's last_complete_pass_at;
  • over-stated Store.last_cmp_max on 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_state opens a run, so the vestige arrives while state == STATE_IDLE).
  • Second disjunct — the BUG-06 sentinel path: session-init boundary = 0 ∧ mp = 0 ∧ cmp = 0 ∧ action = -1 opens the run through process_type2, but _update_zone rejects boundary = 0 so zones == []. A vestige at packet position 2 (before the real boundary lands ~60 s later) must still be dropped.
  • STATE_COMPLETED / STATE_INTERRUPTED deliberately 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 vestige line ~85 ms after the state → mowing transition (or at the first wire packet in the sentinel order).
  • sensor.<slug>_last_run_started anchors 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 constants MP_TASK_END, RUN_START_SUB_TOLERANCE; new helper _gate_run_start_vestige; single call site at the top of process_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).

NavimowHA v1.1.0-raoul.21 — BUG-18 followup + BUG-17 diag corrections

Choose a tag to compare

@raouldekezel raouldekezel released this 19 Jul 18:56
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 time field is 163 ms before the state → mowing transition (not 85 ms — that's the packet delivery delay AFTER the transition; both deltas now kept distinct in the copy).
  • Figuier size_estimate_updated_ms typo 17844540683831784460068383 (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.tsv regenerated packet-synced (each row's (mp, cmp) pair comes from the same type-2 packet — 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.md now 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:

  1. mp = 100 arrives in late-delivery at task-transition (3 h 20 gap observed).
  2. cmp is zone-persistent across sessions (Figuier resumed at cmp = 4404 after 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.

NavimowHA v1.1.0-raoul.20 — BUG-18 fix + BUG-17 diag

Choose a tag to compare

@raouldekezel raouldekezel released this 19 Jul 18:30
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_progress stuck at 100 %, it corresponds to the case documented in this issue.

NavimowHA-v1.1.0-raoul.19

Pre-release

Choose a tag to compare

@raouldekezel raouldekezel released this 10 Jul 16:29
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), attrs boundary_id, zone_name, area_precise, last_complete_pass_at. None until the first complete pass — no fake 0 fallback.
  • sensor.<slug>_zones_total_area — aggregate Σ zone total area. Attrs zone_ids, zone_names (parallel list, live-refreshed on renames), area_precise.
  • sensor.<slug>_last_run_area — promoted from the session_area attribute of last_run_result. ceil state + area_precise attr.

Renames (with entity_registry.async_migrate_entries so HA history survives)

  • run_statecurrent_run_state
  • run_progresscurrent_run_progress
  • zone_progresscurrent_zone_progress
  • zone_<id> (last-mow surface) → zone_<id>_last_area
  • zone_<id>_durationzone_<id>_last_duration

Uniform contract on every m² sensor

  • state = ceil int, attr area_precise = float. Applied to weekly_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_area removed.
  • zones (count aggregate): total_area + per_zone removed. zone_ids stays.
  • zone_<id>_last_area: size_estimate removed.

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_mowed false-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 to zone_<id>_total_area as a belt-and-braces for anyone who installed the branch head before the naming pass.

NavimowHA v1.1.0-raoul.18 — 5 PRs: FEAT-09, HARD-07/08/16/17

Choose a tag to compare

@raouldekezel raouldekezel released this 10 Jul 13:15
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 → tonte on the six run_* / 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_area payload field). current_run_started also drops courant for en cours — feminine agreements consistent throughout. FR-only, no en.json / code / unique_id change → 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 the last_run_* family). Walks last_finished_run["zones"], resolves each boundary_id via _zone_raw_name (HARD-15), joins with . Fallback #<id> per boundary, no dedup (an interleaved run reads honestly as Prunier → Figuier → Prunier). Introduces the opt-in flag refresh_on_zone_rename: bool = False on NavimowSensorEntityDescription — the sensor subscribes to SIGNAL_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_rename to sensor.<slug>_current_zone. Same situation: _current_zone_display reads config_entry.options each 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_name behaviour (HARD-15).

  • HARD-08 (#69, PR #102) — Declare SensorDeviceClass.AREA on 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_value stays m² — the conversion is HA-side.

  • HARD-07 (#68, PR #103) — Consolidate the three byte-identical DeviceInfo(...) constructions in sensor.py into a single _device_info(coordinator) helper. NavimowSensor, NavimowPositionSensor, and the zone family (_NavimowZoneEntity + aggregate) all route through it. Text and structure only — identifiers unchanged, so the device registry sees the exact same device. Companion note: binary_sensor.py and lawn_mower.py carry 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) and last_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 cours tile row (_debut_ / _duree_ / _resultat_) gains its fourth sibling: Zones de la dernière tonte (FR) / Last run zones (EN).
  • FR display strings now read Tonte instead of Passage on 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.

NavimowHA v1.1.0-raoul.17 — HARD-15: zone_name attribute

Choose a tag to compare

@raouldekezel raouldekezel released this 09 Jul 17:29
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_name on the three per-zone sensors (sensor.<slug>_zone_<id> / _duration / _last_mowed). Templates now resolve boundary_id → operator name without regex-stripping the device prefix off friendly_name and without picking the surface sub-class to dodge the durée / dernière tonte suffix leakage. Rename via Settings → Integrations → Navimow → Configure → rename zone reflects live on the attribute (via the existing SIGNAL_ZONE_NAMES_UPDATED dispatcher, no reload). Reviewer's optional consolidation adopted: _current_zone_display (HARD-11) now also routes through the shared _zone_raw_name helper, 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 or Zone #<id> fallback (matches the entity title; intentional cosmetic divergence with current_zone's shorter #<id> fallback — templates correlate on boundary_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.

NavimowHA v1.1.0-raoul.16

Pre-release

Choose a tag to compare

@raouldekezel raouldekezel released this 09 Jul 13:36
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.77RESUME same run (FEAT-06), zones[-1].cmp_max updated 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 mp plateaus at 99 AND whose cmp_max never reaches 10000 close via the sustained-timer path with interrupted (2026-07-04 morning: cmp peaked 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.