# API Documentation ## Overview The AFA Systems Presence Detection API provides RESTful endpoints for managing beacons, settings, and real-time WebSocket communication for live updates. ## Base URL ``` http://localhost:8080 ``` ## Authentication Currently, the API does not implement authentication. This should be added for production deployments. ## REST API Endpoints ### Beacon Management #### Get All Beacons Retrieves a list of all registered beacons with their current status and location information. ```http GET /api/beacons ``` **Response:** ```json { "beacons": [ { "name": "Conference Room Beacon", "beacon_id": "beacon_001", "beacon_type": "ingics", "beacon_location": "conference_room", "last_seen": 1703078400, "distance": 2.5, "location_confidence": 85, "hs_button_counter": 42, "hs_button_battery": 85, "hs_button_random": "abc123", "hs_button_mode": "normal" } ] } ``` #### Create Beacon Registers a new beacon in the system. ```http POST /api/beacons Content-Type: application/json ``` **Request Body:** ```json { "name": "Meeting Room Beacon", "beacon_id": "beacon_002", "beacon_type": "eddystone", "beacon_location": "meeting_room", "hs_button_counter": 0, "hs_button_battery": 100 } ``` **Response:** ```json { "message": "Beacon created successfully", "beacon": { "name": "Meeting Room Beacon", "beacon_id": "beacon_002", "beacon_type": "eddystone", "beacon_location": "meeting_room", "last_seen": 0, "distance": 0, "location_confidence": 0, "hs_button_counter": 0, "hs_button_battery": 100 } } ``` #### Update Beacon Updates an existing beacon's information. ```http PUT /api/beacons/{id} Content-Type: application/json ``` **Path Parameters:** - `id` (string): The beacon ID to update **Request Body:** ```json { "name": "Updated Conference Room Beacon", "beacon_location": "main_conference", "location_confidence": 90 } ``` **Response:** ```json { "message": "Beacon updated successfully", "beacon": { "name": "Updated Conference Room Beacon", "beacon_id": "beacon_001", "beacon_type": "ingics", "beacon_location": "main_conference", "last_seen": 1703078400, "distance": 2.5, "location_confidence": 90, "hs_button_counter": 42, "hs_button_battery": 85 } } ``` #### Delete Beacon Removes a beacon from the system. ```http DELETE /api/beacons/{id} ``` **Path Parameters:** - `id` (string): The beacon ID to delete **Response:** ```json { "message": "Beacon deleted successfully" } ``` ### Settings Management #### Get System Settings Retrieves current system configuration settings. ```http GET /api/settings ``` **Response:** ```json { "settings": { "location_confidence": 80, "last_seen_threshold": 300, "beacon_metrics_size": 10, "ha_send_interval": 60, "ha_send_changes_only": true, "rssi_min_threshold": -90, "enforce_rssi_threshold": true } } ``` #### Update System Settings Updates system configuration settings. ```http POST /api/settings Content-Type: application/json ``` **Request Body:** ```json { "location_confidence": 85, "last_seen_threshold": 600, "beacon_metrics_size": 15, "ha_send_interval": 30, "rssi_min_threshold": -85 } ``` **Response:** ```json { "message": "Settings updated successfully", "settings": { "location_confidence": 85, "last_seen_threshold": 600, "beacon_metrics_size": 15, "ha_send_interval": 30, "ha_send_changes_only": true, "rssi_min_threshold": -85, "enforce_rssi_threshold": true } } ``` ### Location Information #### Get Beacon Locations Retrieves current location information for all beacons. ```http GET /api/locations ``` **Response:** ```json { "beacons": [ { "method": "location_update", "previous_confident_location": "reception", "distance": 3.2, "id": "beacon_001", "location": "conference_room", "last_seen": 1703078450 }, { "method": "location_update", "previous_confident_location": "office_a", "distance": 1.8, "id": "beacon_002", "location": "meeting_room", "last_seen": 1703078440 } ] } ``` #### Get Specific Beacon Location Retrieves location information for a specific beacon. ```http GET /api/locations/{id} ``` **Path Parameters:** - `id` (string): The beacon ID **Response:** ```json { "method": "location_update", "previous_confident_location": "reception", "distance": 3.2, "id": "beacon_001", "location": "conference_room", "last_seen": 1703078450 } ``` ### Health Check #### System Health Check if the API server is running and basic systems are operational. ```http GET /api/health ``` **Response:** ```json { "status": "healthy", "timestamp": "2024-12-20T10:30:00Z", "services": { "database": "connected", "kafka": "connected", "redis": "connected" } } ``` ## WebSocket API ### WebSocket Connection Connect to the WebSocket endpoint for real-time updates. ``` ws://localhost:8080/ws/broadcast ``` ### WebSocket Message Format #### Beacon Update Notification ```json { "type": "beacon_update", "data": { "method": "location_update", "beacon_info": { "name": "Conference Room Beacon", "beacon_id": "beacon_001", "beacon_type": "ingics", "distance": 2.5 }, "name": "conference_room", "beacon_name": "Conference Room Beacon", "previous_location": "reception", "new_location": "conference_room", "timestamp": 1703078450 } } ``` #### Button Press Event ```json { "type": "button_event", "data": { "beacon_id": "beacon_001", "button_counter": 43, "button_mode": "normal", "timestamp": 1703078460 } } ``` #### Battery Alert ```json { "type": "battery_alert", "data": { "beacon_id": "beacon_002", "battery_level": 15, "alert_level": "warning", "timestamp": 1703078470 } } ``` #### Fall Detection Event ```json { "type": "fall_detection", "data": { "beacon_id": "beacon_001", "event_type": "fall_detected", "confidence": 92, "timestamp": 1703078480 } } ``` #### System Status Update ```json { "type": "system_status", "data": { "active_beacons": 12, "total_locations": 8, "kafka_status": "connected", "redis_status": "connected", "timestamp": 1703078490 } } ``` ## Data Models ### Beacon Model ```typescript interface Beacon { name: string; beacon_id: string; beacon_type: "ingics" | "eddystone" | "minew_b7" | "ibeacon"; beacon_location: string; last_seen: number; // Unix timestamp distance: number; // Distance in meters previous_location?: string; previous_confident_location?: string; expired_location?: string; location_confidence: number; // 0-100 location_history: string[]; beacon_metrics: BeaconMetric[]; // Handshake/Button specific fields hs_button_counter: number; hs_button_prev: number; hs_button_battery: number; hs_button_random: string; hs_button_mode: string; } ``` ### BeaconMetric Model ```typescript interface BeaconMetric { location: string; distance: number; rssi: number; timestamp: number; } ``` ### Settings Model ```typescript interface Settings { location_confidence: number; // Minimum confidence level (0-100) last_seen_threshold: number; // Seconds before beacon considered offline beacon_metrics_size: number; // Number of RSSI measurements to keep ha_send_interval: number; // Home Assistant update interval (seconds) ha_send_changes_only: boolean; // Only send updates on changes rssi_min_threshold: number; // Minimum RSSI for detection enforce_rssi_threshold: boolean; // Filter weak signals } ``` ### LocationChange Model ```typescript interface LocationChange { method: string; // "location_update" | "beacon_added" | "beacon_removed" beacon_ref: Beacon; // Complete beacon information name: string; // Beacon name beacon_name: string; // Beacon name (duplicate) previous_location: string; // Previous location new_location: string; // New location timestamp: number; // Unix timestamp } ``` ## Error Responses ### Standard Error Format ```json { "error": { "code": "VALIDATION_ERROR", "message": "Invalid request data", "details": { "field": "beacon_id", "reason": "Beacon ID is required" } } } ``` ### Common Error Codes | Code | HTTP Status | Description | |------|-------------|-------------| | `VALIDATION_ERROR` | 400 | Request data validation failed | | `NOT_FOUND` | 404 | Resource not found | | `CONFLICT` | 409 | Resource already exists | | `INTERNAL_ERROR` | 500 | Internal server error | | `SERVICE_UNAVAILABLE` | 503 | Required service is unavailable | ### Validation Error Example ```http POST /api/beacons Content-Type: application/json ``` **Invalid Request:** ```json { "name": "", "beacon_type": "invalid_type" } ``` **Response:** ```json { "error": { "code": "VALIDATION_ERROR", "message": "Invalid request data", "details": { "name": "Name cannot be empty", "beacon_type": "Invalid beacon type. Must be one of: ingics, eddystone, minew_b7, ibeacon" } } } ``` ## Rate Limiting Currently, the API does not implement rate limiting. Consider implementing rate limiting for production deployments: - Suggested limits: 100 requests per minute per IP address - WebSocket connections: Maximum 50 concurrent connections - Consider authentication-based rate limiting ## CORS Configuration The API server is configured with CORS enabled for development. Production deployments should restrict CORS origins to specific domains. ## Integration Examples ### JavaScript/TypeScript Client ```typescript class PresenceAPIClient { private baseURL: string; constructor(baseURL: string = 'http://localhost:8080') { this.baseURL = baseURL; } async getBeacons(): Promise { const response = await fetch(`${this.baseURL}/api/beacons`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data.beacons; } async createBeacon(beacon: Partial): Promise { const response = await fetch(`${this.baseURL}/api/beacons`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(beacon), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error?.message || 'Failed to create beacon'); } const data = await response.json(); return data.beacon; } connectWebSocket(onMessage: (message: any) => void): WebSocket { const ws = new WebSocket(`${this.baseURL.replace('http', 'ws')}/ws/broadcast`); ws.onmessage = (event) => { try { const message = JSON.parse(event.data); onMessage(message); } catch (error) { console.error('Failed to parse WebSocket message:', error); } }; ws.onerror = (error) => { console.error('WebSocket error:', error); }; ws.onclose = () => { console.log('WebSocket connection closed'); }; return ws; } } // Usage example const client = new PresenceAPIClient(); // Get all beacons const beacons = await client.getBeacons(); console.log('Active beacons:', beacons); // Create a new beacon const newBeacon = await client.createBeacon({ name: 'Test Beacon', beacon_id: 'test_beacon_001', beacon_type: 'eddystone', beacon_location: 'test_room' }); // Connect to WebSocket for real-time updates const ws = client.connectWebSocket((message) => { switch (message.type) { case 'beacon_update': console.log('Beacon location updated:', message.data); break; case 'button_event': console.log('Button pressed:', message.data); break; case 'battery_alert': console.log('Low battery warning:', message.data); break; } }); ``` ### Python Client ```python import requests import websocket import json from typing import List, Dict, Any class PresenceAPIClient: def __init__(self, base_url: str = "http://localhost:8080"): self.base_url = base_url self.ws_url = base_url.replace("http", "ws") def get_beacons(self) -> List[Dict[str, Any]]: """Get all registered beacons.""" response = requests.get(f"{self.base_url}/api/beacons") response.raise_for_status() data = response.json() return data["beacons"] def create_beacon(self, beacon_data: Dict[str, Any]) -> Dict[str, Any]: """Create a new beacon.""" response = requests.post( f"{self.base_url}/api/beacons", json=beacon_data ) response.raise_for_status() data = response.json() return data["beacon"] def update_beacon(self, beacon_id: str, beacon_data: Dict[str, Any]) -> Dict[str, Any]: """Update an existing beacon.""" response = requests.put( f"{self.base_url}/api/beacons/{beacon_id}", json=beacon_data ) response.raise_for_status() data = response.json() return data["beacon"] def delete_beacon(self, beacon_id: str) -> None: """Delete a beacon.""" response = requests.delete(f"{self.base_url}/api/beacons/{beacon_id}") response.raise_for_status() def get_settings(self) -> Dict[str, Any]: """Get system settings.""" response = requests.get(f"{self.base_url}/api/settings") response.raise_for_status() return response.json()["settings"] def update_settings(self, settings_data: Dict[str, Any]) -> Dict[str, Any]: """Update system settings.""" response = requests.post( f"{self.base_url}/api/settings", json=settings_data ) response.raise_for_status() return response.json()["settings"] # Usage example client = PresenceAPIClient() # Get all beacons beacons = client.get_beacons() print(f"Found {len(beacons)} beacons") # Create a new beacon new_beacon = client.create_beacon({ "name": "Python Test Beacon", "beacon_id": "python_test_001", "beacon_type": "eddystone", "beacon_location": "python_room" }) print(f"Created beacon: {new_beacon['name']}") # Update settings settings = client.update_settings({ "location_confidence": 85, "ha_send_interval": 30 }) print(f"Updated settings: {settings}") ``` ## Testing ### Unit Testing Example (Go) ```go package api_test import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) func TestGetBeacons(t *testing.T) { // Setup test server router := setupTestRouter() req, _ := http.NewRequest("GET", "/api/beacons", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) assert.Contains(t, response, "beacons") } func TestCreateBeacon(t *testing.T) { router := setupTestRouter() beaconData := map[string]interface{}{ "name": "Test Beacon", "beacon_id": "test_001", "beacon_type": "eddystone", "beacon_location": "test_room", } jsonData, _ := json.Marshal(beaconData) req, _ := http.NewRequest("POST", "/api/beacons", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusCreated, w.Code) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) assert.Contains(t, response, "beacon") } ``` ## Security Considerations For production deployments, consider implementing: 1. **Authentication**: JWT tokens or API key authentication 2. **Authorization**: Role-based access control (RBAC) 3. **Rate Limiting**: Prevent API abuse 4. **Input Validation**: Comprehensive input sanitization 5. **HTTPS**: TLS encryption for all API communications 6. **CORS**: Restrict origins to trusted domains 7. **Logging**: Comprehensive audit logging 8. **Security Headers**: Implement security HTTP headers ## API Versioning The current API is version 1. Future versions will be: - Version 1: `/api/v1/...` (current, implied) - Version 2: `/api/v2/...` (future breaking changes) Backward compatibility will be maintained within major versions.