91 lines
2.7 KiB
Python
91 lines
2.7 KiB
Python
"""
|
|
Demeter Server — REST API Endpoints
|
|
|
|
Provides JSON endpoints for querying device metadata and sensor
|
|
readings from the in-memory store.
|
|
|
|
Routes:
|
|
GET /api/devices — list all devices + latest readings
|
|
GET /api/devices/{device_id} — single device detail
|
|
GET /api/devices/{device_id}/readings — sensor readings only
|
|
GET /api/status — server status summary
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
|
|
router = APIRouter(prefix="/api", tags=["devices"])
|
|
|
|
|
|
def _get_store(request: Request):
|
|
"""Retrieve the DeviceStore from app state."""
|
|
return request.app.state.store
|
|
|
|
|
|
def _get_observer(request: Request):
|
|
"""Retrieve the CoapObserverClient from app state."""
|
|
return request.app.state.observer
|
|
|
|
|
|
@router.get("/devices")
|
|
async def list_devices(request: Request) -> dict[str, Any]:
|
|
"""List all registered devices with their latest readings."""
|
|
store = _get_store(request)
|
|
snapshot = await store.snapshot()
|
|
return {
|
|
"devices": list(snapshot.values()),
|
|
"count": len(snapshot),
|
|
"timestamp": time.time(),
|
|
}
|
|
|
|
|
|
@router.get("/devices/{device_id}")
|
|
async def get_device(device_id: str, request: Request) -> dict[str, Any]:
|
|
"""Get a single device's full state."""
|
|
store = _get_store(request)
|
|
device = await store.get_device(device_id)
|
|
if device is None:
|
|
raise HTTPException(status_code=404, detail=f"Device {device_id!r} not found")
|
|
return device.to_dict()
|
|
|
|
|
|
@router.get("/devices/{device_id}/readings")
|
|
async def get_readings(device_id: str, request: Request) -> dict[str, Any]:
|
|
"""Get sensor readings for a specific device."""
|
|
store = _get_store(request)
|
|
device = await store.get_device(device_id)
|
|
if device is None:
|
|
raise HTTPException(status_code=404, detail=f"Device {device_id!r} not found")
|
|
return {
|
|
"device_id": device_id,
|
|
"online": device.online,
|
|
"readings": {
|
|
uri: reading.to_dict()
|
|
for uri, reading in device.readings.items()
|
|
},
|
|
}
|
|
|
|
|
|
@router.get("/status")
|
|
async def server_status(request: Request) -> dict[str, Any]:
|
|
"""Server status: subscription count, device overview."""
|
|
store = _get_store(request)
|
|
observer = _get_observer(request)
|
|
|
|
devices = await store.get_all_devices()
|
|
online = sum(1 for d in devices if d.online)
|
|
|
|
return {
|
|
"server": "demeter",
|
|
"version": request.app.state.settings.app_version,
|
|
"devices_total": len(devices),
|
|
"devices_online": online,
|
|
"active_subscriptions": observer.active_subscriptions,
|
|
"subscriptions": observer.subscription_status(),
|
|
"timestamp": time.time(),
|
|
}
|