HTTP API¶
This page documents the HTTP API exposed by the MAC server.
The OpenAPI spec is also available at /openapi.json on a running server,
with interactive documentation at /docs (Swagger UI) and /redocs (ReDoc).
- GET /api/¶
API index route.
Returns a placeholder message.
- Status Codes:
200 OK – Response body for GET /api/.
- DELETE /api/machine/locked_out/{machine_name}¶
Set or clear machine lockout state.
POST to lock out a machine (prevent all use). DELETE to unlock the machine.
- Parameters:
machine_name (string)
- Status Codes:
200 OK – Generic success response.
404 Not Found – Generic error response.
500 Internal Server Error – Generic error response.
503 response body for the oops and locked_out endpoints when persisting machine state to disk exceeds
STATE_SAVE_TIMEOUT_SEC.The in-memory state mutation (and any fire-and-forget Slack notification) has already taken effect; only the pickle write timed out. The next successful save will catch up.
- POST /api/machine/locked_out/{machine_name}¶
Set or clear machine lockout state.
POST to lock out a machine (prevent all use). DELETE to unlock the machine.
- Parameters:
machine_name (string)
- Status Codes:
200 OK – Generic success response.
404 Not Found – Generic error response.
500 Internal Server Error – Generic error response.
503 response body for the oops and locked_out endpoints when persisting machine state to disk exceeds
STATE_SAVE_TIMEOUT_SEC.The in-memory state mutation (and any fire-and-forget Slack notification) has already taken effect; only the pickle write timed out. The next successful save will catch up.
- DELETE /api/machine/oops/{machine_name}¶
Set or clear machine Oops state.
POST to set the machine into Oops (maintenance needed) state. DELETE to clear the Oops state.
- Parameters:
machine_name (string)
- Status Codes:
200 OK – Generic success response.
404 Not Found – Generic error response.
500 Internal Server Error – Generic error response.
503 response body for the oops and locked_out endpoints when persisting machine state to disk exceeds
STATE_SAVE_TIMEOUT_SEC.The in-memory state mutation (and any fire-and-forget Slack notification) has already taken effect; only the pickle write timed out. The next successful save will catch up.
- POST /api/machine/oops/{machine_name}¶
Set or clear machine Oops state.
POST to set the machine into Oops (maintenance needed) state. DELETE to clear the Oops state.
- Parameters:
machine_name (string)
- Status Codes:
200 OK – Generic success response.
404 Not Found – Generic error response.
500 Internal Server Error – Generic error response.
503 response body for the oops and locked_out endpoints when persisting machine state to disk exceeds
STATE_SAVE_TIMEOUT_SEC.The in-memory state mutation (and any fire-and-forget Slack notification) has already taken effect; only the pickle write timed out. The next successful save will catch up.
- POST /api/machine/update¶
API method to update machine state.
Accepts POSTed JSON containing the following key/value pairs:
machine_name(string) - name of the machine sending the updateoops(boolean) - whether the oops button is pressedrfid_value(string) - value of the RFID fob/card that is currentlypresent in the machine, or empty string if none present. Note that ESPHome strips leading zeroes from this, so inside this method it is left-padded with zeroes to a length of 10 characters.
uptime(float) - uptime of the ESP32 (MCU).wifi_signal_db(float) - WiFi signal strength in dBwifi_signal_percent(float) - WiFi signal strength in percentinternal_temperature_c(float) - internal temperature of the ESP32 in°C.
amps(float; optional) - amperage value from the current clampammeter, if present, or 0.0 otherwise.
- Status Codes:
200 OK – Response body for POST /api/machine/update (200).
404 Not Found – Generic error response.
500 Internal Server Error – Generic error response.
503 Service Unavailable – Generic error response.
- POST /api/reload-users¶
Reload users configuration.
Hot-reloads users.json without requiring a server restart. Returns counts of removed, updated, and added users.
- Status Codes:
200 OK – Response body for POST /api/reload-users (200).
500 Internal Server Error – Generic error response.
- GET /metrics¶
API method to return Prometheus-compatible metrics.
State Save Timeout (HTTP 503)¶
The endpoints that mutate machine state —
POST /api/machine/update,POST /api/machine/oops/<machine_name>andDELETE /api/machine/oops/<machine_name>,POST /api/machine/locked_out/<machine_name>andDELETE /api/machine/locked_out/<machine_name>
— bound the time spent persisting state to disk to
STATE_SAVE_TIMEOUT_SEC (2.0 seconds). If the underlying disk hangs
and the write exceeds this budget, the handler returns:
For POST /api/machine/update (firmware-facing):
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{"error": "state save timeout"}
For the admin endpoints (POST/DELETE on /api/machine/oops/<name>
and /api/machine/locked_out/<name>), the response body includes
action_applied: true to indicate that the requested state mutation
took effect in memory even though persistence timed out — these
actions also send Slack notifications in a fire-and-forget fashion,
which cannot be rolled back, so the action is live and the next
successful save will catch up:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{"error": "state save timeout", "action_applied": true}
For the firmware-facing endpoint, the 503 response is the recommended
path for the MCU to recover from a stuck disk on the server: it sees a
clean error, leaves its current relay state alone, and retries on its
next 10-second heartbeat. Without this bound, a slow-but-eventually
successful 200 response can wedge the firmware via ESPHome
http_request issues such as #6677.
Each timeout increments the per-machine mac_state_save_timeouts_total
Prometheus counter. A Slack notification is posted to
SLACK_CONTROL_CHANNEL_ID exactly once, on the transition from 1
to 2 lifetime timeouts for a given machine; subsequent timeouts under a
sustained disk hang do not re-page (operators monitoring the Prometheus
counter can alert on continued growth).
Second Relay Protocol Additions¶
POST /api/machine/update accepts an optional additive request field and emits
an additive response field to support the per-machine second_relay
configuration (see Second Relay (second_relay) — Optional):
Request —
second_relay_state(boolean, optional): The actual current state of the second relay as known to the MCU. Reported by firmware that drives a second relay; older firmware omits this field. The server uses this for observability only and never for authorization decisions.Response —
second_relay(boolean, always emitted, defaults tofalse): Desired state of the second relay. For machines withoutsecond_relayconfigured this is alwaysfalse. Firmware that does not know about this field simply ignores it.
The display field is byte-identical between pre-feature and post-feature
servers for any given (machine, operator, machine state) tuple. second_relay
configuration never causes LCD changes.
Prometheus Metrics¶
GET /metrics
Returns Prometheus-compatible metrics in text/plain format. This endpoint
is not included in the OpenAPI spec as it does not return JSON.
For machines with second_relay configured, the following additional
metrics are emitted (one sample per machine, with labels machine_name,
display_name, and second_relay_alias):
machine_second_relay_state— whether the second relay is currently energized (0/1)machine_second_relay_configured— always1for machines with asecond_relayblockmachine_second_relay_unauth_warn_only— theunauthorized_warn_onlyconfig flag (0/1)machine_second_relay_always_enabled— thealways_enabledconfig flag (0/1)
These metrics are not emitted at all for machines without second_relay,
keeping cardinality minimal.
The mac_state_save_timeouts_total counter (one sample per machine)
exposes the lifetime count of writes to the per-machine pickle that
exceeded STATE_SAVE_TIMEOUT_SEC. See State Save Timeout (HTTP 503)
above for the firmware-facing behavior.
See dm_mac.views.prometheus for details on the available metrics.