feat: OpenAPI 3.0 spec with Swagger UI
- GET /openapi.yaml: raw OpenAPI spec - GET /openapi.json: JSON-formatted spec - GET /docs: Swagger UI for interactive API docs - 19 endpoints documented with schemas - Added pyyaml dependency
This commit is contained in:
2
TASKS.md
2
TASKS.md
@@ -16,7 +16,7 @@
|
||||
|
||||
### P3 - Low
|
||||
- [ ] Add pagination to all list endpoints
|
||||
- [ ] Add OpenAPI/Swagger spec
|
||||
- [x] Add OpenAPI/Swagger spec
|
||||
- [ ] Add request logging middleware
|
||||
|
||||
## Completed: v0.1.2 - OSINT Features
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencies = [
|
||||
"flask-cors>=4.0",
|
||||
"gunicorn>=21.0",
|
||||
"python-dotenv>=1.0",
|
||||
"pyyaml>=6.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""ESP32-Web Flask Application."""
|
||||
import click
|
||||
from datetime import datetime, UTC
|
||||
from flask import Flask
|
||||
from flask import Flask, Response, send_from_directory
|
||||
from pathlib import Path
|
||||
|
||||
from .config import Config
|
||||
@@ -42,6 +42,45 @@ def create_app(config_class=Config):
|
||||
uptime_str = f'{minutes}m{seconds}s'
|
||||
return {'status': 'ok', 'uptime': uptime_str, 'uptime_seconds': uptime_seconds}
|
||||
|
||||
# OpenAPI spec endpoints
|
||||
@app.route('/openapi.yaml')
|
||||
def openapi_yaml():
|
||||
"""Serve OpenAPI spec as YAML."""
|
||||
spec_path = Path(__file__).parent / 'openapi.yaml'
|
||||
return Response(spec_path.read_text(), mimetype='text/yaml')
|
||||
|
||||
@app.route('/openapi.json')
|
||||
def openapi_json():
|
||||
"""Serve OpenAPI spec as JSON."""
|
||||
import json
|
||||
import yaml
|
||||
spec_path = Path(__file__).parent / 'openapi.yaml'
|
||||
spec = yaml.safe_load(spec_path.read_text())
|
||||
return spec
|
||||
|
||||
@app.route('/docs')
|
||||
def swagger_ui():
|
||||
"""Serve Swagger UI."""
|
||||
return '''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ESP32-Web API</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
||||
<script>
|
||||
SwaggerUIBundle({
|
||||
url: "/openapi.json",
|
||||
dom_id: '#swagger-ui',
|
||||
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
|
||||
layout: "BaseLayout"
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>'''
|
||||
|
||||
# Start UDP collector in non-testing mode
|
||||
if not app.config.get('TESTING'):
|
||||
from .collector import collector
|
||||
|
||||
836
src/esp32_web/openapi.yaml
Normal file
836
src/esp32_web/openapi.yaml
Normal file
@@ -0,0 +1,836 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: ESP32-Web API
|
||||
description: REST API for ESP32 sensor fleet management
|
||||
version: 0.1.3
|
||||
contact:
|
||||
name: ESP32-Web
|
||||
servers:
|
||||
- url: /api/v1
|
||||
description: API v1
|
||||
|
||||
tags:
|
||||
- name: sensors
|
||||
description: Sensor management and fleet operations
|
||||
- name: devices
|
||||
description: Discovered BLE/WiFi devices
|
||||
- name: alerts
|
||||
description: Security alerts and anomalies
|
||||
- name: events
|
||||
description: Sensor events (motion, presence)
|
||||
- name: probes
|
||||
description: WiFi probe requests
|
||||
- name: stats
|
||||
description: Aggregate statistics
|
||||
- name: export
|
||||
description: Data export endpoints
|
||||
|
||||
paths:
|
||||
/sensors:
|
||||
get:
|
||||
tags: [sensors]
|
||||
summary: List all sensors
|
||||
operationId: listSensors
|
||||
responses:
|
||||
'200':
|
||||
description: List of sensors
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
sensors:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Sensor'
|
||||
|
||||
/sensors/heartbeat:
|
||||
get:
|
||||
tags: [sensors]
|
||||
summary: Get heartbeat status for all sensors
|
||||
operationId: getHeartbeatStatus
|
||||
responses:
|
||||
'200':
|
||||
description: Heartbeat summary
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HeartbeatSummary'
|
||||
post:
|
||||
tags: [sensors]
|
||||
summary: Refresh heartbeat status for all sensors
|
||||
operationId: refreshHeartbeats
|
||||
responses:
|
||||
'200':
|
||||
description: Update result
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: updated
|
||||
online:
|
||||
type: integer
|
||||
stale:
|
||||
type: integer
|
||||
offline:
|
||||
type: integer
|
||||
|
||||
/sensors/{hostname}:
|
||||
get:
|
||||
tags: [sensors]
|
||||
summary: Get sensor by hostname
|
||||
operationId: getSensor
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hostname'
|
||||
responses:
|
||||
'200':
|
||||
description: Sensor details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Sensor'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/sensors/{hostname}/command:
|
||||
post:
|
||||
tags: [sensors]
|
||||
summary: Send UDP command to sensor
|
||||
operationId: sendCommand
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hostname'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [command]
|
||||
properties:
|
||||
command:
|
||||
type: string
|
||||
example: STATUS
|
||||
responses:
|
||||
'200':
|
||||
description: Command sent
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: sent
|
||||
command:
|
||||
type: string
|
||||
'403':
|
||||
description: Command not allowed
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/sensors/{hostname}/config:
|
||||
get:
|
||||
tags: [sensors]
|
||||
summary: Get sensor configuration
|
||||
operationId: getSensorConfig
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hostname'
|
||||
responses:
|
||||
'200':
|
||||
description: Sensor configuration
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
$ref: '#/components/schemas/SensorConfig'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'504':
|
||||
description: Sensor not responding
|
||||
put:
|
||||
tags: [sensors]
|
||||
summary: Update sensor configuration
|
||||
operationId: updateSensorConfig
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hostname'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
rate:
|
||||
type: integer
|
||||
description: CSI send rate (Hz)
|
||||
power:
|
||||
type: integer
|
||||
description: TX power (dBm)
|
||||
adaptive:
|
||||
type: boolean
|
||||
threshold:
|
||||
type: number
|
||||
ble:
|
||||
type: boolean
|
||||
csi_mode:
|
||||
type: string
|
||||
enum: [raw, amplitude, hybrid]
|
||||
presence:
|
||||
type: boolean
|
||||
powersave:
|
||||
type: boolean
|
||||
chanscan:
|
||||
type: boolean
|
||||
responses:
|
||||
'200':
|
||||
description: Update results
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
results:
|
||||
type: object
|
||||
errors:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/sensors/{hostname}/ota:
|
||||
post:
|
||||
tags: [sensors]
|
||||
summary: Trigger OTA update
|
||||
operationId: triggerOta
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hostname'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [url]
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
example: https://example.com/firmware.bin
|
||||
responses:
|
||||
'200':
|
||||
description: OTA triggered
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: ota_triggered
|
||||
url:
|
||||
type: string
|
||||
'400':
|
||||
description: Invalid URL
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/sensors/{hostname}/calibrate:
|
||||
post:
|
||||
tags: [sensors]
|
||||
summary: Trigger baseline calibration
|
||||
operationId: triggerCalibrate
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hostname'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
seconds:
|
||||
type: integer
|
||||
minimum: 3
|
||||
maximum: 60
|
||||
default: 10
|
||||
responses:
|
||||
'200':
|
||||
description: Calibration started
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: calibration_started
|
||||
seconds:
|
||||
type: integer
|
||||
'400':
|
||||
description: Invalid seconds value
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/sensors/{hostname}/metrics:
|
||||
get:
|
||||
tags: [sensors]
|
||||
summary: Get sensor activity metrics
|
||||
operationId: getSensorMetrics
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hostname'
|
||||
- name: hours
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 24
|
||||
minimum: 1
|
||||
maximum: 168
|
||||
description: Time range in hours (max 168)
|
||||
responses:
|
||||
'200':
|
||||
description: Sensor metrics
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SensorMetrics'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/devices:
|
||||
get:
|
||||
tags: [devices]
|
||||
summary: List discovered devices
|
||||
operationId: listDevices
|
||||
parameters:
|
||||
- name: type
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [ble, wifi]
|
||||
- $ref: '#/components/parameters/limit'
|
||||
- $ref: '#/components/parameters/offset'
|
||||
responses:
|
||||
'200':
|
||||
description: List of devices
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
devices:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Device'
|
||||
limit:
|
||||
type: integer
|
||||
offset:
|
||||
type: integer
|
||||
|
||||
/devices/{mac}:
|
||||
get:
|
||||
tags: [devices]
|
||||
summary: Get device by MAC address
|
||||
operationId: getDevice
|
||||
parameters:
|
||||
- name: mac
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: aa:bb:cc:dd:ee:ff
|
||||
responses:
|
||||
'200':
|
||||
description: Device details with sightings
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Device'
|
||||
- type: object
|
||||
properties:
|
||||
sightings:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Sighting'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/alerts:
|
||||
get:
|
||||
tags: [alerts]
|
||||
summary: List security alerts
|
||||
operationId: listAlerts
|
||||
parameters:
|
||||
- name: type
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: sensor_id
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
- $ref: '#/components/parameters/hours'
|
||||
- $ref: '#/components/parameters/limit'
|
||||
- $ref: '#/components/parameters/offset'
|
||||
responses:
|
||||
'200':
|
||||
description: List of alerts
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
alerts:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Alert'
|
||||
limit:
|
||||
type: integer
|
||||
offset:
|
||||
type: integer
|
||||
|
||||
/events:
|
||||
get:
|
||||
tags: [events]
|
||||
summary: List sensor events
|
||||
operationId: listEvents
|
||||
parameters:
|
||||
- name: type
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: sensor_id
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
- $ref: '#/components/parameters/hours'
|
||||
- $ref: '#/components/parameters/limit'
|
||||
- $ref: '#/components/parameters/offset'
|
||||
responses:
|
||||
'200':
|
||||
description: List of events
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
events:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Event'
|
||||
limit:
|
||||
type: integer
|
||||
offset:
|
||||
type: integer
|
||||
|
||||
/probes:
|
||||
get:
|
||||
tags: [probes]
|
||||
summary: List probe requests
|
||||
operationId: listProbes
|
||||
parameters:
|
||||
- name: ssid
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- $ref: '#/components/parameters/hours'
|
||||
- $ref: '#/components/parameters/limit'
|
||||
- $ref: '#/components/parameters/offset'
|
||||
responses:
|
||||
'200':
|
||||
description: List of probe requests
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
probes:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Probe'
|
||||
limit:
|
||||
type: integer
|
||||
offset:
|
||||
type: integer
|
||||
|
||||
/probes/ssids:
|
||||
get:
|
||||
tags: [probes]
|
||||
summary: List SSIDs with counts
|
||||
operationId: listSsids
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hours'
|
||||
responses:
|
||||
'200':
|
||||
description: SSID list with counts
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
ssids:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
ssid:
|
||||
type: string
|
||||
count:
|
||||
type: integer
|
||||
|
||||
/stats:
|
||||
get:
|
||||
tags: [stats]
|
||||
summary: Get aggregate statistics
|
||||
operationId: getStats
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hours'
|
||||
responses:
|
||||
'200':
|
||||
description: Statistics
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Stats'
|
||||
|
||||
/export/devices.csv:
|
||||
get:
|
||||
tags: [export]
|
||||
summary: Export devices as CSV
|
||||
operationId: exportDevicesCsv
|
||||
responses:
|
||||
'200':
|
||||
description: CSV file
|
||||
content:
|
||||
text/csv:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/export/devices.json:
|
||||
get:
|
||||
tags: [export]
|
||||
summary: Export devices as JSON
|
||||
operationId: exportDevicesJson
|
||||
responses:
|
||||
'200':
|
||||
description: JSON file
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Device'
|
||||
|
||||
/export/alerts.csv:
|
||||
get:
|
||||
tags: [export]
|
||||
summary: Export alerts as CSV
|
||||
operationId: exportAlertsCsv
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hours'
|
||||
responses:
|
||||
'200':
|
||||
description: CSV file
|
||||
content:
|
||||
text/csv:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/export/probes.csv:
|
||||
get:
|
||||
tags: [export]
|
||||
summary: Export probes as CSV
|
||||
operationId: exportProbesCsv
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/hours'
|
||||
responses:
|
||||
'200':
|
||||
description: CSV file
|
||||
content:
|
||||
text/csv:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
components:
|
||||
parameters:
|
||||
hostname:
|
||||
name: hostname
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: muddy-storm
|
||||
limit:
|
||||
name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 100
|
||||
maximum: 1000
|
||||
offset:
|
||||
name: offset
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 0
|
||||
hours:
|
||||
name: hours
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 24
|
||||
|
||||
responses:
|
||||
NotFound:
|
||||
description: Resource not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
|
||||
schemas:
|
||||
Sensor:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
hostname:
|
||||
type: string
|
||||
ip:
|
||||
type: string
|
||||
last_seen:
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: string
|
||||
enum: [online, stale, offline, unknown]
|
||||
|
||||
SensorConfig:
|
||||
type: object
|
||||
properties:
|
||||
hostname:
|
||||
type: string
|
||||
ip:
|
||||
type: string
|
||||
version:
|
||||
type: string
|
||||
uptime:
|
||||
type: string
|
||||
rate:
|
||||
type: integer
|
||||
tx_power:
|
||||
type: integer
|
||||
adaptive:
|
||||
type: boolean
|
||||
ble:
|
||||
type: boolean
|
||||
presence:
|
||||
type: boolean
|
||||
powersave:
|
||||
type: boolean
|
||||
csi_mode:
|
||||
type: string
|
||||
channel:
|
||||
type: integer
|
||||
rssi:
|
||||
type: number
|
||||
heap:
|
||||
type: integer
|
||||
|
||||
SensorMetrics:
|
||||
type: object
|
||||
properties:
|
||||
hostname:
|
||||
type: string
|
||||
hours:
|
||||
type: integer
|
||||
activity:
|
||||
type: object
|
||||
properties:
|
||||
sightings:
|
||||
type: integer
|
||||
alerts:
|
||||
type: integer
|
||||
events:
|
||||
type: integer
|
||||
recent_events:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Event'
|
||||
|
||||
HeartbeatSummary:
|
||||
type: object
|
||||
properties:
|
||||
total:
|
||||
type: integer
|
||||
online:
|
||||
type: integer
|
||||
stale:
|
||||
type: integer
|
||||
offline:
|
||||
type: integer
|
||||
sensors:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
hostname:
|
||||
type: string
|
||||
ip:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
last_seen:
|
||||
type: string
|
||||
format: date-time
|
||||
seconds_ago:
|
||||
type: integer
|
||||
|
||||
Device:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
mac:
|
||||
type: string
|
||||
device_type:
|
||||
type: string
|
||||
enum: [ble, wifi]
|
||||
vendor:
|
||||
type: string
|
||||
nullable: true
|
||||
name:
|
||||
type: string
|
||||
nullable: true
|
||||
company_id:
|
||||
type: integer
|
||||
nullable: true
|
||||
manufacturer:
|
||||
type: string
|
||||
nullable: true
|
||||
first_seen:
|
||||
type: string
|
||||
format: date-time
|
||||
last_seen:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
Sighting:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
device_id:
|
||||
type: integer
|
||||
sensor_id:
|
||||
type: integer
|
||||
rssi:
|
||||
type: integer
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
Alert:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
sensor_id:
|
||||
type: integer
|
||||
alert_type:
|
||||
type: string
|
||||
source_mac:
|
||||
type: string
|
||||
nullable: true
|
||||
target_mac:
|
||||
type: string
|
||||
nullable: true
|
||||
rssi:
|
||||
type: integer
|
||||
nullable: true
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
Event:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
sensor_id:
|
||||
type: integer
|
||||
type:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
Probe:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
sensor_id:
|
||||
type: integer
|
||||
device_id:
|
||||
type: integer
|
||||
ssid:
|
||||
type: string
|
||||
rssi:
|
||||
type: integer
|
||||
channel:
|
||||
type: integer
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
Stats:
|
||||
type: object
|
||||
properties:
|
||||
sensors:
|
||||
type: object
|
||||
properties:
|
||||
total:
|
||||
type: integer
|
||||
online:
|
||||
type: integer
|
||||
devices:
|
||||
type: object
|
||||
properties:
|
||||
total:
|
||||
type: integer
|
||||
ble:
|
||||
type: integer
|
||||
wifi:
|
||||
type: integer
|
||||
alerts:
|
||||
type: object
|
||||
properties:
|
||||
count:
|
||||
type: integer
|
||||
hours:
|
||||
type: integer
|
||||
events:
|
||||
type: object
|
||||
properties:
|
||||
count:
|
||||
type: integer
|
||||
hours:
|
||||
type: integer
|
||||
probes:
|
||||
type: object
|
||||
properties:
|
||||
count:
|
||||
type: integer
|
||||
hours:
|
||||
type: integer
|
||||
Reference in New Issue
Block a user