Commit Graph

2 Commits (f4de1e7cec9a08f6887e616c99fe8dd7ceb58154)

Author SHA1 Message Date
Amir Alexander Abdelbaki 483cbce728 Exact positions inside a room, fused from radar and BLE
The 3D floorplan could say which room somebody was in. This makes it able to
say where in the room — from a different sensor, kept deliberately separate
from the one that knows who they are.

Room-level presence comes from BLE, which cannot give coordinates: RSSI-to-
distance is noisy enough that trilateration in a house lands in the wrong
room. Coordinates come from mmWave radar (LD2450-class), which tracks moving
targets and reports x/y — and cannot say who anybody is, because it sees a
moving blob.

So the two are fused, by a rule that refuses far more often than it commits:

    exactly one occupant in the room AND exactly one target in the room
        -> that target is that person
    anything else
        -> targets stay anonymous, occupants stay unpositioned

Two people in a room are two blobs that cannot be told apart. Guessing which
is which would put a name on the wrong person, and a display that does that
occasionally is worse than one that never tries — its wrong answers are
indistinguishable from its right ones. position_ambiguous says so out loud.

Which room a target is in is computed from the polygon rather than from which
sensor saw it: a radar in an open-plan kitchen sees into the living room, and
attributing by sensor would put people through walls.

identity
  floorplan_levels gains metres_wide; without it positions are not computed
  and the API reports that rather than guessing a scale. New floorplan_sensors
  table holds where each radar sits on the plan and which way it faces —
  drawn by a human, because a wrong rotation mirrors every target it reports
  and the result looks plausible rather than broken. Targets at exactly (0,0)
  are dropped: that is how these radars say "nothing here", and treating it
  as a detection grows a phantom person on top of every sensor.

render/floorplan-3d
  A fused person is drawn at their coordinate with a footprint dot, since a
  marker floated above the floor otherwise reads as further back in the room.
  An unattributed target is a hollow dashed puck with a question mark — no
  colour, no initial, because every visual language here for a person is
  reserved for people the system can name. A radar target lights the room even
  with nobody named: somebody is in there, and that the house cannot say who
  is a fact about the house.

Hardware: HLK-LD2450 added to components.md, ~EUR 15-25 per room, with the
advice to buy one and check its facing before buying more.

Position maths and the fusion rule are unit-tested headlessly. No radar has
been bought, mounted or read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FanS1vyE2gLhGkqKq6HtYj
2026-08-10 15:03:45 +02:00
Amir Alexander Abdelbaki a948f4b375 Workshop assistant, fleet monitoring, infra health, and shared endpoint surfaces
Adds the workshop/office assistant and the plumbing several other features
were waiting on. The through-line: every new capability that could act on
its own proposes instead, and says out loud when it does not know something.

New service — workshop/
  Project notebook (workshop.db) plus a never-pruned knowledge store
  (workshop-knowledge.db): standing workflow instructions by activity,
  keyword facts, durable project learnings, and the household's ONE hardware
  inventory. GET /context returns everything applying right now in one call,
  so the assistant is told the standing considerations rather than reminded
  of them. Two databases because they have different lifetimes: rebuilding
  the project store must not take the note about how you solder with it.

  Hardware statuses distinguish reserved (still on the shelf) from in_use
  (installed and working) — "can I use this right now" has different answers
  for the two, and naming a project on an in_use item never silently demotes
  it.

  Gitea repos with append-only history: commit/push/branch yes, unattended;
  force-push/rebase/amend/reset/filter-repo never, enforced server-side by
  branch protection rather than only by this code refusing. When history
  genuinely must be scrubbed, /scrub-request prints the commands for a human
  to run — the manual step is the safety mechanism.

  Fleet scripts: one monitoring-agent script per kind of machine, fetched by
  each endpoint's fleet-bootstrap timer. Remote code execution by design, so
  the constraints are the design — upload is a draft, publishing is separate,
  scripts live in SQLite rather than on the writable share, every version is
  kept, and the endpoint verifies the checksum and reports pass or fail.
  Slots exist for the ESP32s and network appliances that cannot run a script
  at all, holding the CheckMK-server-side config instead.

Infrastructure health
  opnsense becomes a LIST of firewalls, each named, keyed by name rather than
  index. CheckMK joins it. Both are polled by workshop (always-on) and read
  by digest-engine, so the digest can say "critical since Tuesday" instead of
  quoting a six-hour-old snapshot. Three states, because "I could not ask" is
  not "nothing is wrong".

pantry-vision
  All four stock movements are camera-driven; stock counts individual units
  and folds brand-free via Grocy product groups. Door-sensor-triggered
  appliance cameras record sightings as hints with timestamps, never as
  stock — a camera at a door cannot tell in from out.

identity
  Per-person colour and settable profile picture, assigned to avoid collisions
  between people sharing an initial, on the 2-bit-per-channel lattice a colour
  Pebble renders natively.

render/ — shared, vendored, dependency-free
  media-visualiser: two-tier by necessity, since most endpoints have no local
  audio; the synthetic tier says on screen that it is not an analysis.
  floorplan-3d: canvas 2D rather than three.js — the scene is prisms on a
  plane, which an isometric projection draws in ~200 lines, predictably on
  weak panels, with the frontend still at zero dependencies.

Config and fleet plumbing
  Rooms are one vocabulary (an HA area_id) from CoreSystemConfig through the
  builders to suggested_area. Keycloak and FreeIPA are coupled as one
  decision with USR_HA_ group naming, declaration-only for now and validated
  as such. Immich alongside the photo share, read-only. Thin clients get the
  full media-key set for a wireless remote.

Docs: fridge-item-location, workshop-assistant, rooms-and-endpoints,
endpoint-surfaces, pebble-presence-watchface.

Testing is stubbed suites and headless unit checks only — no real Grocy,
camera, vision model, CheckMK, Gitea, Samba or browser has been involved.
The CheckMK API shape and Gitea's branch-protection payload are written from
documentation and have version-sensitive field names.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FanS1vyE2gLhGkqKq6HtYj
2026-08-10 14:54:37 +02:00