Difference between revisions of "API reference"

From bcmeter.org
Jump to navigation Jump to search
(Fix hostname: bcmeter-XXXX.local (last 4 hex chars of MAC))
(Update API reference to match current bcmeter-dev codebase: add incidents section to /api/logs, add identify/debug_mobile actions, add /api/maintenance-logs and /api/debug_mobile/status, add wifi/scan/refresh POST, update response schemas, add static routes, remove endpoints not in current codebase (cleanempty/export/factory_reset), add CORS note)
 
Line 1: Line 1:
== API Reference ==
== API Reference ==


The bcMeter exposes a REST API on port 80. All endpoints are accessible without authentication when connected to the device network.
The bcMeter exposes a REST API on port 80. All endpoints are accessible without authentication when connected to the device network. CORS is enabled for all GET requests (<code>allow_origins=["*"]</code>).


The software is open source: [https://github.com/dahljo/bcmeter github.com/dahljo/bcmeter]
The software is open source: [https://github.com/dahljo/bcmeter github.com/dahljo/bcmeter]
Line 10: Line 10:
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description
|-
|-
| GET || <code>/api/status</code> || Current measurement state, last sensor readings, error codes, sample count, WiFi mode, firmware version
| GET || <code>/api/status</code> || Current measurement state, last sensor readings, error codes, sample count, WiFi mode, firmware version, OTA info. Returns <code>"env": "pi"</code> to distinguish from ESP32 (<code>"env": "esp32"</code>).
|-
|-
| GET || <code>/api/system</code> || Hardware info: MAC address, memory, time, last calibration, GPS position, barometric altitude
| GET || <code>/api/system</code> || Hardware info: IP, MAC address, disk usage, memory, modem status, GPS detail, barometric altitude, geolocation, current time
|-
|-
| GET || <code>/api/logs</code> || Structured health report with sections: hardware, measurement, system, network
| GET || <code>/api/logs</code> || Structured health report as JSON with sections: <code>hardware</code>, <code>measurement</code>, <code>system</code>, <code>network</code>, <code>incidents</code>. Each entry has <code>{k, v, s}</code> (key, value, status: ok/warn/error/info).
|-
| GET || <code>/api/maintenance-logs</code> || Downloads a zip bundle containing: rotating bcmeter.log files, syslog, 10 newest CSV sessions, dmesg, journalctl for bcmeter and pigpiod services, config JSON, incident log.
|-
| GET || <code>/api/debug_mobile/status</code> || Returns modem debug send progress (from email handler).
|}
|}


=== Measurement Control ===
=== Measurement Control ===
All control is dispatched through a single endpoint with an <code>action</code> query parameter:


{| class="wikitable"
{| class="wikitable"
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description
|-
|-
| GET || <code>/api/control?action=start</code> || Begin measurement session (requires time sync + calibration). Add <code>&force=1</code> to override preflight checks.
| GET || <code>/api/control?action=start</code> || Begin measurement session. Returns 409 if time not synced, 423 if never calibrated, 424 if calibration stale (>28 days). Add <code>&force=1</code> to override preflight checks.
|-
| GET || <code>/api/control?action=stop</code> || Stop measurement and save session file.
|-
| GET || <code>/api/control?action=calibrate</code> || Start calibration routine (returns 202 immediately — poll <code>/api/calibration</code> for progress). Requires sampling to be stopped.
|-
|-
| GET || <code>/api/control?action=stop</code> || Stop measurement and save session file
| GET || <code>/api/calibration</code> || Calibration status: <code>{running, done, ok, elapsed_ms, log}</code>.
|-
|-
| GET || <code>/api/control?action=calibrate</code> || Start calibration routine (async — poll <code>/api/calibration</code> for progress)
| GET || <code>/api/control?action=clear_error</code> || Reset error state to ERR_NONE.
|-
|-
| GET || <code>/api/calibration</code> || Calibration status: running, elapsed time, log output, success/failure
| GET || <code>/api/control?action=identify</code> || Triggers LED identify blink pattern (Morse code spelling "bcmeter" for 30 seconds). Useful for locating a specific device when multiple units are deployed.
|-
|-
| GET || <code>/api/control?action=clear_error</code> || Reset error state
| GET || <code>/api/control?action=debug_mobile</code> || Triggers modem debug email via Lambda. Requires modem present.
|}
|}


Line 40: Line 50:
| GET || <code>/api/config</code> || All settings as JSON. Each parameter includes type, value, label, tab, and description.
| GET || <code>/api/config</code> || All settings as JSON. Each parameter includes type, value, label, tab, and description.
|-
|-
| POST || <code>/api/config</code> || Update settings. JSON body with key-value pairs. Only changed values need to be sent.
| POST || <code>/api/config</code> || Update settings. JSON body with key-value pairs. Accepts <code>{key: value}</code> or <code>{key: {value: ...}}</code> format. Only changed values need to be sent. Triggers deferred modem onboarding if <code>mail_logs_to</code> is set.
|-
|-
| POST || <code>/api/device/rename</code> || Set device name. Body: <code>{"name": "bcmeter01"}</code>
| POST || <code>/api/device/rename</code> || Set device name and hostname. Body: <code>{"name": "bcmeter01"}</code> (1–32 chars). Runs <code>hostnamectl set-hostname</code>.
|-
|-
| GET || <code>/api/control?action=synctime&ts=EPOCH&tz=IANA</code> || Sync device clock. <code>ts</code> = Unix timestamp, <code>tz</code> = IANA timezone string.
| GET || <code>/api/control?action=synctime&ts=EPOCH&tz=IANA</code> || Sync device clock. <code>ts</code> = Unix timestamp, <code>tz</code> = IANA timezone string (e.g. <code>Europe/Berlin</code>). Applies timezone via <code>timedatectl</code>.
|}
|}


Line 52: Line 62:
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description
|-
|-
| GET || <code>/api/wifi/scan</code> || List nearby networks. Add <code>?refresh=1</code> to trigger a new scan.
| GET || <code>/api/wifi/scan</code> || List nearby networks: <code>{scanning: bool, networks: [{ssid, rssi, secure}]}</code>. Add <code>?refresh=1</code> to trigger a new background scan.
|-
| POST || <code>/api/wifi/scan/refresh</code> || Explicitly trigger a new WiFi scan (returns 202, or 409 if already scanning).
|-
|-
| GET || <code>/api/wifi/status</code> || Current connection: mode (sta/ap), SSID, IP address
| GET || <code>/api/wifi/status</code> || Current connection: <code>{mode, status, ssid, ip, rssi, quality (0-4), internet, timeSynced}</code>.
|-
|-
| POST || <code>/api/wifi/connect</code> || Connect to network. Body: <code>{"ssid": "...", "pass": "...", "hidden": false}</code>
| POST || <code>/api/wifi</code> || Save WiFi credentials. Body: <code>{"ssid": "...", "pass": "..."}</code> (password min 8 chars).
|-
|-
| GET || <code>/api/wifi/connect/status</code> || Connection progress: state, hostname, log messages
| POST || <code>/api/wifi/connect</code> || Save credentials and initiate background connection attempt (returns 202). Password must be 8–63 chars. Body: <code>{"ssid": "...", "pass": "..."}</code>
|-
|-
| POST || <code>/api/wifi/delete</code> || Clear saved WiFi credentials, switch to AP mode
| GET || <code>/api/wifi/connect/status</code> || Connection progress: <code>{state: 0-3, elapsed, log, mode, ip, ssid, device_name, hostname}</code>. States: 0=idle, 1=connecting, 2=success, 3=failed. Poll after POST to <code>/api/wifi/connect</code>.
|-
|-
| GET || <code>/api/ap-security</code> || Hotspot security settings: secured (bool), password
| POST || <code>/api/wifi/delete</code> || Delete credentials and switch to AP mode (deferred 2s background switch).
|-
|-
| POST || <code>/api/ap-security</code> || Update hotspot password. Body: <code>{"secured": true, "password": "..."}</code>
| GET || <code>/api/ap-security</code> || Hotspot security settings: <code>{secured: bool, password: str}</code>.
|-
| POST || <code>/api/ap-security</code> || Update hotspot password. Body: <code>{"secured": true, "password": "..."}</code> (min 8 chars).
|}
|}


Line 72: Line 86:
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description
|-
|-
| GET || <code>/api/csv</code> || Current session CSV data. Add <code>?file=FILENAME</code> for archived files.
| GET || <code>/api/csv</code> || Current session CSV data. Returns header-only if no active session. Add <code>?file=FILENAME</code> for archived files.
|-
|-
| GET || <code>/api/files</code> || List stored CSV logs as JSON: <code>[{"name": "...", "size": 1234}, ...]</code>
| GET || <code>/api/files</code> || List stored CSV logs: <code>[{name, size, lines, date}]</code> sorted newest first.
|-
|-
| GET || <code>/api/control?action=cleardata</code> || Delete all stored log files
| GET || <code>/api/control?action=cleardata</code> || Delete all stored log files.
|-
| GET || <code>/api/control?action=cleanempty</code> || Remove empty (0 byte) log files
|-
| GET || <code>/api/control?action=export</code> || Export all logs to SD card
|}
|}


Line 88: Line 98:
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description
|-
|-
| GET || <code>/api/ota/status</code> || Check if update available: version, release notes, apply state/progress
| GET || <code>/api/ota/status</code> || Check if update available: <code>{available, version, url, sha256, notes, skipped}</code>.
|-
|-
| POST || <code>/api/ota/check</code> || Force check for new firmware
| POST || <code>/api/ota/check</code> || Force check for new firmware against GitHub Releases.
|-
|-
| POST || <code>/api/ota/skip</code> || Skip current available update
| POST || <code>/api/ota/skip</code> || Skip current available update for this boot.
|-
|-
| POST || <code>/api/ota/apply</code> || Download and flash firmware update
| POST || <code>/api/ota/apply</code> || Download and apply firmware update. Returns 409 if none pending.
|-
|-
| POST || <code>/api/update</code> || Direct firmware upload (multipart form-data, .tar.gz)
| POST || <code>/api/update</code> || Direct firmware upload (.tar.gz or .zip archive). Extracts over the code directory and restarts <code>bcMeter.service</code> via <code>systemctl</code>. Stops active session first.
|}
|}


Line 104: Line 114:
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description
|-
|-
| GET || <code>/api/control?action=reboot</code> || Reboot the device
| GET || <code>/api/control?action=reboot</code> || End session and reboot the device (fire-and-forget thread via <code>sudo reboot</code>).
|-
| GET || <code>/api/control?action=shutdown</code> || Safely shut down
|-
|-
| GET || <code>/api/control?action=factory_reset</code> || Erase all data and settings, reboot to factory state
| GET || <code>/api/control?action=shutdown</code> || Safely shut down via <code>sudo shutdown -h now</code>.
|-
|-
| POST || <code>/api/email/validate</code> || Validate email API key. Body: <code>{"api_key": "..."}</code>
| POST || <code>/api/email/validate</code> || Validate email API key against the Lambda endpoint. Body: <code>{"api_key": "..."}</code>. Returns <code>{valid: bool, error: str}</code>.
|}
|}


=== Captive Portal ===
=== Captive Portal ===


These endpoints handle captive portal detection on mobile devices:
These endpoints handle captive portal detection on mobile devices and redirect to the main interface in AP mode:


{| class="wikitable"
{| class="wikitable"
Line 124: Line 132:
| <code>/hotspot-detect.html</code> || Apple captive portal check
| <code>/hotspot-detect.html</code> || Apple captive portal check
|-
|-
| <code>/success.txt</code> || Generic connectivity probe
| <code>/canonical.html</code> || Firefox captive portal check
|-
| <code>/ncsi.txt</code> || Windows captive portal check
|-
| <code>/success.txt</code> || Generic connectivity probe (returns <code>"success"</code>)
|}
 
=== Static Routes ===
 
{| class="wikitable"
! Path !! Description
|-
| <code>/</code> || Serves the SPA frontend (<code>interface/index.html</code>)
|-
| <code>/discover</code> || Standalone device discovery page (also hostable on bcmeter.org)
|-
| <code>/manual/*</code> || Static files from <code>interface/manual/</code>
|-
| <code>/current_log.csv</code> || Legacy redirect to <code>/api/csv</code>
|}
|}



Latest revision as of 19:41, 31 March 2026

API Reference

The bcMeter exposes a REST API on port 80. All endpoints are accessible without authentication when connected to the device network. CORS is enabled for all GET requests (allow_origins=["*"]).

The software is open source: github.com/dahljo/bcmeter

Status & Diagnostics

Method Endpoint Description
GET /api/status Current measurement state, last sensor readings, error codes, sample count, WiFi mode, firmware version, OTA info. Returns "env": "pi" to distinguish from ESP32 ("env": "esp32").
GET /api/system Hardware info: IP, MAC address, disk usage, memory, modem status, GPS detail, barometric altitude, geolocation, current time
GET /api/logs Structured health report as JSON with sections: hardware, measurement, system, network, incidents. Each entry has {k, v, s} (key, value, status: ok/warn/error/info).
GET /api/maintenance-logs Downloads a zip bundle containing: rotating bcmeter.log files, syslog, 10 newest CSV sessions, dmesg, journalctl for bcmeter and pigpiod services, config JSON, incident log.
GET /api/debug_mobile/status Returns modem debug send progress (from email handler).

Measurement Control

All control is dispatched through a single endpoint with an action query parameter:

Method Endpoint Description
GET /api/control?action=start Begin measurement session. Returns 409 if time not synced, 423 if never calibrated, 424 if calibration stale (>28 days). Add &force=1 to override preflight checks.
GET /api/control?action=stop Stop measurement and save session file.
GET /api/control?action=calibrate Start calibration routine (returns 202 immediately — poll /api/calibration for progress). Requires sampling to be stopped.
GET /api/calibration Calibration status: {running, done, ok, elapsed_ms, log}.
GET /api/control?action=clear_error Reset error state to ERR_NONE.
GET /api/control?action=identify Triggers LED identify blink pattern (Morse code spelling "bcmeter" for 30 seconds). Useful for locating a specific device when multiple units are deployed.
GET /api/control?action=debug_mobile Triggers modem debug email via Lambda. Requires modem present.

Configuration

Method Endpoint Description
GET /api/config All settings as JSON. Each parameter includes type, value, label, tab, and description.
POST /api/config Update settings. JSON body with key-value pairs. Accepts {key: value} or {key: {value: ...}} format. Only changed values need to be sent. Triggers deferred modem onboarding if mail_logs_to is set.
POST /api/device/rename Set device name and hostname. Body: {"name": "bcmeter01"} (1–32 chars). Runs hostnamectl set-hostname.
GET /api/control?action=synctime&ts=EPOCH&tz=IANA Sync device clock. ts = Unix timestamp, tz = IANA timezone string (e.g. Europe/Berlin). Applies timezone via timedatectl.

WiFi Management

Method Endpoint Description
GET /api/wifi/scan List nearby networks: {scanning: bool, networks: [{ssid, rssi, secure}]}. Add ?refresh=1 to trigger a new background scan.
POST /api/wifi/scan/refresh Explicitly trigger a new WiFi scan (returns 202, or 409 if already scanning).
GET /api/wifi/status Current connection: {mode, status, ssid, ip, rssi, quality (0-4), internet, timeSynced}.
POST /api/wifi Save WiFi credentials. Body: {"ssid": "...", "pass": "..."} (password min 8 chars).
POST /api/wifi/connect Save credentials and initiate background connection attempt (returns 202). Password must be 8–63 chars. Body: {"ssid": "...", "pass": "..."}
GET /api/wifi/connect/status Connection progress: {state: 0-3, elapsed, log, mode, ip, ssid, device_name, hostname}. States: 0=idle, 1=connecting, 2=success, 3=failed. Poll after POST to /api/wifi/connect.
POST /api/wifi/delete Delete credentials and switch to AP mode (deferred 2s background switch).
GET /api/ap-security Hotspot security settings: {secured: bool, password: str}.
POST /api/ap-security Update hotspot password. Body: {"secured": true, "password": "..."} (min 8 chars).

Data Management

Method Endpoint Description
GET /api/csv Current session CSV data. Returns header-only if no active session. Add ?file=FILENAME for archived files.
GET /api/files List stored CSV logs: [{name, size, lines, date}] sorted newest first.
GET /api/control?action=cleardata Delete all stored log files.

Firmware Updates

Method Endpoint Description
GET /api/ota/status Check if update available: {available, version, url, sha256, notes, skipped}.
POST /api/ota/check Force check for new firmware against GitHub Releases.
POST /api/ota/skip Skip current available update for this boot.
POST /api/ota/apply Download and apply firmware update. Returns 409 if none pending.
POST /api/update Direct firmware upload (.tar.gz or .zip archive). Extracts over the code directory and restarts bcMeter.service via systemctl. Stops active session first.

System Operations

Method Endpoint Description
GET /api/control?action=reboot End session and reboot the device (fire-and-forget thread via sudo reboot).
GET /api/control?action=shutdown Safely shut down via sudo shutdown -h now.
POST /api/email/validate Validate email API key against the Lambda endpoint. Body: {"api_key": "..."}. Returns {valid: bool, error: str}.

Captive Portal

These endpoints handle captive portal detection on mobile devices and redirect to the main interface in AP mode:

Endpoint Purpose
/generate_204 Android captive portal check
/hotspot-detect.html Apple captive portal check
/canonical.html Firefox captive portal check
/ncsi.txt Windows captive portal check
/success.txt Generic connectivity probe (returns "success")

Static Routes

Path Description
/ Serves the SPA frontend (interface/index.html)
/discover Standalone device discovery page (also hostable on bcmeter.org)
/manual/* Static files from interface/manual/
/current_log.csv Legacy redirect to /api/csv

Example: Start a Measurement

# Sync time first
curl "http://bcmeter-XXXX.local/api/control?action=synctime&ts=$(date +%s)&tz=Europe/Berlin"

# Start measurement
curl "http://bcmeter-XXXX.local/api/control?action=start"

# Poll status
curl "http://bcmeter-XXXX.local/api/status"

# Download current data
curl "http://bcmeter-XXXX.local/api/csv" -o measurement.csv