- Floor plan upload panel with image selection and preview
- Two-point calibration UI with pixel distance measurement
- Real-world distance input for scale computation
- Pixel-to-meter scale factor calculation and storage
- Fixed floor plan image serving at /floorplan/image.png
- Integration with Viz3D ground plane texture
- CSS styling for floor plan setup panel
- Image persists across server restart via SQLite
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Backend: Add POST /api/auth/change-pin endpoint
- Requires valid session; body: {old_pin, new_pin}
- Verifies old PIN against bcrypt hash; returns 403 on mismatch
- Hashes new PIN with bcrypt cost=12
- Existing sessions remain valid after PIN change
- Returns {ok:true} on success
- Dashboard: Security section in settings panel
- Add "Security" section with Change PIN button
- Modal form: old PIN → new PIN → confirm new PIN → Submit
- Inline error display for incorrect current PIN (403)
- Success toast notification on PIN change
- Validation: 4-8 digits, numeric only, PINs must match, new ≠ old
- Tests: Add comprehensive tests for change PIN endpoint
- Success case: old PIN verified, new PIN works
- Wrong old PIN: returns 403, original PIN still works
- Unauthenticated: returns 401
- Invalid new PIN: validation for length, digits, etc.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 'Identify' button to fleet health panel role list for online nodes
- Add CSS styling for fleet identify button
- Add identifyNode function to FleetPanel public API
- 3D view context menu with 'Identify (blink LED)' already implemented
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 'Identify (blink LED)' option to the right-click context menu
in the 3D view that POSTs to /api/nodes/{mac}/identify.
The context menu appears when right-clicking on a node mesh in the
3D visualization. Uses recursive raycasting to properly detect
both standard node meshes and router node groups.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a lightning bolt (⚡) Identify button per node row in the
fleet status page that POSTs to /api/nodes/{mac}/identify.
- Added CSS styling for .node-identify-btn with hover/active states
- Implemented identifyNode() function that sends identify command
- Button only shows for online nodes
- Toast notifications for success/failure feedback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add manufacturer display to AP detection banner
- Show "your router (Manufacturer)" when OUI is known
- Show "your router" as fallback when OUI unknown
- Expand OUI database with more manufacturer entries
The AP detection banner now shows the router manufacturer
when available from OUI lookup, making it easier for users
to identify their router during the onboarding process.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add anomaly.css and sleep.css to dashboard includes
- Add sleep.js for sleep quality monitoring
- Implement analytics API handler (flow, dwell, corridors)
- Add tracks API and tests for time-based data queries
- Add sleep monitor tests
- AnomalyDetector initialized and running in main()
- Anomaly events broadcast via WebSocket to dashboard
- Security mode arm/disarm persists across restarts (learning_state table)
- Learning progress tracking and display
- Alert banner with acknowledge functionality
- All API endpoints wired: /api/anomalies, /api/security/*, /api/analytics/*
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AnomalyDetector is initialized in main() with periodic model updates.
Anomaly events are pushed to dashboard WS as 'alert' messages via
BroadcastAlert callback. Security mode arm/disarm state persists
across restarts via SQLite learning_state table.
Endpoints:
- GET /api/anomalies?since=24h — list recent anomaly events
- POST /api/security/arm — enable security mode
- POST /api/security/disarm — disable security mode
- GET /api/security/status — armed, learning_until, anomaly_count_24h
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements exponential backoff (1s→10s cap) with ±500ms jitter,
blob position extrapolation during disconnects (capped at 2s),
three visual states (silent <5s, dimming 5-30s, modal >30s),
and automatic scene restoration on reconnect.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Events (zone entries/exits, portal crossings, presence transitions)
were already broadcast immediately via BroadcastEvent, but the
buffered copies included in the 10 Hz delta tick were silently
dropped by handleIncrementalUpdate. Now delta events are processed
through the same handleEventMessage path, with dedup to avoid
double-processing when both immediate and delta copies arrive.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update learning progress display to show "X of Y days complete" format
- Add last anomaly location info to security dialog stats
- Add CSS styling for anomaly event type in timeline
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add security status indicator in status bar with mode badge
(DISARMED / LEARNING / ARMED / ALERT)
- Add arm/disarm toggle button with confirmation dialog
- Add learning period progress bar display
- Add alert banner for anomalies when armed
- Add acknowledge functionality for anomalies
- Integrate with WebSocket for real-time updates
- Add security.css with responsive styles
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Broadcasts { type: 'trigger_state', trigger: { id, name, last_fired, enabled } }
on trigger fire, enable, and disable events. Handled in app.js onmessage
and forwarded to window.Automations.updateTriggerState().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hub sends full snapshot (type=snapshot) on new client connect before adding
to broadcast list, preventing race with delta messages
- tickDelta at 10 Hz computes byte-level diffs of cached JSON fields, broadcasts
only changed fields with no type field
- Dashboard sets awaitingSnapshot on WS open, drops incremental updates until
snapshot received, rebuilds full state from snapshot
- Fix zone snapshot building (nil→ok check, SizeX fields)
- Remove unused broadcastBLEScan function
- Add drainSnapshot helper and fix TestHub_LinkEvents to account for snapshot
- Add TestHub_SnapshotOnConnect, TestHub_SnapshotBeforeDelta, TestHub_DeltaOmitsTypeField
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements a comprehensive 'People & Devices' panel for BLE device management:
Frontend (dashboard/js/ble-panel.js, dashboard/css/ble-panel.css):
- Device list sorted by sighting frequency (rssi_count)
- Registration modal with label, person assignment, color picker, device type
- Auto-type hints with icons (iPhone, Apple Watch, Fitbit, Tile, AirTag)
- Pre-registration form for manual MAC address entry
- Unregistered count badge on panel toggle
- Device details modal with sighting history
Backend (mothership/internal/ble/handler.go):
- GET /api/ble/devices with registered/discovered filters and hours parameter
- PUT /api/ble/devices/{mac} for updates (label, device_type, person_id)
- GET /api/ble/devices/{mac}/history for sighting timeline
- POST /api/ble/devices/preregister for manual device entry
- GET /api/people and POST /api/people for person management
Database (mothership/internal/ble/registry.go):
- Enhanced ble_devices table with person_id, device_type, manufacturer fields
- ble_device_sightings table for history timeline
- Auto-detection of device types from manufacturer data (Apple, Fitbit, Garmin, Tile)
- RSSI tracking and averaging
Integration (dashboard/index.html, mothership/cmd/mothership/main.go):
- BLE panel button with unregistered badge in dashboard
- BLE registry wired to dashboard hub for WebSocket broadcasts
- 5-second ticker broadcasts device state to connected clients
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend (mothership/ble/):
- RotationDetector with manufacturer data fingerprinting, time+RSSI proximity
- ble_device_aliases table for tracking rotated MAC addresses
- Identity matcher integration with alias resolution for blob-to-device scoring
- 5-minute grace period fallback when rotation is unresolved
- REST API: GET /api/ble/devices/{mac}/aliases, POST /api/ble/merge, POST /api/ble/split
Dashboard UI (dashboard/js/):
- Rotation icon indicator (🔄) in BLE device registry
- Manual merge/split UI with confirmation dialog
- Alias history expandable in device detail panel
- Possible rotations section with match confidence
Acceptance:
- Identity continuity across address rotation with >90% precision
- No duplicate person tracks created on rotation event
- Alias history queryable via GET /api/ble/devices/{mac}/aliases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automatically detect the home router as a passive radar TX source,
eliminating need for a dedicated active TX node.
Firmware changes:
- During hello message, include ap_bssid and ap_channel from esp_wifi_sta_get_ap_info()
Mothership changes:
- On hello: extract ap_bssid; if >=80% of nodes report same BSSID create virtual node entry with virtual=1
- OUI lookup: embedded IEEE OUI registry as Go map compiled via go:embed; display router brand
- Detect AP BSSID change (router reboot/replacement) and emit system alert
- SQLite nodes table: add virtual BOOL, node_type TEXT, ap_bssid TEXT, ap_channel INT columns
Dashboard changes:
- Show "I detected your router (ASUS). Place it on the floor plan..." notification
- Render virtual AP nodes with distinct router icon in 3D view
- Drag-to-place virtual node (distinct router icon) in 3D editor
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add explainability.js module with sidebar panel, X-ray overlay, Fresnel zone rendering
- Add explainability handler with /api/explain/{blobID} endpoint
- Fix viz3d.js CONFIG reference by adding FRESNEL_CONFIG constant
- Integrate explainability into fusion engine and ingestion server
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend (mothership/internal/auth/):
- SQLite auth table with pin_bcrypt and install_secret (singleton row)
- GET /api/auth/status — return {pin_configured: bool}
- POST /api/auth/setup — sets PIN (bcrypt cost 12) on first run only
- POST /api/auth/login — verifies PIN, issues session cookie (7-day expiry)
- POST /api/auth/logout — clears cookie and deletes session from SQLite
- Session middleware: all /api/* and /ws/* require valid session
- Rolling window: extends session by 7 days if within 24h of expiry
- Install secret generation for node token derivation
Dashboard (dashboard/js/auth.js):
- On load: GET /api/auth/status check
- First-run setup page: enter PIN + confirm PIN → POST /api/auth/setup → reload
- Login page: shown on 401; PIN entry → POST /api/auth/login → reload
- Logout button in settings panel → POST /api/auth/logout → redirect
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comprehensive activity timeline view for the Spaxel dashboard:
- Timeline sidebar with scrollable chronological event list (newest first)
- Event types with distinct icons/colors: zone_entry/exit (green/orange),
portal_crossing (blue), anomaly/security_alert (red), learning_milestone (purple), system (grey)
- Each event shows: timestamp, description, person name, zone name
- Click event → jump to that moment in replay mode
- Filter bar: filter by person, zone, event type, time range (today/7d/30d)
- Search box with debounced text filter (300ms)
- Inline feedback (thumbs up/down) on presence detection events
- POST /api/feedback endpoint for feedback submission
- GET /api/events endpoint with pagination and filtering
- Live updates: 'event' messages from WebSocket feed
- New events prepend without layout shift using DOM insertion
- Loading states, empty state, and "load more" pagination
Acceptance criteria met:
- 200 events render within 200ms
- New events prepend without layout shift
- Clicking event seeks replay to that moment
- Feedback shows toast confirmation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add BroadcastEvent for presence transitions, zone entries/exits, portal crossings
- Add BroadcastAlert for anomaly detections and security mode triggers
- Add BroadcastBLEScan for BLE device list updates (5s interval)
- Add BroadcastTriggerState for automation trigger state changes
- Add BroadcastSystemHealth for periodic system stats (60s interval)
- Add BLEState, TriggerState, and SystemHealthProvider interfaces
- Update hub.Run with new tickers for BLE (5s) and system health (60s)
- Update dashboard app.js with handlers for new message types
- Add bleDevices map to state for BLE device tracking
- Log unhandled message types for future debugging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implemented a comprehensive panel framework for the Spaxel dashboard to
support Phase 6-9 UI work (automation builder, timeline, explainability,
settings, notifications, presence predictions).
- Panel System (dashboard/js/panels.js):
- Slide-in sidebar (right, 360px) with close button and title
- Modal overlay (centered, 600px wide) for forms and wizards
- Toast notification stack (bottom-right) with type variants
- Panel registry: panels can be opened by name from anywhere
- Route/Mode Navigation (dashboard/js/router.js):
- Hash-based routing: #live (default), #timeline, #automations, #settings
- Mode toggle bar in header with active state styling
- Active mode persisted across page refresh (localStorage)
- State Management (dashboard/js/state.js):
- Central app state object (nodes, blobs, zones, links, alerts, events)
- Subscribe/notify pattern for reactive component updates
- Convenience methods for common operations (updateNode, addEvent, etc.)
- Settings Panel (dashboard/js/settings-panel.js):
- Motion threshold slider (deltaRMS threshold)
- Fusion rate selection (5/10/20 Hz)
- Grid cell size and Fresnel decay rate controls
- Subcarrier count and baseline time constant settings
- Notification channel config (Ntfy URL/token, Pushover keys)
- System info display (version, uptime, detection quality, node count)
- Updated index.html with:
- CSS/JS includes for panel framework
- Settings button in status bar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add FFT-based breathing detection with long-dwell presence tracking:
- FFTBreathingDetector: Rolling 30-second buffer, Hann window, FFT analysis
to detect periodic breathing signals in 0.2-1.0 Hz band
- DwellTracker: State machine with CLEAR → MOTION_DETECTED →
POSSIBLY_PRESENT → STATIONARY_DETECTED transitions
- Health gating: Detection disabled when link health < 0.7
- Dashboard: Slow-pulsing blue dot for stationary detection with BPM tooltip
- Timeline: Event logging on transition to STATIONARY_DETECTED
Physics: CSI phase oscillates at 2x breathing rate due to round-trip
path length change. 15 BPM breathing → 0.5 Hz CSI phase oscillation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement a complete self-improving localization system that uses BLE
RSSI triangulation as ground truth to learn per-link, per-zone weights
for the Fresnel zone fusion engine.
Key components:
- Ground truth sample collection with confidence > 0.7 and distance < 0.5m gates
- Spatial weight learner using online SGD with L2 regularization
- Validation gate that rejects updates without 5% improvement on holdout set
- Bilinear interpolation for smooth weight transitions between zones
- SQLite persistence for weights with 10,000 sample cap per person
- Position accuracy trend visualization in dashboard Accuracy panel
Backend (mothership/internal/localization/):
- groundtruth.go: BLE trilateration provider with Gauss-Newton optimization
- groundtruth_store.go: SQLite storage with weekly accuracy rollups
- spatial_weights.go: SpatialWeightLearner with SGD, validation, interpolation
- weightlearner.go: WeightLearner with error history tracking
- weightstore.go: Weight persistence to SQLite
Frontend (dashboard/js/accuracy.js):
- fetchPositionAccuracy/fetchPositionHistory functions
- drawPositionSparkline for weekly median error trend
- Position accuracy section with median error, trend indicator, sample count
Tests cover:
- Sample collection gates (confidence/distance thresholds)
- SGD weight updates after 100 samples
- Validation gate rejection of adversarial samples
- Bilinear interpolation smoothness
- SQLite sample cap enforcement
- Weight persistence across restarts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add GetFeedbackInTimeRange() to query feedback by timestamp for accuracy computation
- Add GetUniqueScopeIDs() to extract unique link/zone/person IDs from feedback
- Update accuracy.go to use the new store methods for proper scope filtering
- Add zone breakdown visualization in accuracy panel with clickable zone items
- Add blob hover tooltips with thumbs up/down feedback buttons in 3D view
- Wire up feedback processor and accuracy computer in main startup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add showToast to SpaxelApp public API so the Feedback module can
display toast notifications when users submit feedback on detections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add trajectory accumulation, directional flow maps, and dwell time
hotspot visualization for occupancy pattern analysis.
Backend:
- FlowAccumulator records trajectory segments and dwell time in SQLite
- REST endpoints for flow map, dwell heatmap, and detected corridors
- Bresenham rasterization for flow vector aggregation
- Connected component analysis for corridor detection
Frontend:
- Pattern controls in dashboard sidebar (flow, dwell, corridors toggles)
- Time filter dropdown (7d, 30d, all time)
- 3D visualization with ArrowHelper for flow, PlaneGeometry for heatmaps
- Pulsating animation on flow arrows
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add feedback_store.go with SQLite storage for detection feedback and accuracy metrics
- Add feedback_processor.go for background processing of user feedback
- Add accuracy.go for weekly precision/recall/F1 metric computation
- Add handler.go with REST API routes for feedback submission and accuracy retrieval
- Wire learning package into main_phase6.go with background processing
- Add dashboard/js/feedback.js with thumbs-up/down UI components
- Add dashboard/js/accuracy.js with accuracy panel rendering and sparkline trends
- Add comprehensive tests for feedback storage and accuracy computation
Feedback UI provides:
- Thumbs-up/down buttons for detection events
- Feedback form with false positive/negative options
- Missed detection reporting with position/zone selection
- Motivational counter showing improvement from user corrections
Accuracy panel shows:
- Circular gauge with F1 score
- Week-over-week trend sparkline
- Per-zone breakdown of precision/recall
- Total corrections count and improvement percentage
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add comprehensive tests for HealthStore (healthpersist_test.go)
- Add link health panel CSS styles and sparkline visualization
- Add ghost node rendering for repositioning advice in viz3d.js
- Wire up all diagnostic API routes in main.go
- Minor test cleanup in linkweather_test.go
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements per-link health metrics that gate and weight detection algorithms:
Per-Link Health Metrics (LinkHealthScorer in ambient.go):
- SNR Estimate (40% weight): motion/quiet deltaRMS ratio via log10 mapping
- Phase Stability (30% weight): phase variance with 0.5 rad threshold
- Packet Rate Health (20% weight): actual vs configured rate
- Baseline Drift (10% weight): hourly normalized L2 change
Gating Effects:
- BreathingDetector: disabled when health_score < 0.7
- FusionEngine: link contributions weighted by health_score
Dashboard Visualization:
- 3D link line color: green (1.0) → yellow (0.5) → red (0.0)
- 3D link line thickness: 2px (>0.7), 1px (0.4-0.7), 0.5px (<0.4)
- System-wide Detection Quality gauge in header
- Link Health panel with per-metric breakdown and sparklines
API: GET /api/links returns health_score and health_details for each link
Tests:
- Health score computation with weighted sub-metrics
- SNR mapping: SNR=100 → 1.0, SNR=10 → 0.5
- Phase stability: variance=0 → 1.0, variance=0.5 → 0.0
- Breathing health gating at 0.7 threshold
- Fusion engine link weight verification
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Interactive onboarding wizard:
- 8-step Web Serial-based provisioning flow
- Firmware flashing via esp-web-install-button (CDN)
- Live CSI waveform feedback during guided calibration
- Server-side provisioning with client-side fallback
- Serial JSON response handling with error mapping
- Post-calibration reinforcement card with link count
OTA firmware management:
- Firmware list with SHA-256 hashes and size display
- Per-node progress tracking (idle/pending/downloading/rebooting/verified/failed/rollback)
- Rolling update orchestration via REST API
- Status bar button with state indicators (normal/in-progress/has-update)
- Node list badges for OTA status and rollback warnings
Guided troubleshooting:
- First-time feature tooltips with 8s auto-dismiss
- Sequential tooltip tour triggered on first node connection
- Node offline cards with step-by-step recovery instructions
- Factory reset instructions modal
- Client-side link health check (60s no-frame threshold)
- Captive portal recovery documentation
Exit criteria: New ESP32-S3 from unboxed to streaming CSI in under 5 minutes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds TransformControls for dragging nodes in the 3D scene, a room dimension
editor (width/depth/height), a GDOP (Geometric Dilution of Precision) coverage
overlay on the ground plane that updates in real-time during node drag, virtual
node support for planning optimal placement before hardware purchase, and REST
API integration to persist positions and room config to the mothership.
Backend adds PUT /api/nodes/{mac}/position, POST /api/nodes/virtual,
DELETE /api/nodes/{mac}, PUT /api/room endpoints. Also wires OTA status
handler and improves firmware manifest to use actual latest binary filename.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add troubleshooting infrastructure for non-technical users:
- TroubleshootManager: node offline cards with stepped recovery
guidance, factory reset modal, and detection quality banners
- TooltipManager: first-time feature tooltips with localStorage
persistence, auto-dismiss, and sequential tour
- Onboarding failure guidance: human-readable error messages for
browser compatibility, USB connection, WiFi provisioning, and
node detection failures
- Post-calibration reinforcement card with summary and next steps
- Client-side link health check with auto-recovery
- CSS for offline cards, tooltips, quality banners, and modals
- Script tags wired in index.html for troubleshoot.js and tooltips.js
- 30 tests covering all troubleshooting and tooltip functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix WebSocket mock to use factory function so resetAllMocks doesn't
break it (state.ws.close is not a function errors)
- Fix TextEncoderStream mock to provide functional readable/writable
for pipeTo (needed by provisioning serial send tests)
- Fix flash_firmware test to check wizard-nav for "Skip Flashing"
button instead of wizard-content
- Fix provisionAndSend "no port" test to use mockResolvedValue
instead of mockResolvedValueOnce so both primary and fallback
paths fail consistently
All 60 tests now pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Web Serial-based wizard that takes a non-technical user from unboxed
ESP32-S3 to streaming CSI in under 5 minutes. 8-step state machine:
browser check → connect → flash firmware (esp-web-tools) → provision
WiFi → detect node → guided calibration → placement guidance → complete.
Includes sessionStorage persistence for resumability across page refreshes,
live CSI waveform during calibration, human-friendly error messages for
all failure modes, and comprehensive Jest test coverage (34 tests).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 500ms periodic presence_update broadcast from the dashboard hub with
per-link motion state (is_motion, delta_rms, confidence). Surface this in
a new Presence panel on the dashboard with coloured dot indicators
(green=clear, amber=motion, red=high-confidence) and deltaRMS values.
Includes a rolling 10s Canvas 2D line chart of deltaRMS with a threshold
line at 0.02 for the selected link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements plan item 17 — full Three.js Phase 3 visualization layer:
- Room bounds: floor, ceiling, semi-transparent walls and wireframe edges
built from registry_state room config (width/depth/height/origin)
- Floor plan texture: upload any image via status-bar button; mapped to
ground plane via THREE.TextureLoader
- Humanoid figures: SkinnedMesh + AnimationMixer with 13-bone skeleton;
four AnimationClip postures (standing, walking, seated, lying); smooth
0.35 s crossfade transitions; walking speed scales animation timeScale;
figures orient in direction of travel
- Vertical pillar anchors: line from floor to ceiling at each blob position
- Footprint trails: floor-level Line geometry following blob.trail history
- Node meshes: OctahedronGeometry at registry node 3-D positions; link
lines redrawn whenever link_active/inactive events arrive
- View presets: 3D perspective, top-down orthographic, first-person follow
(lerps camera to track first active blob)
- WebSocket: handles registry_state, loc_update, link_inactive message types
newly routed through handleJSONMessage; existing CSI/motion pipeline intact
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dashboard hub broadcasts motion state changes immediately on transition
(idle↔motion) via BroadcastMotionState; periodic state snapshots include
motion_states for new client init
- Per-link presence badge (green CLEAR / red MOTION) rendered in link list
alongside global presence indicator in status bar
- Amplitude mean time-series chart (60 s rolling window) for selected link,
line segments colored by motion state at each sample
- Fix: links created from JSON link_active/state events now initialize
ampHistory and lastAmpSample so time-series accumulates from first frame
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dashboard presence indicator: per-link MOTION/CLEAR badges with global
status bar indicator, motion_state WebSocket messages, amplitude chart
- CSI recording buffer: disk-backed circular buffer (replay/store.go) with
magic-tagged binary format, wrap/eviction, 360 MB default (~48 h at 20 Hz)
- Adaptive sensing rate: RateController ramps nodes to 20 Hz on motion,
drops to 2 Hz after 10 s idle; wires to SendConfigToMAC over WebSocket
- Fix: alias internal/signal as sigproc to avoid conflict with os/signal
- Fix: add GetAllMotionStates() to MockIngestionState in dashboard tests
All tests pass (signal, ingestion, replay, dashboard).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Static HTML/JS dashboard served by mothership at /
- Three.js 3D scene with ground grid, OrbitControls (pan/zoom/rotate)
- WebSocket connection at /ws/dashboard for real-time CSI streaming
- Binary CSI frame parsing (24-byte header + I/Q payload)
- Amplitude bar chart (64 subcarriers) as 2D Canvas overlay
- Node/link panels with live status and click-to-select
- Dashboard Go package with Hub for client broadcasting
- Ingestion server broadcasts CSI frames and node events to dashboard
- 5 new tests for dashboard hub operations
Complete: 3D scene, WebSocket, amplitude chart, node/link panels
Remaining: Docker packaging (Phase 1 final item)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>