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>
- 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>
The load shedding system was already fully implemented with 4 levels,
rolling average timing, and all integration points wired. This change
extracts the state machine evaluation into a separate evaluate() method
so tests can inject controlled timing values without depending on wall
clock time, and fixes the recovery counter reset test accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire anomaly detection backend into dashboard WebSocket feed as
typed 'anomaly_detected' and 'alert' messages. Add security mode
state to snapshot/delta broadcasts via SecurityStateProvider.
Include load shedding integration for crowd flow, detection event
logging, identity matching improvements, and sleep integration
updates. All acceptance criteria met: arm/disarm persists,
learning progress refreshes, alert banner <2s, acknowledge flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The shell script was using GET /api/auth/setup to check auth status,
but that endpoint only accepts POST requests. The correct endpoint for
checking auth status is /api/auth/status which returns the
pin_configured field.
This fix ensures the E2E test properly detects when PIN authentication
is enabled and configures it accordingly.
AnomalyDetector initialized in main() with periodic model updates.
Anomaly events broadcast to dashboard WS as 'alert' messages via
BroadcastAlert. GET /api/anomalies?since=24h lists recent events.
POST /api/security/arm and /api/security/disarm manage security mode.
GET /api/security/status returns armed state, learning progress, and
24h anomaly count. Arm/disarm state persisted to learning_state table
and restored on restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>