|
|
|
@@ -0,0 +1,748 @@ |
|
|
|
# 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<Beacon[]> { |
|
|
|
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<Beacon>): Promise<Beacon> { |
|
|
|
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. |