Commit Graph

1 Commits (f4de1e7cec9a08f6887e616c99fe8dd7ceb58154)

Author SHA1 Message Date
Amir Alexander Abdelbaki f4de1e7cec Build the Pebble watchapp
The floorplan on a wrist, rendering the same GET /floorplan/presence payload as
render/floorplan-3d. If the two ever disagree, one of them is lying.

An app rather than a face, because buttons decide it: watchfaces receive no
button events and Pebble restricts touch to apps, and cycling through rooms was
the point. That costs being the default screen, and it means the JS — therefore
the data — only lives while the app is open, so every launch begins with a
fetch. The last plan is persisted and drawn immediately WITH ITS AGE, then
repainted when the fetch lands: a spinner on a screen that already has something
true to say is worse than slightly stale truth, clearly labelled.

The screens
  Plan: occupied rooms light, empty dark, and a dotted edge for rooms HA never
  reports on — "nobody is in the study" and "nothing can see the study" are
  different sentences. Occupancy reads in lightness, never hue, because colour
  already means who. Fused positions are drawn where the person is; an
  unattributed radar target is a hollow ring with no colour and no initial,
  since every visual language here for a person is reserved for people the
  system can name.

  Detail: one room per screen, Up/Down wrapping at both ends, each person a
  coloured dot plus their name. "Nobody here" is printed rather than left blank,
  which reads as a fault. The last entry in the cycle is "Somewhere in the
  house" — the people it cannot place are exactly who you picked the watch up
  to find.

The wire format is the fragile part
  One packed byte array, ~80 bytes for a three-room plan with names, written in
  JS and read in C — two implementations of one format, in two languages, that
  never run in the same process. Drift between them decodes as plausible-looking
  garbage rather than as an error, so test/run-tests.sh builds a payload with the
  real writer and decodes it with the real reader compiled against a stub SDK. It
  also checks that a truncated payload is refused wholesale (half a plan looks
  exactly like a whole one) and that a version mismatch is refused rather than
  decoded.

  That test earned itself immediately: it caught a disagreement about whether a
  radar target in a room HA does not report should read as occupied or unknown.
  The code was right — a positive detection outranks no-data — and the test's
  expectation was wrong.

Unverified, and both are about the new hardware rather than the code: the Round
2's platform identifier, and its round-screen detection in the JS. Nothing
hardcodes a resolution, so both are one-line changes once the SDK names them.
It has never run on a watch, in an emulator, or against a real identity.

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