From 4e9feaaead5e5ebb778a422b9105018a6bda8176 Mon Sep 17 00:00:00 2001 From: Sergey Matyunin Date: Tue, 8 Sep 2026 15:20:49 +0300 Subject: [PATCH] docs: preserve manual radar setup entry (#485) Issue: #485 User-Visible: no --- docs/specs/485-radar-presence-stage1.md | 101 +++++++++++++++++++----- docs/specs/485-radar-presence.md | 19 +++-- 2 files changed, 94 insertions(+), 26 deletions(-) diff --git a/docs/specs/485-radar-presence-stage1.md b/docs/specs/485-radar-presence-stage1.md index c5d9960c..c47c46be 100644 --- a/docs/specs/485-radar-presence-stage1.md +++ b/docs/specs/485-radar-presence-stage1.md @@ -45,9 +45,11 @@ mutable state. Names may change in review without changing the contracts. Device editor → select an eligible device or placed-entity marker → **Presence on plan**. Owner clarification of 2026-09-08: show this section **only for -suitable devices**, not every device in the editor. Ineligible devices have -no section header, placeholder, disabled switch, unsupported teaser or radar -setup button. The global display preference below remains unchanged. +suitable devices**, not every device in the editor. Without eligibility there +is no main section header, placeholder, disabled switch, unsupported teaser or +primary radar setup button. Q5's accepted secondary manual-entry action below +is the deliberate route for unknown/custom hardware, not an automatic section. +The global display preference below remains unchanged. Section eligibility is distinct from live availability and from successful automatic source assignment: @@ -66,22 +68,50 @@ automatic source assignment: the saved version is unsupported. Unsupported/malformed saved content remains inert and preserved under the existing compatibility rules; showing repair grants no runtime or device-write capability. +- The user's explicit **This is a presence radar** action admits an otherwise + unrecognized real device or placed-entity marker to manual setup for the + current draft session. No positive manufacturer/profile descriptor or owning + HA `device_id` is required for this route. It neither preselects numeric + sources nor grants live, calibration or hardware-write capabilities. - A normal light, switch, temperature/humidity sensor, ordinary PIR, virtual marker, or a device with merely arbitrary numeric/binary entities is not a - radar candidate. Two numeric values or a generic occupancy/motion class alone - do not prove a radar profile. A virtual marker with inherited invalid radar + radar candidate automatically. Two numeric values or a generic occupancy/motion + class alone do not prove a radar profile. A virtual marker with inherited invalid radar content may show cleanup-only repair, never enable live setup. - Unresolved metadata with no saved radar configuration is not positive evidence: - do not flash a generic section while loading or offer setup on every unknown - device. Reevaluate the same exact device when registry data becomes available; + do not flash a generic section while loading. The secondary manual-entry + action remains available without that metadata. Reevaluate the same exact + device when registry data becomes available; never retarget to another device in order to make the section appear. -Use one derived eligibility result for the section entry and its child tools; -do not persist a new display flag or run source subscriptions merely to decide -whether a header is visible. Manual mapping remains available for an eligible -radar whose firmware/source roles need manual assignment; it is not an entry -point on unrelated devices. The resolver's concrete metadata signatures are -engineering choices with positive and negative fixtures, not a new UI taxonomy. +Use one derived eligibility result for the section entry and its child tools: +positive recognition, existing saved repair, or explicit session-local manual +entry. Do not persist a new display flag or run source subscriptions merely to +decide whether a header is visible. The resolver's concrete metadata signatures +are engineering choices with positive and negative fixtures, not a UI taxonomy. + +**Manual entry (Q5).** In the marker editor's collapsed **Additional actions** +group at the end of its body, offer **This is a presence radar** for a real +device/placed-entity binding that has neither positive recognition nor a saved +radar block. The group/action is secondary, keyboard/touch operable and governed +by normal editor write access; virtual markers never get it. This explicit +escape hatch is available even if registry identity is unresolved, including +a standalone `sensor` with no `device_id`. Ordinary devices still have no main +radar section unless the administrator deliberately takes this action. + +The action opens the same setup wizard at manual profile/source selection, +with no auto-picked profile/entities and no extra confirmation dialog. The +user chooses one of the generic profiles in §3 and exact sources, then follows +the existing setup/Save flow. Persist only the complete normal `marker.radar` +block on successful atomic Save, not an `is_radar` flag or a provisional block. +Cancel/Esc, leaving setup, closing the editor or page hide discards this +session-local declaration with the draft and releases any setup listeners. If +there was no eligibility beforehand, the main section stays hidden on return +or reopening. Saving makes subsequent access use the saved-configuration path, +including while disabled/offline. Removing that saved configuration restores +automatic-only eligibility and the secondary entry if still unrecognized. +Changing the marker binding clears an unsaved manual declaration; it must not +silently transfer consent or draft sources to another device/virtual marker. Resolve an `entity:` marker's owning device through the existing registry snapshot's entity `device_id` and `src/ha-binding-status.ts` authority. Inspect @@ -89,10 +119,12 @@ full available registry metadata, not only the enabled-state `dev.entities` projection: filtering out disabled diagnostic entities must not hide a known radar. Proposed `radar-model.ts` owns the pure section predicate; the existing conditional vacuum section in `houseplan-editor-runtime.ts` is a UI integration -precedent, not radar detection logic. Completely unidentifiable hardware with -no saved radar block cannot be automatically distinguished from an ordinary -PIR; it stays unqualified rather than making the section universal again. -This does not remove manual role assignment from an identified radar. +precedent, not radar detection logic. Completely unidentifiable hardware cannot +be automatically distinguished from an ordinary PIR; Q5's explicit manual +entry resolves this without making the section universal. An entity without an +owning device cannot auto-discover sibling sources but remains eligible for +explicit generic mapping. This does not remove manual role assignment from an +identified radar. For eligible, not-yet-configured radars the section is collapsed/off. No automatic enabling on upgrade, placement or source discovery. A virtual marker cannot own @@ -130,8 +162,21 @@ Do not infer axes/units from translated friendly names, or select an unrelated global entity with a similar name. A placed entity resolves its current registry device for discovery; on an eligible radar or existing saved repair path, missing/limited role metadata offers manual selection without claiming deleted -or disabled. With no positive radar evidence and no saved configuration, §2 -hides the section instead. Existing binding-status authority remains in use. +or disabled. With neither positive radar evidence nor saved configuration, §2 +keeps the main section hidden until explicit manual entry. Existing binding-status +authority remains in use. + +Manual generic mapping does not require an owning HA device, known firmware or +radar-specific `device_class`. It accepts exact, explicitly selected `sensor.*` +and `binary_sensor.*` roles required by the chosen profile, even when their +`device_id` is absent or differs. List only sources readable by the current +user; present their exact ids and device attribution (when known), with no +cross-device auto-binding or fuzzy-name fallback. Numeric profiles still require +their complete unit/axis/range conventions and finite values per this section. +For example, a custom numeric distance sensor may use `range_v1` without an +occupancy source or manufacturer signature; it yields an arc, not an invented +target point. Marker/space ownership and per-source read ACL remain mandatory. +Manual declaration never supplies a verified hardware adapter signature. For LD2450 automatic role mapping, require the same ESPHome device, recognized LD2450 descriptor and a complete unique role set exposed by registry metadata. @@ -446,7 +491,13 @@ Use exact space/marker ids, not arbitrary source arrays in ordinary subscribe. | `houseplan/radar/setup/inspect` | `{marker_id,draft_sources?}` | `may_write` + source read permission; bounded capability/source-value snapshot; no persistence/services | | `houseplan/radar/setup/subscribe` | `{marker_id,draft_sources?,expected_config_rev}` | Same guards; ephemeral setup values, <=1 draft subscription/user/marker, <=8 globally | -Draft sources use the same strict schema/size caps and owner checks. Setup +Draft sources use the same strict schema/size caps and owner checks: the +existing marker belongs to the requested space/configuration and is writable +under `may_write`; every explicitly selected source is independently readable. +Generic profiles do not require shared/non-null HA `device_id`. Same-device +checks remain mandatory for verified hardware adapter roles, not generic input. +Neither manual-entry consent nor frontend eligibility is authorization; the +server validates the draft profile/source graph itself. Setup does not gain permission to query arbitrary HA state using an unvalidated attribute path. Server rejects disabled/removed markers, stale revisions or invalid source graph before allocating listeners. Subscription ids use HA's @@ -470,6 +521,8 @@ handle values; every new dynamic error is mapped to a translated key. | Key | English | Русский | |---|---|---| | `title` | Presence on plan | Присутствие на плане | +| `additional_actions` | Additional actions | Дополнительные действия | +| `declare_radar` | This is a presence radar | Это радар присутствия | | `show_global` | Show presence on the plan | Показывать присутствие на плане | | `enable` | Configure presence on plan | Настроить присутствие на плане | | `show_live` | Show live presence | Показывать текущее присутствие | @@ -555,7 +608,8 @@ Every `S1-*` is required. Proposed test names are not claims of completed tests. | S1-16 | New/old frontend/backend safe; unsupported commands absent; original vacuum/Glow unaffected | rolling-matrix smoke, existing vacuum/light unit/golden; API capability bypass => unsupported-call assertion fails | | S1-17 | Editor lazy, active load bounded and no idle animation/subscriptions when disabled | bundle graph + baseline/candidate perf + backend timer count; eager editor or runaway listener => budget fails | | S1-18 | Text, status announcements and safe desktop/touch exit meet §2/5/8 | i18n parity, keyboard/touch smoke, reviewed screenshots; ordinary expected-vs-actual witnesses | -| S1-19 | Section appears only for eligible radars or an existing saved repair path; unavailable/no-target/manual-binding cases retain access, ordinary devices have no teaser; global preference remains visible without radars | unit eligibility matrix + device-editor smoke/golden for coordinate/range/zone/presence radars, light/switch/temperature/PIR/virtual/unknown devices and saved broken/future config; remove eligibility guard => ordinary-device header assertion fails; gate by on/valid coordinates => offline/range/manual repair assertion fails; hide global switch until first radar => empty-installation settings assertion fails | +| S1-19 | Main section appears only for recognized radars, an existing saved repair path or explicit session-local manual entry; unavailable/no-target/manual-binding cases retain access, ordinary devices have no automatic section/teaser; global preference remains visible without radars | unit eligibility matrix + device-editor smoke/golden for coordinate/range/zone/presence radars, light/switch/temperature/PIR/virtual/unknown devices and saved broken/future config; remove eligibility guard => ordinary-device header assertion fails; gate by on/valid coordinates => offline/range/manual repair assertion fails; hide global switch until first radar => empty-installation settings assertion fails | +| S1-20 | Secondary manual entry supports unknown/custom radars and standalone sources without device_id; only validated atomic Save persists eligibility, Cancel/exit/binding change does not; declaration grants no source or hardware privileges | device-editor smoke for unrecognized custom range sensor (no device_id or occupancy), explicit multi-device Cartesian bindings, save/reopen/disable/repair/remove and Cancel/reload; unit/backend ACL, invalid-unit and hardware-adapter negative fixtures. Require auto identity => generic-setup witness fails; persist declaration early => cancel-config comparison fails; bypass ACL or adapter proof => restricted-source/service canary fails | Implementation artifacts: `test/radar-geometry.test.mjs`, `test/radar-model.test.mjs`, `tests_backend/test_ha_radar.py`, shared JSON coordinate/source fixtures, @@ -613,3 +667,8 @@ owner questions. Atomic frames are not assumed for separate HA sensors. A backend reporting API is deliberately required for uniform normalization and future browser-independent recording. Exact shared coordinate fixtures, not historical ARCHITECTURE pixel dimensions, are the mathematical acceptance truth. +For Q5, session-local declaration state and the collapsed additional-actions +group are implementation choices for the accepted secondary-entry behaviour; +no extra persisted classification flag is necessary. Generic source ownership +means marker/space ownership plus per-entity authorization, not device-registry +membership. Hardware adapter identity is a separate verified capability. diff --git a/docs/specs/485-radar-presence.md b/docs/specs/485-radar-presence.md index dfc1edc3..3297ed01 100644 --- a/docs/specs/485-radar-presence.md +++ b/docs/specs/485-radar-presence.md @@ -25,7 +25,8 @@ closing this issue before all its AC are met requires an owner decision. Authority: `docs/SCOPE.md`, repository process, the issue's owner request, [UX proposal](https://github.com/Matysh/houseplan-card/issues/485#issuecomment-5583682816), [analysis](https://github.com/Matysh/houseplan-card/issues/485#issuecomment-5583878376), -and [accepted defaults](https://github.com/Matysh/houseplan-card/issues/485#issuecomment-5583909140). +and [accepted defaults](https://github.com/Matysh/houseplan-card/issues/485#issuecomment-5583909140), +including the [Q5 manual-entry decision](https://github.com/Matysh/houseplan-card/issues/485#issuecomment-5585013213). The numbered specs turn those requirements into testable contracts. Old open questions in the original issue are superseded by these explicit decisions; the external field report is evidence, not an instruction to copy a firmware. @@ -50,6 +51,7 @@ Original HA entities, automations and existing light Glow/spill remain untouched | Q2 default | Stages 1–2 use one selected room if its polygon exists. Stage 3 permits multiple allowed rooms. No invented room rectangle. | | Q3 default | Stage-2 raw recording starts manually, defaults to 1 h, each run <=24 h, each point expires after 24 h, works without a browser, editor-only access. | | Q4 default | Cross-radar consolidation is Stage 3; earlier stages make no global people count or identity claim. | +| Q5 default | Hide the device section until positive radar recognition, an existing saved configuration, or the user's explicit secondary action **This is a presence radar**. The action opens manual setup; Cancel saves nothing. Keep the global preference unchanged. | The requested weekly heat map has its own explicit, default-off collection action; it is not enabled by raw recording or live display. Each collection @@ -65,8 +67,12 @@ There is no new editor mode or separate fleet of person/device markers. Add configuration under the existing device/placed-entity marker in Device editor: **Presence on plan**, shown only for an eligible presence-radar device as defined by Stage 1 §2. Unsupported ordinary devices get no empty/disabled -section. An existing saved radar configuration retains its repair path even -when its sources become unavailable. This owner clarification of 2026-09-08 +section. Unknown/custom devices and standalone entities retain an explicit +secondary **This is a presence radar** action under additional device-editor +actions; this opens the same manual source wizard without automatic recognition. +Until that choice the main section stays hidden; cancelling unsaved setup leaves +it hidden and persists nothing. An existing saved radar configuration retains +its repair path even when its sources become unavailable. This owner clarification of 2026-09-08 does **not** condition the global Show presence on the plan preference on device discovery or first setup: that preference stays unchanged. Reuse the primary House Plan panel and full dashboard @@ -90,8 +96,11 @@ Named products in the initial issue are examples, not a claim that every firmware is automatically supported. Positively identified radars with unknown connection roles use manual bindings or an unsupported explanation, never guessed axes/units/writable capabilities. Hardware with neither positive radar -metadata nor a saved radar configuration does not get a generic setup section; -an arbitrary pair of numbers or occupancy class is not enough to qualify it. +metadata nor a saved radar configuration gets no automatic setup section, but +Q5's explicit manual entry preserves generic numeric/range/occupancy support. +An arbitrary pair of numbers or occupancy class is not automatic radar evidence; +user declaration is entry consent, not proof of source validity or device-write +capability. Exact manually selected sources still pass Stage 1 profile/ACL checks. ## 4. Shared architecture and authoritative state