mirror of
https://github.com/Matysh/houseplan-card
synced 2026-08-01 00:48:29 +00:00
The integration now records the path itself (trails.py): it watches the source entity's state changes, so recording needs no open card, has no multi-tab write races, and every screen sees the same line — reloads included, which retires the localStorage snapshot after one day of life. Stored per marker: the current run plus exactly one previous (owner call — users want cleaned-vs-uncleaned at a glance). The previous run renders at 40% opacity; the current one still trims its live tail so it never outruns the puck. Runs rotate on start or map switch, points cap at 2000 with decimation, store writes debounce 10 s, and houseplan_trail_updated pushes live cards. TrailBook is pure under 5 backend tests; the WS command degrades silently on older backends.
132 lines
7.2 KiB
Markdown
132 lines
7.2 KiB
Markdown
# Live robot vacuums on the plan — the spec (source of truth)
|
|
|
|
Status: approved by the owner 2026-07-31. Scope decisions final: all
|
|
three Tier-A adapters in P1, the trail ships in P1, the marker's placed
|
|
position IS the dock, and tap-to-clean is out — display only, no
|
|
commands (owner: «не нужно вообще»).
|
|
|
|
## Principle
|
|
|
|
The device marker «Пылесос» NEVER moves: it stands where the user
|
|
placed it — that is the base/dock — with its normal badge, states and
|
|
tap actions. While the robot cleans, a SECOND visual appears: a round
|
|
puck (the vacuum icon in a circle), no badge plate, with a soft
|
|
pulse, driving around the plan on live coordinates. Owner's exact
|
|
wording: «Иконка движущегося пылесоса должна быть круглой, без
|
|
подложки и с легкой пульсацией. Основное устройство "Пылесос" при этом
|
|
остается всегда на своем месте (база)». The puck is not clickable UI
|
|
chrome duplicating the device — a tap on it opens the same more-info as
|
|
the base marker. When the robot docks, the puck drives home and
|
|
dissolves into the base marker.
|
|
|
|
## Data tiers (auto-detected, zero user input)
|
|
|
|
- **Tier A — coordinates + own calibration data.** `vacuum_position
|
|
{x,y,angle}` (+ `calibration_points`, rooms, sometimes `path`):
|
|
Xiaomi Cloud Map Extractor, dreame-vacuum (Tasshack), Valetudo camera
|
|
(sca075). All three ship in P1.
|
|
- **Tier B — bare coordinates.** Roomba core (`position {x,y,theta}`),
|
|
raw Valetudo MQTT, template sensors. Works after manual calibration.
|
|
P3.
|
|
- **Tier C — current room only.** `current_room` / `current_segment`.
|
|
The room being cleaned gets a soft fill pulse + a 🤖 badge in the
|
|
room card; no puck. Segment→room matched by name, manual override in
|
|
the dialog.
|
|
- **Tier D — nothing.** Today's behaviour; yellow badge on the base
|
|
marker while cleaning per the «yellow = working right now» principle.
|
|
|
|
Tiers degrade gracefully: coords gone → room highlight; that gone too →
|
|
just the base marker.
|
|
|
|
## Coordinate binding
|
|
|
|
Affine transform (translate+rotate+scale+mirror), 6 numbers, least
|
|
squares over ≥3 point pairs. Stored per robot map:
|
|
`marker.vacuum.calibration[map_id]` — multi-floor robots get one matrix
|
|
per map; an active map without a matrix falls back to Tier C.
|
|
|
|
- **Path 1, auto-calibration by rooms (the default):** Tier-A adapters
|
|
expose the robot's room list with coordinates; our rooms carry HA
|
|
area bindings. Match by name, take centroids of ≥3 matches, solve,
|
|
show a live preview («робот сейчас здесь») — one click to confirm.
|
|
- **Path 2, the fit panel (replaced the 3-point wizard, owner call
|
|
2026-07-31):** the robot's rooms render as a translucent dashed ghost
|
|
over the plan; the user DRAGS the ghost into place and stretches it by
|
|
its four corner handles (uniform scale about the opposite corner, like
|
|
a graphics-editor frame). Rotation is quarter-turn buttons, mirror is
|
|
one toggle — both re-anchor about the ghost centre. Mirror defaults ON:
|
|
every robot map seen so far has Y flipped versus the screen. No numeric
|
|
fields; the old park-the-robot-three-times wizard is gone entirely —
|
|
it was the most fragile part of the feature.
|
|
- The panel folds into the same stored 6-number matrix
|
|
(S·R(rot)·mirror + offset); legacy matrices reopen in the panel with
|
|
the rotation snapped to the nearest quarter.
|
|
|
|
## Puck behaviour
|
|
|
|
Appears when the robot leaves `docked` AND live coords flow; CSS
|
|
interpolation ~1.2 s between updates; a data gap over 10 s teleports
|
|
without animation (no gliding through walls on sparse cloud updates).
|
|
Soft pulse always while visible (respects prefers-reduced-motion: the
|
|
pulse freezes, position still animates). Size: same --icon-size
|
|
system as devices, circle, transparent background, no badge plate.
|
|
Stale coords (>60 s while «cleaning»): puck freezes and dims. On
|
|
`docked`/`idle`-at-base the puck rides home and fades out. Hidden
|
|
markers render neither base nor puck (general hidden rules). Works in
|
|
the full card, static card and kiosk; in editors no puck — the base
|
|
marker is edited as usual.
|
|
|
|
## Trail (ships in P1)
|
|
|
|
- Integration `path` when available (Map Extractor) — transform and
|
|
draw as-is, includes history from before the card was opened.
|
|
- Otherwise self-recorded: client-side ring buffer, ~600 points with
|
|
Douglas-Peucker thinning, one SVG polyline.
|
|
- Style fixed, no options: cartography casing — a dark translucent halo under a light core. Neutral (pure black/white alphas) and readable over any room fill; blend modes were rejected: each has a blind luminance where the line vanishes, and mix-blend-mode is costly on old kiosk WebViews. Lifecycle:
|
|
appears on cleaning start, lives until dock + 10 min, dissolves;
|
|
`docked → cleaning` clears the old trail. Recorded SERVER-SIDE by the integration itself (trails.py): it watches the source entity, so the path records with zero cards open and every screen sees the same line. Stored per marker: the current run and ONE previous run (owner call — users compare cleaned vs uncleaned). The previous run renders at 40% opacity even at rest; the current run trims its live tail while moving. Rotation on run start or map switch; 2000-point cap with decimation; store writes debounced 10 s; houseplan_trail_updated notifies live cards.
|
|
- Marker option «Показывать след уборки», on by default where data
|
|
exists.
|
|
|
|
## Setup UX
|
|
|
|
A «Живая позиция» section in the device dialog, vacuum markers only:
|
|
status line (which source was found / room-only / nothing), one button
|
|
(«Настроить автоматически» for Tier A, «Калибровка по трём точкам» for
|
|
Tier B), three checkboxes (live position / trail / room highlight, all
|
|
on), and for multi-map robots a per-map calibration status list. The
|
|
source entity is discovered via the device registry — no YAML, no
|
|
entity pickers.
|
|
|
|
## Storage & validation
|
|
|
|
`marker.vacuum: { live, trail, room_highlight, source, calibration:
|
|
{[map_id]: [6 numbers]}, segment_map: {[segment]: room_id} }` — all
|
|
optional (old configs stay valid), matrices are 6 finite numbers,
|
|
backend-validated. Calibration is server-side state shared by every
|
|
screen, like the whole plan.
|
|
|
|
## Cases covered explicitly
|
|
|
|
Two vacuums (independent markers, calibrations, pucks); one robot on
|
|
two floors (maps ↔ spaces, the puck only renders in the space whose map
|
|
is active); robot outside the plan (±4 canvas bounds allow it, trail
|
|
clipped by viewport); integration restart changes map_id (rebind by
|
|
room-list match, else ask to recalibrate); user redraws rooms (the
|
|
matrix does not depend on rooms); demo stand gets a scripted synthetic
|
|
robot as the showcase.
|
|
|
|
## Out of scope
|
|
|
|
Commands of any kind (owner decision: display only) — no tap-to-clean,
|
|
no zone sending. No-go zones, cleaned-area polygons, cleaning history,
|
|
multi-robot collision avoidance — v2 candidates.
|
|
|
|
## Phases
|
|
|
|
- **P1:** adapter framework + all three Tier-A adapters +
|
|
auto-calibration by rooms + manual 3-point wizard + the puck + trail (both sources).
|
|
- **P2:** Tier C room highlight + the demo-stand scripted robot.
|
|
- **P3:** Tier B zoo (Roomba, raw Valetudo MQTT), Deebot, multi-map
|
|
polish by feedback.
|