This repository ships a production-style HTTP API on top of the same SDAP/SDCP stack used by the CLI and samples. Any frontend (React, Vue, mobile) or backend language can integrate via JSON over HTTP.
Diagrams: Control flow charts (Mermaid) — clients, gateway, SDAP/SDCP, and ADC knob path. MCU samples: arduino-knobs (HTTP), esp32-sdcp-vmc (native SDCP). OpenAPI → C: openapi-codegen.md.
| Piece | Path | Role |
|---|---|---|
| HTTP + JSON + Swagger + static UI | src/MonitorControl.Web |
ASP.NET Core 8 host (MonitorControl.Web) |
| Python reverse proxy + static UI | examples/python-service |
Optional uvicorn + httpx gateway to the .NET API |
dotnet run --project src/MonitorControl.Web --urls http://127.0.0.1:5080- Browser UI:
http://127.0.0.1:5080/— discover, VMC, VMS, VMA reads, firmware (guarded). - OpenAPI:
http://127.0.0.1:5080/swagger - Health:
GET /api/health
All JSON bodies use camelCase by default (host, timeoutMs, …).
| Method | Path | Body | Notes |
|---|---|---|---|
| GET | /api/health |
— | Liveness |
| GET | /api/sdap/discover |
query durationMs, optional bind |
UDP 53862 listen window; returns unique devices |
| POST | /api/vmc/get |
{ "host", "field", "timeoutMs"?, "sdcpUnitId"?, "vmcItem"? } |
STATget; sdcpUnitId 0–255 = single-connection unit; vmcItem = B000|monitor (default) or B001|builtIn |
| POST | /api/vmc/set |
{ "host", "args": ["TOKEN","…"], "timeoutMs"?, "sdcpUnitId"?, "vmcItem"? } |
STATset tail; same optional sdcpUnitId / vmcItem |
| POST | /api/vmc/broadcast |
{ "scope", "groupId"?, "broadcastAddress"?, "port"?, "localBind"?, "tokens", "vmcItem"? } |
UDP SDCP VMC to 53484 (no TCP host; affects every monitor in scope — use with care) |
| GET | /api/events/monitor |
query host, optional fields, intervalMs, sdcpUnitId, vmcItem |
SSE (text/event-stream): server polls STATget and pushes JSON lines. Custom event fault carries SDCP/TCP errors. |
| GET | /ws/monitor-watch |
query host, optional fields, intervalMs, sdcpUnitId, vmcItem |
WebSocket (binary/text UTF‑8 JSON objects): same poll model as SSE. |
| POST | /api/vms/product-info |
{ "host", "timeoutMs"? } |
VMS product info + hex payload |
| POST | /api/vma/control-software-version |
{ "host", "timeoutMs"? } |
VMA read |
| POST | /api/vma/kernel-version |
same | |
| POST | /api/vma/rtc |
same | |
| POST | /api/vma/fpga1-version |
same | |
| POST | /api/vma/fpga2-version |
same | |
| POST | /api/vma/fpga-core-version |
same | |
| POST | /api/vma/firmware/upgrade-kernel-size |
{ "host", "sizeBytes", "timeoutMs"? } |
Danger — see below |
| POST | /api/vma/firmware/upgrade-fpga-size |
{ "host", "sizeBytes", "timeoutMs"? } |
Danger |
| POST | /api/vma/firmware/upgrade-chunk |
{ "host", "chunkIndex", "timeoutMs"? } |
Danger |
| POST | /api/vma/firmware/upgrade-restart |
{ "host", "timeoutMs"? } |
Danger |
Firmware routes return 403 unless both:
- Server:
MonitorControl:AllowDangerousFirmware=trueinappsettings.jsonor environment variableMONITOR_CONTROL_ALLOW_DANGEROUS_FIRMWARE=1/true/yes. - Request header:
X-Firmware-Ack: CONFIRM(exact value, case-sensitive).
The bundled HTML UI sets this header when the operator checks the confirmation box.
Only one process can bind UDP 53862 on a given interface. If discover fails with “bind failed”, stop other tools using SDAP on that machine or pass bind to a free adapter address.
See examples/python-service/README.md. It proxies /api/* to the .NET base URL and serves the same static UI from port 8000 by default.
- SSE (
GET /api/events/monitor?...): proxied — the browser can stay on :8000 and the FastAPI app streams from the upstream .NET URL (main.pydetectsfull_path.startswith("events/")and useshttpxstreaming). - WebSocket (
GET /ws/monitor-watch): not proxied bypython-service. Open the WebSocket to the MonitorControl.Web listener (e.g.ws://127.0.0.1:5080/ws/monitor-watch?host=...), same host/port as Swagger.
See diagrams/monitor-control-flows.md (Python gateway figure).
SDCP in this stack is request/response on TCP; the monitor does not open an outbound HTTP channel. The SSE and WebSocket routes synthesize “live” updates by polling STATget on the server at intervalMs (default 2000 ms). Tune fields to tokens your chassis supports (defaults: MODEL, BRIGHTNESS, CONTRAST). Pass sdcpUnitId and vmcItem query parameters when the monitor matches the SDAP/TCP addressing rules in pvm-740-programmer-manual-synthesis.md (same strings as JSON vmcItem).
- Browser: bundled UI → “Live snapshots (SSE)” uses
EventSource. - WebSocket URL:
ws://<host>:<port>/ws/monitor-watch?host=<monitor-ip>&intervalMs=3000(usewss://behind TLS).
- Run
MonitorControl.Web(or proxy REST + SSE through the Python gateway on :8000 while keeping .NET on :5080). - Call
GET /api/sdap/discoverto learn monitor IPs (or use fixed inventory). Only one process should bind UDP 53862 per interface. - Use
POST /api/vmc/get/vmc/setfor shading and UI automation; optionalPOST /api/vmc/broadcastfor UDP Group/All (same token list asvmc/setbut no per-monitor TCP response). - Use
GET /api/events/monitor(SSE) and/or WebSocketGET /ws/monitor-watchfor push-shaped updates. If you use python-service, SSE can go through :8000; WebSocket must target the .NET port (see diagrams/monitor-control-flows.md). - Use
POST /api/vms/product-infofor structured factory reads. - Restrict firmware routes to authenticated operators; keep firmware disabled in production unless you fully control the OTA sequence.
- Route registration:
MonitorApiExtensions.cs,MonitorPushEndpoints.cs - Firmware gate:
WireFormat.cs - UI:
wwwroot/