From 19b445f9bb4ede36aa6ae9332944c488cca148ab Mon Sep 17 00:00:00 2001 From: ruv Date: Mon, 25 May 2026 16:14:51 -0400 Subject: [PATCH] chore(adr-125 iter 1): fix C6 COM port + ship HAP-python reference impl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes from the ADR-125 e2e bootstrap session: 1. CLAUDE.md hardware table: COM4 -> COM12 for ESP32-C6 (the C6 + Seeed MR60BHA2 dev kit now enumerates on COM12 on ruvzen, not COM4 as previously documented). Same fix applied to the ESP32-S3 row (COM7 -> COM9) which CLAUDE.local.md already covered but the top-level table had not been updated. 2. scripts/hap-test-sensor.py — the ~80 LOC HAP-python sidecar that ADR-125 §2.1.a names as the reference implementation. Already running on ruv-mac-mini, already paired with operator's iPhone (paired_clients: 1), already round-trips a MotionDetected characteristic from a touch-file toggle through the HomePod (as Home Hub) to the Home app. Substrate validated for iter 2+: - C6 provisioned on ruv.net (IP 192.168.1.179, ch 5, RSSI -38) - UDP frames: 44 packets in 8s @ mac-mini:5005 (~5.5 pps) - HAP bridge paired and live Refs ADR-125, #794. Co-Authored-By: claude-flow --- CLAUDE.md | 4 +- scripts/hap-test-sensor.py | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 scripts/hap-test-sensor.py diff --git a/CLAUDE.md b/CLAUDE.md index 05f3bf63..33ed0713 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,9 +73,9 @@ All 5 ruvector crates integrated in workspace: | Device | Port | Chip | Role | Cost | |--------|------|------|------|------| -| ESP32-S3 (8MB flash) | COM7 | Xtensa dual-core | WiFi CSI sensing node | ~$9 | +| ESP32-S3 (8MB flash) | COM9 (ruvzen, was COM7) | Xtensa dual-core | WiFi CSI sensing node | ~$9 | | ESP32-S3 SuperMini (4MB) | — | Xtensa dual-core | WiFi CSI (compact) | ~$6 | -| ESP32-C6 + Seeed MR60BHA2 | COM4 | RISC-V + 60 GHz FMCW | mmWave HR/BR/presence | ~$15 | +| ESP32-C6 + Seeed MR60BHA2 | COM12 (ruvzen, was COM4) | RISC-V + 60 GHz FMCW | mmWave HR/BR/presence + WiFi CSI | ~$15 | | HLK-LD2410 | — | 24 GHz FMCW | Presence + distance | ~$3 | **Not supported:** ESP32 (original), ESP32-C3 — single-core, can't run CSI DSP pipeline. diff --git a/scripts/hap-test-sensor.py b/scripts/hap-test-sensor.py new file mode 100644 index 00000000..fa319389 --- /dev/null +++ b/scripts/hap-test-sensor.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +""" +hap-test-sensor.py — ADR-125 §2.1.a smoke test. + +Stands up a single HomeKit Accessory Protocol (HAP-1.1) bridge with one +child MotionSensor named "RuView Test Motion". Once paired in the Apple +Home app, the HomePod (acting as Home Hub) sees state changes when +TOGGLE_FILE (default /tmp/ruview-motion) is touched / removed. + +Usage: + python3 hap-test-sensor.py + +Pair from iPhone: Home app -> Add Accessory -> More Options -> "RuView Test Bridge". +The setup code is printed on stdout AND written to ~/.ruview-hap/setup-code.txt. + +Trigger motion: touch /tmp/ruview-motion +Clear motion: rm /tmp/ruview-motion + +State persists across restarts in ~/.ruview-hap/accessory.state. +""" + +from pathlib import Path +import os +import sys +import time +import signal + +from pyhap.accessory import Accessory, Bridge +from pyhap.accessory_driver import AccessoryDriver +from pyhap.const import CATEGORY_SENSOR, CATEGORY_BRIDGE + +STATE_DIR = Path(os.path.expanduser("~/.ruview-hap")) +STATE_DIR.mkdir(exist_ok=True) +STATE_FILE = STATE_DIR / "accessory.state" +SETUP_CODE_FILE = STATE_DIR / "setup-code.txt" +TOGGLE_FILE = Path(os.environ.get("RUVIEW_MOTION_TOGGLE", "/tmp/ruview-motion")) + + +class RuViewMotion(Accessory): + category = CATEGORY_SENSOR + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + serv = self.add_preload_service("MotionSensor") + self.char_motion = serv.configure_char("MotionDetected") + self._last = False + + @Accessory.run_at_interval(1.0) + def run(self): + present = TOGGLE_FILE.exists() + if present != self._last: + self.char_motion.set_value(present) + self._last = present + print( + f"[hap-test] MotionDetected -> {present} (toggle file: {TOGGLE_FILE})", + flush=True, + ) + + +def main() -> int: + driver = AccessoryDriver(port=51826, persist_file=str(STATE_FILE)) + + bridge = Bridge(driver, "RuView Test Bridge") + bridge.category = CATEGORY_BRIDGE + bridge.add_accessory(RuViewMotion(driver, "RuView Test Motion")) + driver.add_accessory(accessory=bridge) + + setup_code = driver.state.pincode.decode() if hasattr(driver.state.pincode, "decode") else driver.state.pincode + SETUP_CODE_FILE.write_text(str(setup_code) + "\n") + print(f"[hap-test] HAP bridge advertising as 'RuView Test Bridge'") + print(f"[hap-test] iPhone pair flow: Home app -> Add Accessory -> More Options") + print(f"[hap-test] Setup code (also in {SETUP_CODE_FILE}): {setup_code}") + print(f"[hap-test] Motion toggle file: {TOGGLE_FILE}") + print(f"[hap-test] State persists in: {STATE_FILE}") + + signal.signal(signal.SIGTERM, lambda *_: driver.stop()) + driver.start() + return 0 + + +if __name__ == "__main__": + sys.exit(main())