Commit Graph

19 Commits (0caa21fc421f46d5b2688aa3d7a371b715ec65ad)

Author SHA1 Message Date
Amir Alexander Abdelbaki 0caa21fc42 Add opt-in arrival push notifications; fix chores' unreachable container URLs
Rides the same arrival transition the visit log is built from, so "arrived" has
exactly one definition in this service rather than two that could disagree.

- notify_on_arrival (default off) is the "if enabled" half: push me when someone
  gets home. announce_arrivals (default ON) is a separate per-person opt-OUT of
  being announced, for anyone who doesn't want their comings and goings
  broadcast. The asymmetry is deliberate — if both defaulted off, ticking
  "notify me" would look broken until everyone else opted in too.
- Per-person notify_topic falling back to NTFY_DEFAULT_TOPIC. Topics are
  deduplicated, or a five-person household sharing one topic would get five
  identical pushes per arrival. The arriver is never told about themselves;
  subscribers who are away still are, since that's most of the point.
- The first sample after startup notifies nobody and establishes a baseline
  instead — otherwise a restart after a gap would fire "X just got home" for
  everyone already on the sofa. Costs one missed notification in that window.
- Face-recognition arrivals say "was just recognised at home", not "just got
  home" — the signals aren't equally reliable and the reader should know which.
- Pushes are sent outside _db_lock and isolated from each other, so a hanging
  ntfy can't stall request handling and one bad push can't swallow the rest.
- POST /people/<id>/test-notification, because the alternative way to find a
  typo'd topic is to wait for someone to walk in and notice nothing happened.

identity itself never touches the WAN — it POSTs to the ntfy already in this
stack. Whether the push reaches a phone that's AWAY is a network question, and
docs/network-integration.md's existing answer (WireGuard in, never a
port-forward) applies unchanged; iOS can't do this locally at all, since ntfy's
iOS app needs APNs and therefore an upstream relay. Documented as a table.

Also fixes two pre-existing bugs found while wiring this up: chores' template
pointed IDENTITY_URL and NTFY_URL at 127.0.0.1, which inside that container is
the container itself. chores would have reached neither identity nor ntfy, and
done nothing at all — silently, since both paths fail soft by design. Deployed
chores.env files still carry the old values and need editing by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:37:10 +02:00
Amir Alexander Abdelbaki 5c05b67a5a Add identity admin panel: nicknames, visit history, device rights, chore assignment
Extends Phase 6's identity rather than adding a service — all of this is facts
about who someone is, already identity's job.

- admin.html/admin.js: manage people/guests, edit every field, prune, history,
  access log. Deliberately not kiosk-shaped and not linked from any wall panel —
  a panel anyone can walk up to shouldn't carry a "prune these people" button.
- Nicknames are an input alias only. /resolve accepts them; every payload also
  carries speak_name (always the real name) and chores/ uses it for reminders.
  A nickname colliding with anyone's name or nickname is refused; a spoken string
  matching two people refuses with ambiguous_name rather than picking one.
- Visit history sampled from identity's own /presence. home:null writes nothing,
  ever — an HA outage must not record a departure that didn't happen. Departures
  need sustained absence (BLE flaps) and are stamped at the last time actually
  seen. "With whom" is an interval-overlap query, not a second table.
- Pruning splits selecting from deleting: the filter fills the checkboxes, the
  delete goes by explicit id, and the filter is never re-run at delete time.
- Device rights are an answer, never an action — identity stores grants and
  answers GET /device-access; HA asks and HA acts. Deny is the default and the
  only fallback. Grants can expire; every check is logged, denials included.
- Chore assignment prefers an assignee who's home but doesn't let an absent one
  block the chore (CHORE_ASSIGNMENT_STRICT flips that). Litter still ignores
  both exemption and assignment.
- Admin-added identifiers still enforce TRUSTED_ENTITY_PREFIXES; only the
  "in range right now" check is relaxed, never the allowlist.

Resolves open decision #26; adds #34-37 for the new unknowns (DEPARTURE_GRACE_
SECONDS is untuned, the panel has never been opened in a browser).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:05:14 +02:00
Amir Alexander Abdelbaki 2565cf99d0 Add trash/transit/chores services, RuView + BLE proxy firmware, network docs
Phase 19: trash-calendar (Kennelbach ICS -> CalDAV) and transit (GTFS
departures + OpenTripPlanner-backed voice route planning, slow walking
speed assumed). Phase 20: Tapo camera integration via identity's new
Frigate face-recognition presence signal, and chores, a presence/
calendar-driven household task nudger ("I don't care who does it, as
long as it gets done") with a passive fairness tally, litter culprit
attribution, and per-person chore_exempt/chore_reminder_style settings
on identity.

Also: the HA integrations + hardware-monitoring catalogs, Music
Assistant wiring, docs/network-integration.md (OPNsense VLAN
segmentation, no WAN port-forwards), the ESPHome BLE-proxy firmware,
and RuView CSI-presence integration (github.com/ruvnet/ruview) with
household automations for sleep/distress/concurrent-vitals/bathroom-
occupancy — all flagged with their real unverified assumptions and
open decisions. Fixed two real port collisions surfaced while wiring
this in (OTP vs. zigbee2mqtt on 8080, Music Assistant vs. pantry-vision
on 8095).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6CrKjW3yVptUnjG35HjC1
2026-07-30 16:53:24 +02:00
Amir Alexander Abdelbaki bad217c369 Add identity registration system and Phase 18 door panel
identity/ (Phase 6, rewritten from the original Node-RED sketch): a
person <-> BLE-identifier registry built directly as a Python service.
Solves multi-phone (multiple identifiers per person), anti-spoofing
(only allowlisted IRK-resolved/fixed-tag entities are ever accepted as
registration candidates, never a raw or randomized MAC), device-less
people (a grandmother without a smartphone gets a no_device registration
plus a hand-operated home/away toggle, reported as "unknown" rather than
a false "away"), and anonymous guests (POST /register/guest, no name
needed). Every person gets an automatic profile picture from their most
recent registration photo. /presence also reports a best-effort room per
person as groundwork for an eventual floor-plan view (not built).
Registration is single-utterance voice ("register me as <name>") or a
touchscreen form; ambiguous/conflicting candidates never auto-commit.

hosts/door-panel/ (Phase 18, new host): structurally kitchen-display's
twin - one Sway workspace, one Chromium kiosk window - defaulting to
identity's weather+clothing/who's-home/groceries-running-low dashboard,
with voice registration as its actual purpose (mic on by default, unlike
every other host's opt-in).

hosts/kitchen-display/: adds opt-in voice satellite and a "Show
registration" screen pointed at identity's register.html, reusing the
same camera-equipped-endpoint registration flow.

pantry-vision/: adds GET /shopping-list (Grocy's own volatile/missing
products, reshaped) for the door panel's "running low" section.

setup-container-host.sh: wires ENABLE_IDENTITY (identity + identity-web,
published like pantry-vision since kiosk browsers call it directly, plus
a persistent SQLite/photos volume unlike pantry-vision's stateless
design) and fixes the HA_URL example to the host's real LAN IP (HA runs
network_mode: host, unreachable by container name - the same situation
Node-RED's own config already documents).

docs/: Phase 6 rewritten, Phase 18 added, hardware/software/guardrail/
open-decision entries throughout project-plan.md and README.md.
components.md gains RuView presence nodes (one per room), fixed BLE tags,
and 2 spare webcams, with the price estimate updated to match.

Nothing here has been run against real hardware, a real HA instance, or
real Private BLE Device entities - TRUSTED_ENTITY_PREFIXES above all
needs checking against a live instance before registration finds
anything. See identity/README.md and hosts/door-panel/README.md for the
itemized verification lists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6CrKjW3yVptUnjG35HjC1
2026-07-30 13:40:04 +02:00
Amir Alexander Abdelbaki 87455c5343 Add ~16 RGB Zigbee smart bulbs to the components list
Compares innr RB 285 C (this project's own existing project-plan.md pick,
router-capable, confirmed zigbee2mqtt support), IKEA TRADFRI (cheapest but
not reliably on Amazon), and Sengled (cheap but not a Zigbee router, which
matters for whole-house mesh coverage) — picks innr. Count is a rough ~16
placeholder pending an actual per-room fixture count. Adds it to the
Lighting/Summed totals/Need/Price estimate sections; new subtotal
~€1,729-2,045.
2026-07-30 12:49:10 +02:00
Amir Alexander Abdelbaki 68253f31d0 Add Amirs Room Sound System; earmark the on-hand one for Living Room
5 Sound Systems total now (was 4) - have 1 (Living Room), need 4 more.
Updates the room list, summed totals, by-room tally, Need section, and
price estimate accordingly: ~€1,489-1,725 total.
2026-07-30 12:42:51 +02:00
Amir Alexander Abdelbaki 58d55c93d3 Confirm both pending Sound Systems (Linus Room, Kitchen)
Resolves the two "?" marks in the room list, summed totals, by-room tally,
and Need/price sections — all 4 Sound Systems are now confirmed, 3 more
kits needed (have 1), total ~€1,259-1,495. Linus Room's separate "???"
line is unrelated and still open.
2026-07-30 12:41:34 +02:00
Amir Alexander Abdelbaki 8f973a5111 Add a price estimate to the components Need list
Rough EUR figures per item (qty x unit price), a confirmed-need subtotal,
and a separate conditional subtotal for the two unconfirmed Sound Systems.
Reuses docs/project-plan.md's own figures where it has them (GPU, ESP32
board, amp, speakers); everything else is a market-price estimate flagged
as such, not a live-checked Amazon price.
2026-07-30 12:40:21 +02:00
Amir Alexander Abdelbaki 20df328b63 Add touch-panel mouse-emulated-touchscreen fallback; revise components.md shopping list
hosts/touch-panel/: cheap touch controllers sometimes report as an absolute-
position HID mouse instead of a real digitizer, which makes libinput hand
sway a type:pointer device instead of type:touch. Add a type:pointer
fallback (flat accel profile, no pointer acceleration, cursor hidden on
idle) so taps still work as single-touch clicks in that case, plus a udev
rule template (configs/udev/99-touchscreen-override.rules) to re-tag a
specific known device back to full wl_touch semantics once its USB vendor/
product ID is known. Documented in a new README section and reflected in
project-plan.md's hardware/guardrail/open-decision entries for Phase 16.

docs/components.md: replace the Lenovo ThinkSmart View pick for Loggia (a
locked-down Teams/Zoom appliance, not a general Linux box) with a cheap
all-in-one touchscreen PC, then supersede that with the Lenovo all-in-one
already on hand for that room — moved from Need to Have throughout. Also
adds a Need section: missing components broken into sub-parts (Sound
System, Voice Reciever) with real Amazon listings found via search.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6CrKjW3yVptUnjG35HjC1
2026-07-30 12:38:15 +02:00
Amir Alexander Abdelbaki 39a54adb8c Add summed totals to the hardware components list
Tallies servers/endpoints across all rooms and calls out the still-open
items (Linus Room's undecided line, the two unconfirmed Sound System
entries).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6CrKjW3yVptUnjG35HjC1
2026-07-30 11:38:15 +02:00
Amir Alexander Abdelbaki f3f870c4b4 Add Phase 16 touch panel and Phase 17 kitchen/fridge display endpoints
hosts/touch-panel/: a touch-driven Sway kiosk with a full Spotify GUI client
(Flathub), a dedicated Home Assistant Chromium kiosk window, a general web
browser, an always-on eww touch dock for app switching, an on-screen keyboard
(wvkbd), and touchpanel-agent giving HA/the LLM the same MQTT-mediated control
the thin client has.

hosts/kitchen-display/ + pantry-vision/: a camera-vision grocery cataloguing
endpoint for the fridge/pantry. The kiosk's browser captures a photo via
getUserMedia and sends it to the new pantry-vision service, which asks an
Ollama vision model to identify the item and estimate shelf life; the
proposal is always human-confirmed before being written into Grocy stock.
The same display shows inventory sorted by soonest-to-expire and Grocy's
recipes.

Wires ENABLE_PANTRY_VISION into setup-container-host.sh and documents both
phases in docs/project-plan.md and README.md, matching this project's
existing per-host hardware/software/guardrail/open-decision conventions.
Nothing here has been run against real hardware, a real camera, or a real
Grocy/vision-model instance — see each new README's verification list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6CrKjW3yVptUnjG35HjC1
2026-07-30 11:33:08 +02:00
Amir Alexander Abdelbaki bfca1c7eb0 Add idle-gallery weather/clock overlay and headless audio endpoint images
Two additions:

- Thin client: an eww weather/clock overlay over the idle-gallery slideshow.
  Time/date come from the local clock, weather from a new retained MQTT topic
  (smarthome/weather/current) an HA automation has to publish — degrades to
  clock-only if that never happens. idle-gallery.sh owns the overlay's
  visibility, mirroring how fullscreen-watcher.sh already owns the
  now-playing widget's.

- hosts/audio-endpoint/: headless, per-room-independent Spotify Connect
  appliances for rooms without a thin client, no shared/synced stream. Two
  build pipelines sharing one architecture-independent systemd
  unit/wrapper: arm64 (Raspberry Pi + HiFiBerry Amp2) via rpi-image-gen
  producing a real flashable .img, and amd64 (mini PC + USB DAC/amp) reusing
  the thin client's own live-build toolchain stripped of its graphical/kiosk
  stack. Both reuse the thin client's existing apt-first/documented-fallback
  Spotify Connect install logic rather than reinventing it. rpi-image-gen's
  exact config schema is flagged explicitly as unverified — no ARM build
  environment was available to validate it against the real tool.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WgTwMqiho8DT6ue5S49WBP
2026-07-29 11:37:29 +02:00
Amir Alexander Abdelbaki 1d8b416ab9 Add Phase 13 admin canvas, Phase 14 voice/display satellite, and thin-client capture-card input
Three additions to the thin-client/container-host stack:

- admin-canvas + admin-web (Phase 13): an on-demand display surface for the
  sys-admin-llm, mirroring digest-engine/digest-web's write/read split but as
  a small always-on token-gated write API instead of a scheduled job. New
  "4:admin" thin-client workspace, MQTT button, and duplicated canvas-sdk with
  stat/image/video/chart window kinds.

- ESPHome firmware for the Waveshare ESP32-S3-Touch-LCD-1.85C-V2 (Phase 14):
  a voice satellite + round-display status screen, evaluated as a DIY
  alternative to Home Assistant Voice PE. Media/cover-art always takes
  priority over an idle weather/time/date cycle, with a voice-state
  visualizer overlaid on top. On-device wake word (micro_wake_word) feeding
  the existing Assist pipeline, per-room "media_player_entity_id" wiring
  documented explicitly to avoid cross-room bleed. Validated with ESPHome's
  own config validator; not yet flashed to real hardware.

- Capture-card ("receiver box") video source selection on the thin client: a
  new "5:capture" workspace and HA select that dynamically enumerates
  USB/PCIe capture cards (re-scanned periodically for hot-plug), launches
  mpv against the chosen one, and unconditionally excludes the gesture-
  control camera from the list to preserve its existing no-HA-entity
  invariant.

Also fixes a pre-existing bug found along the way: digest-browser's pkill
pattern matched any --kiosk mpv/firefox process rather than its own, which
would have let the new admin-browser/capture-view scripts kill each other's
windows.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WgTwMqiho8DT6ue5S49WBP
2026-07-29 11:03:11 +02:00
Amir Alexander Abdelbaki 2682e16020 Add a counter-run verification pass as the digest's final hallucination filter
Every generated document now goes through a second, independent Ollama call
(synth/counter_run.py) before anything is written to output/: it checks quotes,
figures, named theoretical connections, and cross-source correlations against
the exact context the original synthesis pass saw, dropping anything that
doesn't trace back to it. A quote the model claims is grounded also gets a
deterministic substring-search backstop, since that's the one claim type
checkable without trusting the verifying call's own word for it.

The verifier shares the same RCI-derived theoretical basis as the document
it's checking, not a neutral outside standard — its job is confirming the
underlying facts are real and the theory genuinely matches their structure,
not flagging correct Marxist analysis as unverifiable for being theoretical
rather than a bare fact.

Fails safe in one direction only: a document that can't be verified at all
(Ollama unreachable a second time) is kept but marked unverified, never
silently passed through unchecked and never blanked outright. A document that
fails entirely is replaced with an honest "withheld pending verification"
placeholder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPD1RhBsjdFKFLpMCLiVT6
2026-07-28 16:05:29 +02:00
Amir Alexander Abdelbaki ad27b073df Sharpen political digest into a curation tool for party-meeting prep
Rewrites synth/prompts/political.md's editorial stance: prioritize material
economics (labour disputes, capital concentration, capital export, trade wars)
over horse-race/personality politics, curate and quote rather than summarize
everything, apply Marx's labour theory of value and Lenin's imperialism by
name when an entry's structure genuinely matches (e.g. a merger -> Lenin's
tendency towards monopoly), correlate findings across news/financial/traffic
data explicitly on the globe view, and add a hard anti-speculation rule so
every claim traces to something actually in the context.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPD1RhBsjdFKFLpMCLiVT6
2026-07-28 15:55:48 +02:00
Amir Alexander Abdelbaki 035874bb2e Add Phase 11 thin-client ISO and Phase 12 quarter-daily LLM digest
Implements both new subsystems from the project plan end to end:

- hosts/thin-client/: Debian live-build Sway kiosk image — greetd autologin,
  wayvnc (replaces RDP, per open decision #1), thinclient-agent (HA MQTT
  discovery + swaymsg control, the sole LLM-reachable surface), mpv/mpv-mpris,
  spotifyd/Steam Link, minimal-chrome Firefox with uBlock Origin/SponsorBlock
  preinstalled, a fullscreen-aware now-playing widget, persistent audio-output
  selection, outbound RDP/VNC via Remmina, HA mobile-app text/pointer remote
  control via ydotool, an optional opt-in gesture-control camera (MediaPipe
  hand tracking, off by default at two independent gates), a maintenance-shell
  keybind, a German default keymap, and an idle-timeout photo slideshow from a
  new gallery SMB share on the container host.

- digest-engine/: quarter-daily LLM digest — email/Signal/Telegram/Discord/
  WhatsApp ingestion (WhatsApp via a headful-Chromium bridge, opt-in, highest
  risk), news (including RCI/marxist.com theoretical sources and a broad
  mainstream/state-affiliated outlet list), financial indicators, flight/naval
  traffic and an OPNsense Suricata-alerts summary as political-analysis
  evidence, Grocy/CalDAV household ingestion, an evening-only recipe and
  shopping-list suggestion from soon-to-expire stock, and a vendored
  digest-canvas SDK (globe/window/glow) rendering compact and full views. A
  digest that goes unviewed (no thin-client canvas display or voice playback)
  is merged into the next run instead of being discarded, tracked via a
  retained MQTT message on the shared broker.

Both are wired into hosts/container-host/scripts/setup-container-host.sh via
the existing ENABLE_X/X_BLOCK pattern, off by default. docs/project-plan.md and
README.md are updated to match; remaining open decisions and manual-
verification items are tracked in-repo (project-plan.md §4, digest-engine/
README.md, hosts/thin-client/README.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPD1RhBsjdFKFLpMCLiVT6
2026-07-28 15:33:40 +02:00
Amir Alexander Abdelbaki 0cd49f9604 Add Node-RED, Netdata, Homepage, ntfy, Portainer, and restic backups to container host setup
Extends the container-host script to cover the rest of the Phase 1/9 stack
(README's outstanding checklist item) so a single Debian host setup gets
Home Assistant plus the full companion stack: automation glue, monitoring,
a dashboard, push notifications, container management, and optional
scheduled backups. Also fixes a stale comment referencing a network-attached
SLZB-06 coordinator when the plan and config target a USB CC2652P dongle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 08:57:50 +02:00
Amir Alexander Abdelbaki 0cc22f82b3 initial commit fixed 2026-07-03 08:52:34 +02:00
Amir Alexander Abdelbaki eeee51dabe initial commit 2026-07-03 08:50:41 +02:00