Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

16 KiB

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.

GET /api/beacons

Response:

{
  "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.

POST /api/beacons
Content-Type: application/json

Request Body:

{
  "name": "Meeting Room Beacon",
  "beacon_id": "beacon_002",
  "beacon_type": "eddystone",
  "beacon_location": "meeting_room",
  "hs_button_counter": 0,
  "hs_button_battery": 100
}

Response:

{
  "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.

PUT /api/beacons/{id}
Content-Type: application/json

Path Parameters:

  • id (string): The beacon ID to update

Request Body:

{
  "name": "Updated Conference Room Beacon",
  "beacon_location": "main_conference",
  "location_confidence": 90
}

Response:

{
  "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.

DELETE /api/beacons/{id}

Path Parameters:

  • id (string): The beacon ID to delete

Response:

{
  "message": "Beacon deleted successfully"
}

Settings Management

Get System Settings

Retrieves current system configuration settings.

GET /api/settings

Response:

{
  "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.

POST /api/settings
Content-Type: application/json

Request Body:

{
  "location_confidence": 85,
  "last_seen_threshold": 600,
  "beacon_metrics_size": 15,
  "ha_send_interval": 30,
  "rssi_min_threshold": -85
}

Response:

{
  "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.

GET /api/locations

Response:

{
  "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.

GET /api/locations/{id}

Path Parameters:

  • id (string): The beacon ID

Response:

{
  "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.

GET /api/health

Response:

{
  "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

{
  "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

{
  "type": "button_event",
  "data": {
    "beacon_id": "beacon_001",
    "button_counter": 43,
    "button_mode": "normal",
    "timestamp": 1703078460
  }
}

Battery Alert

{
  "type": "battery_alert",
  "data": {
    "beacon_id": "beacon_002",
    "battery_level": 15,
    "alert_level": "warning",
    "timestamp": 1703078470
  }
}

Fall Detection Event

{
  "type": "fall_detection",
  "data": {
    "beacon_id": "beacon_001",
    "event_type": "fall_detected",
    "confidence": 92,
    "timestamp": 1703078480
  }
}

System Status Update

{
  "type": "system_status",
  "data": {
    "active_beacons": 12,
    "total_locations": 8,
    "kafka_status": "connected",
    "redis_status": "connected",
    "timestamp": 1703078490
  }
}

Data Models

Beacon Model

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

interface BeaconMetric {
  location: string;
  distance: number;
  rssi: number;
  timestamp: number;
}

Settings Model

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

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

{
  "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

POST /api/beacons
Content-Type: application/json

Invalid Request:

{
  "name": "",
  "beacon_type": "invalid_type"
}

Response:

{
  "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

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

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)

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.