Add X-ray overlay showing contributing links to detections with confidence breakdown.
- Users can click "Why?" on any blob to see detailed explanation
- Contributing links are highlighted with Fresnel zone visualization
- Per-link contribution breakdown shows deltaRMS, zone number, weight
- BLE identity match details displayed when available
- Confidence gauge shows overall detection certainty
Explainability is accessible via:
- Right-click context menu on blob figures
- "Why?" button in blob hover tooltip
- Click directly on humanoid blob figures
- Timeline event "Why?" buttons
Accepts: Users can see exactly why a detection was triggered with visual overlays and confidence metrics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 8 implementation: Activity Timeline (Component 27)
- Tap-to-jump navigation: Click any event to create replay session and seek to that moment
- Inline feedback display: Thumbs up/down buttons on each event for detection feedback
- Replay API integration: Creates replay window around event timestamp (±5 seconds)
- Feedback API: New /api/feedback endpoint for correct/incorrect/missed detection reports
- Event loading improvements: Real-time WebSocket event insertion with animation
- Filter UI: Type, zone, person, time range, and search filters
- Load more pagination: Keyset cursor-based pagination for large event sets
Acceptance criteria met:
- Users can view all system events chronologically
- Tap any event to jump to that moment in time via replay mode
- Inline feedback buttons allow marking detections correct/incorrect
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement time-travel debugging, CSI simulator, and pre-deployment
simulator for deep system analysis and tuning.
Time-Travel Debugging:
- Replay session management with seek/play/pause controls
- Parameter tuning overlay with instant preview
- CSI replay store with circular buffer for 48h retention
- REST API for replay control (/api/replay/*)
CSI Simulator CLI (cmd/sim/main.go):
- Virtual ESP32-S3 nodes with synthetic CSI generation
- Walker simulation with random walk or path-following
- Fresnel zone modulation based on walker positions
- BLE advertisement simulation for identity testing
- WebSocket integration with mothership
Pre-Deployment Simulator:
- Virtual space definition with 3D node placement
- Synthetic walker generation and movement simulation
- Fresnel zone accumulation for blob detection
- GDOP map computation for coverage quality visualization
- Deployment recommendations based on coverage analysis
- SSE endpoint for real-time simulation updates
Dashboard Integration:
- Fresnel zone ellipsoid rendering (already in viz3d.js)
- Activity timeline with tap-to-jump (already in timeline.js)
- Detection explainability with X-ray overlay (already in explainability.js)
Exit Criteria Met:
- Time-travel replays historical CSI data with seek/play/pause
- Simulator produces realistic synthetic CSI and blob detections
- Fresnel zone overlay renders wireframe ellipsoids between links
- Developers can tune parameters and see instant results
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement localization that learns from ground truth data.
- BLE integration as ground truth source: BLE RSSI trilateration provides
continuous position estimates for registered devices
- Fresnel zone weight refinement: SGD-based per-link weight learning
- Continuous weight adjustment based on BLE-blob position feedback
- SelfImprovingLocalizer ties together BLE ground truth, weight learning,
and fusion engine
- REST API endpoints for weights, ground truth, accuracy tracking
Acceptance: Localization accuracy improves automatically as BLE ground
truth data accumulates.
Document the complete presence prediction system implementation
for Home Assistant integration with:
- Per-person transition probability tracking with Laplace smoothing
- Per-zone occupancy patterns from historical data
- Time-slot based predictions via Monte Carlo simulation
- HA sensor exposure with auto-discovery (3 sensors per person)
- Accuracy tracking targeting >75% at 15-minute horizon
All acceptance criteria for presence prediction have been met.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement REST API endpoints for managing learned weights and tracking
improvement in the self-improving localization system.
- Add LocalizationHandler with endpoints for:
- GET /api/localization/weights - get all learned link weights
- GET /api/localization/weights/{linkID} - get specific link weight
- POST /api/localization/weights/reset - reset all weights to default
- GET /api/localization/spatial-weights - get spatial weights per zone
- GET /api/localization/groundtruth/* - ground truth sample management
- GET /api/localization/accuracy/* - position accuracy tracking
- GET /api/localization/learning/* - learning progress and history
- Integrate spatial weight learner into fusion engine:
- Add AddLinkInfluenceWithSpatialWeights to grid.go for per-cell weight application
- Update Fuse() in fusion.go to use spatial weight functions when available
- Apply both sigma adjustments and spatial weights for Fresnel zone computation
- Add comprehensive table-driven tests for all API endpoints
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implemented prediction API handler with comprehensive REST endpoints:
- GET /api/predictions - Get all predictions (optional filter by person/horizon)
- GET /api/predictions/stats - Get prediction statistics and data age
- POST /api/predictions/recompute - Force probability recomputation
- GET /api/predictions/accuracy - Get accuracy stats for all people
- GET /api/predictions/accuracy/overall - Get overall system accuracy
- GET /api/predictions/accuracy/{personID} - Get person-specific accuracy
- GET /api/predictions/pending - Get pending prediction count
- GET /api/predictions/patterns/zones - Get zone occupancy patterns
- GET /api/predictions/patterns/zones/{zoneID} - Get pattern for specific zone
- POST /api/predictions/patterns/compute - Compute zone occupancy patterns
- GET /api/predictions/horizon - Get Monte Carlo horizon predictions
- GET /api/predictions/horizon/{personID} - Get horizon prediction for person
The implementation includes:
- Proper error handling with appropriate HTTP status codes
- Query parameter support for filtering (person, horizon)
- JSON responses for all endpoints
- Helper function for logging prediction accuracy
- Table-driven tests for all endpoints
HA sensor exposure for predictions was already implemented in the MQTT
client via PublishPredictionSensors() and UpdatePredictionState() methods.
Accepts the 75% accuracy target at 15-minute horizon per specification.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements comprehensive anomaly detection system that learns normal household
patterns over 7+ days and alerts on deviations. Transforms spaxel into a basic
home security system.
Core features:
- Normal behaviour model: statistical tracking of occupancy patterns per
(hour_of_week, zone_id) slot with expected occupancy, typical person count,
and typical BLE devices
- Four anomaly types: unusual hour presence, unknown BLE device, motion during
away mode, unusual dwell duration
- Security mode: lowered thresholds, immediate alerts, bypasses quiet hours
- Auto-away/disarm: automatic security mode activation based on BLE device
presence (15min absence, auto-disarm on device return)
- Alert chain: staged notifications (dashboard → push → webhook → escalation)
- WebSocket integration: real-time anomaly broadcasts to dashboard
API endpoints:
- GET/POST /api/mode: system mode control (home/away/sleep)
- GET /api/security/status: current security state
Tests cover all anomaly types, alert chain timing, security mode thresholds,
auto-away/disarm, acknowledgement flow, and cooldown deduplication.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verified the diurnal adaptive baseline system is fully implemented:
- 24 hourly slots per link per subcarrier
- 7-day learning phase with >=300 samples/slot requirement
- Motion-gated updates with outlier protection
- 15-minute crossfade at hour boundaries
- SQLite persistence with diurnal_baselines table
- 24-hour polar chart dashboard visualization
- REST API endpoints for diurnal data
- Comprehensive test coverage (45+ tests)
All acceptance criteria met:
- Baseline correctly crossfades at hour boundaries (±60s)
- Motion events during learning do not corrupt slots
- Polar chart renders for links with >=1 ready slot
- No performance regression: baseline lookup remains O(1)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add REST API for diurnal baseline data:
- GET /api/diurnal/status - learning status for all links
- GET /api/diurnal/slots/{linkID} - slot data for specific link
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
Update .beads/issues.jsonl and .needle-predispatch-sha after
remote rebase to maintain tracking state.
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>
All components complete:
- Firmware: LED blink at 5Hz via FreeRTOS task, configurable GPIO
- Mothership: POST /api/nodes/{mac}/identify endpoint, WebSocket sending
- Dashboard: Identify button in fleet panel and 3D view context menu
Acceptance criteria met:
✓ LED blinks at ~5 Hz for specified duration
✓ Blink stops automatically when duration_ms expires
✓ REST endpoint returns 404 for disconnected nodes
✓ LED GPIO configurable via sdkconfig (default GPIO8)
✓ Dashboard integration complete
- Fleet status page now has 'Identify' button per row that POSTs to /api/nodes/{mac}/identify
- 3D view right-click context menu has 'Identify (blink LED)' option for nodes
- Both options trigger LED blink on the selected node for identification
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>
Added tests for:
- Auto-away activation when all registered BLE devices absent for 15+ minutes
- Auto-disarm triggering when registered BLE device returns with strong RSSI
- Manual override pausing auto-away detection
- System mode GET/PUT REST API endpoints
- Edge cases: no registered devices, weak BLE signals, unregistered devices
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>
- POST /api/nodes/{mac}/identify endpoint with {duration_ms: 5000} body
- Forwards identify message as WebSocket JSON to target node
- Returns 404 if node not connected; 200 on success
- Includes table-driven tests for all edge cases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add led_init() call during firmware initialization in app_main()
- Add led_stop_blink() call on WebSocket disconnect
- LED GPIO configurable via CONFIG_SPAXEL_LED_GPIO (default GPIO8)
- Blink runs in FreeRTOS task at ~5Hz (100ms on/100ms off)
- Any running blink is cancelled when new identify message received
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>
Create mothership/internal/oui/oui.go with LookupOUI function:
- Accepts net.HardwareAddr (MAC address)
- Extracts first 3 bytes as uint32 in big-endian order (OUI)
- Returns manufacturer name from ouiMap or empty string if not found
- Handles edge cases gracefully (short MAC returns "", no panic)
Acceptance criteria met:
- LookupOUI returns correct vendor for known OUIs
- Unknown OUI returns empty string (no panic)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>