openapi: 3.0.3
info:
title: OpenEVSE WiFi API
description: |
The API for the OpenEVSE WiFi module
## Authentication
Authentication is only enforced when an admin password is configured. With no
password set (or while the device is in AP-only provisioning mode) every
operation is open and none of the credentials below are needed. When a password
is set, every operation except `/login`, `/logout` and `/emoncms/describe`
requires **one** of:
* **HTTP Basic** credentials (`Authorization` header) - intended for machine
clients such as scripts and integrations. If the username is left blank in the
configuration a default admin user is used.
* A **session cookie** (`oevse_session`) obtained from [`POST /login`](#tag/Authentication)
- intended for the browser UI. Sessions last 6 hours, or 30 days if `remember`
was requested.
Over plain HTTP, Basic credentials and the session cookie are sent without
transport encryption. Use HTTPS, which the device serves once a certificate is
configured, to protect them in transit.
### Authentication failures
| Status | Meaning |
|---|---|
| `401` | No valid credentials were supplied. Clients that send `X-Requested-With: OpenEVSE` get a JSON body `{"msg":"auth"}`; any other client gets a `WWW-Authenticate` Basic challenge so browsers prompt for credentials. |
| `429` | A *wrong* Basic password, or a failed `/login`, was presented while the brute-force throttle is active (`{"msg":"locked"}`). Correct credentials are never throttled, and the counter decays on its own. Requests with no credentials at all still get `401`. |
| `403` | CSRF guard: a state-changing (non-`GET`) request authenticated by session cookie must also send `X-Requested-With: OpenEVSE`. Basic-auth clients are unaffected. A few `GET` endpoints also require this header and are documented on the operation. |
version: '1.0'
contact:
email: jeremy@bigjungle.net
name: Jeremy Poulter
url: 'https://github.com/jeremypoulter/'
license:
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
servers:
- url: '{server}'
variables:
server:
default: 'http://openevse.local'
- url: 'http://localhost:8080'
security:
- {}
- BasicAuth: []
- SessionCookie: []
paths:
/status:
get:
operationId: getStatus
summary: Get the EVSE status
description: |
The status endpoint can be called to get the status of the EVSE.
While it is posible to poll this endpoint, the recomendatoin is to retrieve the initial
state then use the [/ws](#statusUpdates)
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: ./models/Status.yaml
examples:
EVSE Status:
value:
mode: STA
wifi_client_connected: 1
eth_connected: 0
net_connected: 1
srssi: -73
ipaddress: 192.168.1.43
emoncms_connected: 1
packets_sent: 22307
packets_success: 22290
mqtt_connected: 1
free_heap: 203268
comm_sent: 335139
comm_success: 335139
rapi_connected: 1
amp: 0
pilot: 32
max_current: 32
temp1: 282
temp2: -2560
temp3: -2560
state: 254
elapsed: 3473
session_energy: 25034
total_energy: 998
total_day: 23
total_week: 68
total_month: 126
total_year: 998
gfcicount: 0
nogndcount: 0
stuckcount: 0
divertmode: 1
solar: 390
grid_ie: 0
charge_rate: 7
divert_update: 0
ota_update: 0
time: '2020-05-12T17:53:48Z'
offset: '+0000'
tags:
- Status
post:
operationId: postStatus
summary: Update external data to EVSE status
description: |
The status endpoint can be called to post external data that should be updated by MQTT
If MQTT is not an option, all external data needed can be updated from an HTTP POST request to /status endpoint.
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
voltage:
type: integer
description: live voltage in V
shaper_live_pwr:
type: integer
description: total household live power in W
solar:
type: integer
description: divert solar production in W
grid_ie:
type: integer
description: divert grid -import/+export in W
battery_level:
type: integer
description: vehicle soc in %
battery_range:
type: integer
description: vehicle range
time_to_full_charge:
type: integer
description: vehicle charge ETA
x-examples:
EVSE Status:
voltage: 220
shaper_live_pwr: 3400
solar: 3000
grid_ie: 3000
battery_level: 85
battery_range: 230
time_to_full_charge: 1590
examples:
EVSE Status:
value:
voltage: 220
shaper_live_pwr: 3400
solar: 3000
grid_ie: 3000
battery_level: 85
battery_range: 230
time_to_full_charge: 1590
tags:
- Status
/ws:
get:
operationId: statusUpdates
summary: EVSE status updates
description: |
As the state changes on the EVSE a websocket frame will be sent over the socket.
The frame will contain a JSON document conntaining only the updated state.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: ./models/Status.yaml
'400':
description: Error
tags:
- Status
/config:
get:
operationId: getConfig
summary: Get the EVSE configuration
description: |
Retrieve the current WiFi and EVSE module configuration
tags:
- Config
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: ./models/Config.yaml
examples: {}
post:
description: |
Update the EVSE configuration
operationId: updateConfig
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'400':
$ref: '#/components/responses/BadRequest'
requestBody:
content:
application/json:
schema:
$ref: ./models/Config.yaml
examples:
Update EmonCMS setting:
value:
emoncms_enabled: true
emoncms_server: 'http://emoncms.org'
emoncms_node: openevse-a7d4
emoncms_apikey: emoncmskey
emoncms_fingerprint: ''
Configure Solar divert:
value:
divert_type: 0
divert_enabled: true
divert_attack_smoothing_time: 8
divert_decay_smoothing_time: 300
divert_min_charge_time: 20
Set normal charging mode:
value:
charge_mode: fast
Set Eco mode:
value:
charge_mode: eco
Configure Tesla credentials:
value:
tesla_enabled: true
tesla_access_token: qts-1234.......
tesla_refresh_token: abcd.......
tesla_created_at: 1623358948
tesla_expires_in: 3888000
Select Tesla vehicle:
value:
tesla_vehicle_id: '2238974987235987'
summary: Update the EVSE configuration
tags:
- Config
/override:
description: Manual override can be used to override a charging timer or to immediately start a charge if the EVSE is in sleeping state.
get:
responses:
'200':
description: Manual overide is active
content:
application/json:
schema:
$ref: ./models/Properties.yaml
'404':
$ref: '#/components/responses/NotFound'
operationId: getManualOverride
tags:
- Manual Override
summary: Get the Manual Override status
description: Returns the current state of the manual override.
post:
description: |
Set the manual override
requestBody:
content:
application/json:
schema:
$ref: ./models/Properties.yaml
examples:
Force stop of charging:
value:
state: disabled
example-1:
value:
state: active
charge_current: 0
max_current: 0
auto_release: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
operationId: setManualOverride
tags:
- Manual Override
summary: Set the manual override
patch:
description: Toggle the manual override
responses:
'200':
description: OK
operationId: toggleManualOverride
tags:
- Manual Override
summary: Toggle the manual Override
delete:
description: Clears the manual override
responses:
'200':
description: OK
operationId: clearManualOverride
tags:
- Manual Override
summary: Clear the manual override
/claims:
get:
description: |
List all claims made by EVSE clients.
The response will be an array of EVSE claims.
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: ./models/Claim.yaml
operationId: listClaims
tags:
- Claims
summary: List EVSE claims
'/claims/{client}':
get:
description: |
Get the claim information for a specific client.
The response will be an EVSE claim.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: ./models/Properties.yaml
'404':
$ref: '#/components/responses/NotFound'
parameters: []
operationId: getClaim
tags:
- Claims
summary: Get EVSE claim information
post:
description: Make/Update a claim
requestBody:
content:
application/json:
schema:
$ref: ./models/Properties.yaml
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'400':
$ref: '#/components/responses/BadRequest'
operationId: setClaim
summary: Make/Update an EVSE claim
tags:
- Claims
delete:
description: Release a claim
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'404':
$ref: '#/components/responses/NotFound'
operationId: releaseClaim
summary: Release an EVSE claim
tags:
- Claims
parameters:
- schema:
type: integer
name: client
in: path
required: true
description: 'The client ID, defined by [OpenEVSE](https://github.com/OpenEVSE/openevse_esp32_firmware/blob/master/src/evse_man.h#L28)'
/schedule:
get:
summary: List schedule events
description: This will get a complete list of events in the schedule
responses:
'200':
description: List of the schedule events
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ScheduleEvent'
'400':
$ref: '#/components/responses/BadRequest'
operationId: listSchedule
tags:
- Schedule
post:
operationId: updateSchedule
description: Perform multiple event updates
responses:
'201':
description: item created
'400':
description: 'invalid input, object invalid'
'409':
description: an existing item already exists
requestBody:
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ScheduleEvent'
examples:
Single event:
value:
- id: 1
state: active
time: '14:15:22Z'
days:
- monday
Daily Timer:
value:
- id: 1
state: active
days:
- monday
- tuesday
- wednesday
- thursday
- friday
- saturday
- sunday
time: '07:00:00'
- id: 2
state: disabled
days:
- monday
- tuesday
- wednesday
- thursday
- friday
- saturday
- sunday
time: '10:00:00'
description: Inventory item to add
tags:
- Schedule
summary: Batch update schedule
'/schedule/{id}':
parameters:
- in: path
name: id
required: true
schema:
type: integer
description: Numeric ID of the event
get:
summary: Get event details
description: |
This will get the details of a specific event in the schedule.
The response will be the EVSE state to set for that event.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: ./models/EvseState.yaml
operationId: getScheduleEvent
tags:
- Schedule
post:
description: |
Update the details of a specific event in the schedule.
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
requestBody:
content:
application/json:
schema:
$ref: ./models/EvseState.yaml
description: ''
operationId: setScheduleEvent
tags:
- Schedule
summary: Update event details
delete:
description: |
Remove a specific event from the schedule.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples: {}
operationId: removeScheduleEvent
tags:
- Schedule
summary: Remove event
/schedule/plan:
get:
summary: Get planned events and state
description: This will return the planned events by day and also the current state of the scheduler
responses:
'200':
description: List of planed events
content:
application/json:
schema:
type: object
properties:
current_day:
$ref: '#/components/schemas/Day'
operationId: getSchedulePlan
tags:
- Schedule
/logs:
get:
summary: Get event block information
tags:
- Event Log
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
min:
type: integer
max:
type: integer
operationId: getEventBlockInfo
description: 'Retrieve the start and end indexes of the log event blocks. Each log event block contains '
parameters: []
parameters: []
'/logs/{index}':
get:
summary: Get log block events
tags:
- Event Log
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/LogEvent'
'404':
$ref: '#/components/responses/NotFound'
operationId: getEventBlock
description: |
Retrieve the log events for a specific block.
The block index range is returned by the /logs endpoint.
parameters:
- schema:
type: integer
name: index
in: path
required: true
/logs/export:
get:
summary: Export session history as CSV
tags:
- Event Log
responses:
'200':
description: OK
content:
text/csv:
schema:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
operationId: exportEventLogCsv
description: >-
Stream every stored log entry as a CSV file (Time, Type, State, Energy
(kWh), Elapsed (min), RFID Tag, User Name, Temperature (C)), one row
per event. The RFID Tag and User Name columns are blank for sessions
without RFID authentication.
parameters: []
/rfid/users:
get:
summary: List RFID user names
tags:
- RFID
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
additionalProperties:
type: string
description: Map of RFID tag UID to the assigned user name.
operationId: getRfidUsers
description: Retrieve the UID → user name mapping used to attribute sessions in History and the CSV export.
parameters: []
post:
summary: Set an RFID user name
tags:
- RFID
requestBody:
content:
application/json:
schema:
type: object
required:
- rfid
properties:
rfid:
type: string
name:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'400':
$ref: '#/components/responses/BadRequest'
operationId: setRfidUser
description: >-
Assign a user name to an RFID tag UID. Sending an empty name removes
the mapping.
delete:
summary: Remove an RFID user name
tags:
- RFID
parameters:
- schema:
type: string
name: rfid
in: query
required: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'400':
$ref: '#/components/responses/BadRequest'
operationId: removeRfidUser
description: Remove the user name mapping for an RFID tag UID.
parameters: []
/login:
post:
operationId: login
summary: Log in and obtain a session cookie
description: |
Checks the admin credentials and, on success, sets the `oevse_session`
cookie (`HttpOnly`, `SameSite=Strict`). Only needed for browser-style
clients; machine clients can use HTTP Basic on every request instead.
This operation is reachable without authentication. Wrong credentials count
towards the same brute-force throttle as a wrong Basic password.
tags:
- Authentication
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- user
- pass
properties:
user:
type: string
pass:
type: string
remember:
type: boolean
description: Issue a 30 day cookie instead of a 6 hour one
now:
type: integer
description: >-
Client time in epoch seconds. Only used to bootstrap the
device clock when it has none, so the cookie can be dated.
responses:
'200':
description: Logged in; `Set-Cookie` carries the session
headers:
Set-Cookie:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'400':
$ref: '#/components/responses/BadRequest'
'401':
description: Wrong username or password
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Invalid:
value:
msg: invalid
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
description: >-
A session cannot be issued yet (`clock`: the device has no valid
time and the client did not supply one; `not ready`: no signing
secret). Use HTTP Basic instead.
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
/logout:
post:
operationId: logout
summary: Clear the session cookie
description: Always succeeds and does not require authentication.
tags:
- Authentication
security: []
responses:
'200':
description: Cookie cleared
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
/restart:
post:
summary: restart gateway or evse modules
description: restart wifi gateway or openevse module
operationId: restart
tags:
- Restart
requestBody:
content:
application/json:
schema:
type: object
properties:
device:
type: string
description: gateway|evse
responses:
'200':
description: Restart successfull
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: restart gateway|evse
/limit:
get:
summary: Get charge limit
description: 'This will return the limit type, value and auto_release (true|false) defined if there''s one'
responses:
'200':
description: Limit properties
content:
application/json:
schema:
$ref: ./models/Limit.yaml
'404':
description: No limit
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: No limit
operationId: limit_get
tags:
- Limit
post:
summary: Set charge limit
description: 'This will set the limit type, value and auto_release (true|false)'
responses:
'200':
description: Limit set
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: created
'500':
description: Failed to parse data
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: Failed to parse JSON
requestBody:
content:
application/json:
schema:
$ref: ./models/Limit.yaml
operationId: limit_set
tags:
- Limit
delete:
summary: Get charge limit
description: This will clear current limit
responses:
'200':
description: Clear Limit ok
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: Deleted
'404':
description: No limit
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: No limit to clear
'500':
description: Failed to clear limit
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: Failed to clear limit
operationId: limit_clear
tags:
- Limit
/loadsharing/peers:
get:
summary: List discovered and configured peers
description: |
Return the set of peers that are discovered (mDNS) and/or configured as
members of the load sharing group.
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/LoadSharingPeer'
operationId: listLoadSharingPeers
tags:
- Load Sharing
post:
summary: Add a peer to the load sharing group
description: |
Add a new peer to the load sharing group. The peer must be specified as a
hostname or IP address (the value must contain `.` or `:`). On success the
controller performs reciprocal peer-list sync on the remote unless
`reciprocal` is false. Config push to the new member is handled separately
by the peer poller after the WebSocket connection is established.
requestBody:
content:
application/json:
schema:
type: object
required:
- host
properties:
host:
type: string
description: Hostname or IP address of the peer to add
example: openevse-2.local
reciprocal:
type: boolean
default: true
description: |
When true (default), add this device to the remote peer's group
list. Set false when handling a reciprocal sync from the remote.
responses:
'200':
description: Peer added or already in group
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Added:
value:
msg: done
Already in group:
value:
msg: already in group
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Invalid JSON:
value:
msg: Invalid JSON
Missing host:
value:
msg: Missing required 'host' parameter
Empty host:
value:
msg: Host cannot be empty
Local device:
value:
msg: Cannot add local device as peer
Invalid host format:
value:
msg: Invalid host format - must contain domain or IP
operationId: addLoadSharingPeer
tags:
- Load Sharing
'/loadsharing/peers/{host}':
put:
summary: Change the priority of a peer
description: >-
Only allowed on the group leader; members answer `403`.
parameters:
- in: path
name: host
required: true
schema:
type: string
description: Hostname or IP address of the peer
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- priority
properties:
priority:
type: integer
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'400':
$ref: '#/components/responses/BadRequest'
'403':
description: Load sharing configuration is read-only on members
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'404':
description: Peer not found
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
operationId: updateLoadSharingPeer
tags:
- Load Sharing
delete:
summary: Remove a peer from the load sharing group
description: |
Remove a peer from the load sharing group. On success the controller
performs reciprocal removal on the remote unless `reciprocal=false`.
The controller also pushes a reset config to the removed member when
applicable.
parameters:
- in: path
name: host
required: true
schema:
type: string
description: Hostname or IP address of the peer to remove
- in: query
name: reciprocal
required: false
schema:
type: boolean
default: true
description: |
When true (default), remove this device from the remote peer's group
list. Set false when handling a reciprocal sync from the remote.
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'400':
description: Cannot remove local device
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Local device:
value:
msg: Cannot remove local device from group
'404':
description: Peer not found
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Peer not found:
value:
msg: Peer not found
operationId: removeLoadSharingPeer
tags:
- Load Sharing
/loadsharing/status:
get:
summary: Get load sharing runtime status
description: |
Return runtime status for load sharing, including online/offline peers and
the most recent computed allocations.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/LoadSharingStatus'
operationId: getLoadSharingStatus
tags:
- Load Sharing
/loadsharing/discover:
post:
summary: Trigger peer discovery
description: |
Trigger an mDNS discovery scan for other OpenEVSE devices.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
operationId: discoverLoadSharingPeers
tags:
- Load Sharing
/boost:
get:
summary: Get the active boost
description: >
Returns the active boost (type, value, remaining, started), or an
empty object when no boost is active. The idle 200+{} response
doubles as the capability probe: older firmware has no clean 404 for
this path, so clients should treat any non-200-JSON response as
unsupported rather than relying on a 404.
responses:
'200':
description: Active boost, or {} when idle
content:
application/json:
schema:
type: object
properties:
type:
type: string
enum: [time, energy, soc, range]
value:
type: integer
description: >
time: seconds (NOT minutes, unlike /limit); energy: Wh
added since activation; soc: absolute percent; range:
absolute distance
remaining:
type: integer
description: Remaining amount in the dimension's unit (ceil)
started:
type: string
format: date-time
operationId: boost_get
tags:
- Boost
post:
summary: Arm (or replace) a boost
description: >
Boost charges NOW - an Active claim at priority 200 (above Divert and
the Scheduler, below Manual and Limit) - until the target is reached,
then releases so the previous controller resumes. Re-POSTing while a
boost is active replaces it with a fresh activation snapshot. Time
values clamp to 604800 seconds (7 days). Energy value is a Wh delta
from the moment of activation; soc/range values are absolute targets
and require a vehicle data source (422 otherwise). A soc/range target
that is ALREADY met arms as a no-op: 201, no claim is taken, and GET
/boost still returns {}. The MQTT /boost event topic publishes
the boolean boost + boost_version on every change; boost_reason
("reached", "cancelled" or "replaced") appears only on end events
(boost false) and never on an active one, so a replace emits the end
event first and then the fresh arm event. The retained /boost
state topic carries this same serialized object ({} when idle) - the
retained remaining is a snapshot at arm/end, not a live countdown.
responses:
'201':
description: Boost armed
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: created
'400':
description: Malformed body, unknown type, or zero value
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: Failed to parse JSON
'422':
description: soc/range boost without a vehicle data source
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: No vehicle data available
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [type, value]
properties:
type:
type: string
enum: [time, energy, soc, range]
value:
type: integer
minimum: 1
operationId: boost_set
tags:
- Boost
delete:
summary: Cancel the active boost
description: This will clear the active boost, if any
responses:
'200':
description: Boost cancelled
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: Deleted
'404':
description: No boost active
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: No boost to clear
operationId: boost_clear
tags:
- Boost
/notifications:
get:
summary: Get the advisory list
description: >
Returns every currently active advisory, acknowledged entries
included - a muted advisory is still listed, marked acked, so the GUI
can render it as muted rather than have it vanish. Advisories are
non-fault conditions the user should know about (a safety check
switched off, a fault that has already cleared, thermal state, relay
wear); anything stopping the EVSE charging right now is a fault, not
an advisory, and is reported through the state fields instead.
first_seen and last_seen are epoch seconds, or 0 when the gateway's
clock had not synced at the moment the reading was taken.
responses:
'200':
description: The advisory list
content:
application/json:
schema:
type: object
properties:
count:
type: integer
description: >
Number of UNMUTED advisories - what a badge should show.
The notifications array may be longer.
max_severity:
type: string
enum: [info, warning, critical]
description: Highest severity among the unmuted advisories
notifications:
type: array
items:
type: object
properties:
id:
type: string
description: >
Stable dotted id, e.g. safety.ground_check. The
only thing a UI keys on; titles and detail text are
looked up from it.
category:
type: string
enum: [safety, fault, wear, thermal]
severity:
type: string
enum: [info, warning, critical]
sticky:
type: boolean
description: >
true when acknowledging mutes the advisory rather
than dismissing it - it stays listed, and keeps its
settings-page marker, until the condition clears.
acked:
type: boolean
first_seen:
type: integer
description: Epoch seconds, 0 if unknown (clock not synced)
last_seen:
type: integer
description: Epoch seconds, 0 if unknown (clock not synced)
operationId: notifications_get
tags:
- Notifications
/notifications/ack:
post:
summary: Acknowledge one advisory
description: >
Acknowledges the advisory named by id. A non-sticky advisory is
dismissed until its token changes - the token is the underlying
counter, so a later GFI trip raises it again. A sticky advisory is
muted instead: it stays in the list marked acked and keeps its
settings-page marker, but stops driving the LCD's amber border and
advisory line. The acknowledgement survives a reboot; it is voided by
any movement in the token, by any EVSE settings change for safety.*
advisories, and by a firmware update.
The id may be sent either on the query string
(POST /notifications/ack?id=safety.ground_check, no body) or as an
application/x-www-form-urlencoded body.
requestBody:
required: false
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
id:
type: string
description: >
The advisory id. May instead be given as the `id` query
parameter.
parameters:
- name: id
in: query
required: false
description: >
The advisory id, e.g. safety.ground_check. Required unless the
same name is sent in a form-encoded body.
schema:
type: string
responses:
'200':
description: Acknowledged
content:
text/plain:
schema:
type: string
'400':
description: No id given, in either the query string or the body
content:
text/plain:
schema:
type: string
'403':
description: >
GET used without the X-Requested-With: OpenEVSE header, or a
cookie-authenticated request without it (CSRF guard)
content:
text/plain:
schema:
type: string
'404':
description: No advisory with that id is currently active
content:
text/plain:
schema:
type: string
operationId: notifications_ack
tags:
- Notifications
/emeter:
delete:
summary: Reset Energy Meter
description: This will clear the Energy Meter
requestBody:
content:
application/json:
schema:
type: object
properties:
hard:
type: integer
description: 0|1 Enable hard reset ( reset total_counter & total_switches)
import:
type: integer
description: 0|1 Import OpenEvse module total kwh.
responses:
'200':
description: Reset counter ok
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: Reset done
'500':
description: Failed to reset counter
content:
application/json:
schema:
type: object
properties:
msg:
type: string
description: Reset failed
operationId: energymeter-reset
tags:
- Energy Meter
/tesla/vehicles:
get:
summary: Get Tesla vehicle list
tags:
- Tesla
responses:
'200':
description: OK
content:
application/json:
schema:
description: ''
type: object
x-examples:
example-1:
count: 3
vehicles:
- id: '1492932278133304'
name: AwesomeSAUCE
- id: '1492931610165415'
name: Herbie
- id: '1492930973960452'
name: 'Life, the Universe, and Everything'
properties:
count:
type: number
vehicles:
type: array
uniqueItems: true
items:
type: object
additionalProperties: false
properties:
id:
type: string
minLength: 1
name:
type: string
minLength: 1
required:
- id
- name
required:
- count
- vehicles
examples:
example-1:
value:
count: 1
vehicles:
- id: '2238974987235987'
name: My Tesla
operationId: get-tesla-vehicles
description: Once the Tesla account credentials have been configured via the `/config` API this endpoint will return a list of vehicles associated with the account. The ID of the vehicle to use for SoC/Range can then be set using `tesla_vehicle_id` on the `/config` endpoint.
/time:
get:
summary: Get the time
tags:
- Time
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
time:
type: string
x-stoplight:
id: 6q0laeyxt5u85
description: The time in UTC according to the OpenEVSE.
format: date-time
offset:
type: string
x-stoplight:
id: u5zngl88caktf
pattern: '^[+-][0-9]{4}$'
local_time:
type: string
x-stoplight:
id: 7h1cpgfjj5j9j
format: date-time
uptime:
type: integer
description: EVSE gateway uptime, in seconds
required:
- time
- offset
- local_time
- uptime
operationId: get-time
description: Gets the time set on the OpenEVSE
post:
summary: Set the time
tags:
- Time
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
x-examples:
Example 1:
sntp_enabled: false
time_zone: 'GMT0BST,M3.5.0/1,M10.5.0'
time: '2018-08-10T14:30:00Z'
properties:
sntp_enabled:
type: boolean
description: |
Set to `true` to enable receiving time updates via NTP, `false` will disable NTP updates.
default: false
time_zone:
type: string
description: The time zone to use on the OpenEVSE. This has the format `|`. The `location name` is not used by the EVSE and is largely to for the UI to provide a human readable version of the timezone and differentiate between locations that use the same time zone.
default: UTC0
time:
type: string
description: If `sntp_enabled` is false this is the time to set. Must be in UTC.
format: date-time
'400':
$ref: '#/components/responses/BadRequest'
operationId: post-time
description: Set the time and associated config options. If not setting the time the `/config` endpoint can be used.
/certificates:
get:
summary: Get a list of certificates
description: |
Returns a list of certificates that have been uploaded to the OpenEVSE. The certificates are used to
authenticate the OpenEVSE to the MQTT broker, provide a secure web interface or as additional root CA
certificates.
operationId: listCertificates
tags:
- Certificates
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
certificates:
type: array
items:
$ref: './models/Certificate.yaml'
post:
summary: Upload a certificate
description: |
Upload a certificate to the OpenEVSE. The certificate can be used to authenticate the OpenEVSE to the MQTT
broker, provide a secure web interface or as additional root CA certificates.
operationId: uploadCertificate
tags:
- Certificates
requestBody:
content:
application/json:
schema:
$ref: './models/Certificate.yaml'
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'400':
$ref: '#/components/responses/BadRequest'
'/certificates/{id}':
get:
summary: Get a certificate
description: |
Returns a certificate that has been uploaded to the OpenEVSE. The certificate is used to
authenticate the OpenEVSE to the MQTT broker, provide a secure web interface or as additional root CA
certificates.
operationId: getCertificate
tags:
- Certificates
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: './models/Certificate.yaml'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
delete:
summary: Delete a certificate
description: |
Delete a certificate that has been uploaded to the OpenEVSE. The certificate is used to
authenticate the OpenEVSE to the MQTT broker, provide a secure web interface or as additional root CA
certificates.
operationId: deleteCertificate
tags:
- Certificates
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
description: Certificate removal failed because storage or trust-bundle preparation failed.
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
example:
msg: Could not remove certificate
/reset:
get:
operationId: factoryReset
summary: Factory reset and reboot the gateway
description: |
Erases the stored configuration and reboots the WiFi module. Also accepts
`POST`. A `GET` must send `X-Requested-With: OpenEVSE`, see
[Authentication](#section/Authentication).
tags:
- System
responses:
'200':
description: Reset accepted, the device is rebooting
content:
text/plain:
schema:
type: string
example: '1'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
/apoff:
get:
operationId: accessPointOff
summary: Turn the WiFi access point off
description: |
Switches off the access point after one second. Also accepts `POST`. A
`GET` must send `X-Requested-With: OpenEVSE`.
tags:
- Network
responses:
'200':
description: OK
content:
text/plain:
schema:
type: string
example: Turning AP Off
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
/scan:
get:
operationId: scanWifi
summary: Scan for WiFi networks
description: >-
Starts a scan and returns the networks found. Returns an empty array if
the scan could not be started.
tags:
- Network
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
properties:
rssi:
type: integer
description: Signal strength in dBm
ssid:
type: string
bssid:
type: string
channel:
type: integer
secure:
type: integer
description: Encryption type, `0` for an open network
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/divertmode:
get:
operationId: setDivertMode
summary: Set the solar divert mode
description: |
Changes the divert mode. Also accepts `POST`. A `GET` must send
`X-Requested-With: OpenEVSE`.
tags:
- Status
parameters:
- in: query
name: divertmode
required: true
schema:
type: integer
enum:
- 1
- 2
description: '`1` Normal (default), `2` Eco'
responses:
'200':
description: OK
content:
text/plain:
schema:
type: string
example: Divert Mode changed
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
/shaper:
get:
operationId: setCurrentShaper
summary: Enable or disable the current shaper
description: |
Also accepts `POST`. A `GET` must send `X-Requested-With: OpenEVSE`.
tags:
- Status
parameters:
- in: query
name: shaper
required: true
schema:
type: integer
enum:
- 0
- 1
description: '`1` enables the shaper, any other value disables it'
responses:
'200':
description: OK
content:
text/plain:
schema:
type: string
example: Current Shaper state changed
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
/rfid/add:
get:
operationId: rfidAddTag
summary: Wait for an RFID badge to be presented
description: |
Puts the RFID reader into learn mode; the tag id is reported over the
websocket once a badge is scanned. Also accepts `POST`. A `GET` must send
`X-Requested-With: OpenEVSE`.
tags:
- RFID
responses:
'200':
description: Waiting for a badge
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Waiting:
value:
msg: Waiting for badge
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
/settime:
post:
operationId: setTimeLegacy
summary: Set the time (legacy alias of POST /time)
description: |
Same as `POST /time`. Accepts either a JSON body or a
form-encoded body (`tz`, `ntp`, `time`). Kept for older clients; new
code should use [`/time`](#tag/Time).
tags:
- Time
responses:
'200':
description: OK
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
/teslaveh:
get:
operationId: getTeslaVehiclesLegacy
summary: List Tesla vehicles (legacy alias of /tesla/vehicles)
description: Returns the same data as [`/tesla/vehicles`](#operation/get-tesla-vehicles).
tags:
- Tesla
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
count:
type: integer
vehicles:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/rapi:
get:
operationId: rapi
summary: Send a RAPI command to the EVSE controller
description: |
Sends an arbitrary RAPI command and returns the controller's reply. `/r`
is an alias. Without the `rapi` parameter it returns an HTML test page
(or an empty body when `json=1`). Because a RAPI command can change
anything on the controller, a `GET` with `rapi` must send
`X-Requested-With: OpenEVSE`; `POST` is also accepted.
Some commands are blocked by the firmware.
tags:
- RAPI
parameters:
- in: query
name: rapi
required: false
schema:
type: string
example: $GS
description: The RAPI command, including the leading `$`
- in: query
name: json
required: false
schema:
type: integer
enum:
- 0
- 1
description: '`1` for a JSON reply rather than an HTML one'
responses:
'200':
description: >-
The controller answered (`$OK` or `$NK`). With `json=1` the body is
`{"cmd": ..., "ret": ...}` where `ret` is the raw reply.
content:
application/json:
schema:
type: object
properties:
cmd:
type: string
ret:
type: string
text/html:
schema:
type: string
'400':
description: >-
The command is blocked by the firmware. With `json=1` the body is
`{"cmd": ..., "error": ...}`.
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: >-
The command failed (timeout, queue full, ...). With `json=1` the body is
`{"cmd": ..., "error": ...}`.
/emoncms/describe:
get:
operationId: emoncmsDescribe
summary: Device discovery for emoncms
description: >-
Returns the string `openevse` so the device can be found by
[emoncms/find](https://github.com/emoncms/find). This is the only operation
that never requires authentication and it allows any CORS origin.
tags:
- System
security: []
responses:
'200':
description: OK
content:
text/plain:
schema:
type: string
example: openevse
/mqtt:
get:
operationId: getMqttStatus
summary: Get the MQTT connection status
description: Connection state of the MQTT client, including the last error.
tags:
- MQTT
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
mqtt_connected:
type: integer
enum:
- 0
- 1
mqtt_status:
type: string
mqtt_broker_ip:
type: string
description: Only present once known
mqtt_broker_version:
type: string
description: Only present once known
mqtt_connected_since:
type: integer
description: Epoch seconds, only present while connected
mqtt_last_rx:
type: integer
description: Epoch seconds of the last MQTT broker traffic, sent or received
mqtt_error:
type: string
description: Error category, empty when there is no failure
mqtt_error_detail:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
post:
operationId: restartMqtt
summary: Restart the MQTT connection
description: Drops and re-establishes the connection to the broker.
tags:
- MQTT
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
/relay/reset:
get:
operationId: resetRelayHealth
summary: Reset the relay contact-life estimate
description: |
Use after a physical relay replacement so wear from the old relay is not
carried over. Requires the controller's relay health feature. Also
accepts `POST`. A `GET` must send `X-Requested-With: OpenEVSE`.
tags:
- Relay
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The controller rejected the command
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Error:
value:
msg: error
/relay/recovery:
get:
operationId: runRelayRecovery
summary: Run the stuck-relay recovery cycle
description: |
Requires controller firmware 9.3.0+. The controller rejects the command
if an EV is connected. The response is deferred until the cycle finishes,
which can take up to about 30 seconds. Also accepts `POST`. A `GET` must
send `X-Requested-With: OpenEVSE`.
tags:
- Relay
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The controller rejected the command, for example an EV is connected
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Error:
value:
msg: error
/cabletemp:
get:
operationId: getCableTemperatures
summary: Get the cable thermistor readings and configuration
description: |
Four logical sources: `ev1`/`ev2` on the EV cable and `in1`/`in2` on the
input cable. Requires controller firmware 9.4.0+. The live readings are
also included in `/status`.
tags:
- Cable Temperature
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
supported:
type: boolean
enabled:
type: boolean
sources:
type: array
items:
type: object
properties:
source:
type: integer
minimum: 0
maximum: 3
name:
type: string
enum:
- ev1
- ev2
- in1
- in2
pin:
type: integer
minimum: 0
maximum: 2
status:
type: integer
description: '`0` ok, `1` not installed or unassigned, `2` open circuit, `3` shorted'
temperature:
type: number
description: Omitted when there is no reading, so it is never confused with 0 C
r25:
type: integer
beta:
type: integer
offset_c10:
type: integer
panic_c10:
type: integer
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
post:
operationId: setCableTemperatureSource
summary: Assign a pin and optionally set the thermistor parameters
description: |
Omitting the calibration fields reassigns the pin only. `r25`, `beta`,
`offset_c10` and `panic_c10` must be supplied together. Ranges are
enforced by the controller. Assigning a source to pin `1` (PP_READ) makes
the controller turn PP auto-ampacity off.
tags:
- Cable Temperature
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- source
- pin
properties:
source:
type: integer
minimum: 0
maximum: 3
pin:
type: integer
minimum: 0
maximum: 2
r25:
type: integer
beta:
type: integer
offset_c10:
type: integer
panic_c10:
type: integer
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The controller rejected the values
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
/claims/target:
get:
operationId: getClaimsTarget
summary: Get the resolved target state
description: >-
The properties that are currently in effect after all
[claims](#tag/Claims) have been resolved, and which client holds each one.
tags:
- Claims
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
properties:
type: object
description: The effective properties
claims:
type: object
description: The client id that owns each of `state`, `charge_current` and `max_current`
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/energy/raw:
get:
operationId: getEnergyRaw
summary: Raw energy log samples
description: |
Recent samples at the logging interval. Builds with the time series
database return the last three hours, wrapped as `{"samples": [...]}`;
`before` pages back one window at a time.
tags:
- Energy
parameters:
- in: query
name: max
schema:
type: integer
description: Return at most this many samples
- in: query
name: before
schema:
type: integer
description: Epoch seconds. Return the window immediately before this time
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
samples:
type: array
items:
type: object
properties:
ts:
type: integer
description: Epoch seconds
a:
type: number
description: Current (A)
t:
type: number
description: Temperature (C)
e:
type: number
description: Session energy
s:
type: integer
description: State of charge, `-1` when unknown
v:
type: number
description: Voltage (V), time series builds only
p:
type: number
description: Power (W), time series builds only
pilot:
type: number
description: Pilot current (A), time series builds only
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/energy/daily:
get:
operationId: getEnergyDaily
summary: Daily energy totals
description: |
Time series builds take `days` (1-365, default 30). Other builds take `year` and
`quarter` (`1`-`4`).
tags:
- Energy
parameters:
- in: query
name: days
schema:
type: integer
minimum: 1
maximum: 365
default: 30
- in: query
name: year
schema:
type: integer
minimum: 2020
maximum: 2100
- in: query
name: quarter
schema:
type: integer
minimum: 1
maximum: 4
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
daily:
type: array
items:
$ref: '#/components/schemas/EnergyBucketDate'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/energy/weekly:
get:
operationId: getEnergyWeekly
summary: Weekly energy totals
description: Only available in builds with the time series database.
tags:
- Energy
parameters:
- in: query
name: weeks
schema:
type: integer
minimum: 1
maximum: 104
default: 12
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
weekly:
type: array
items:
$ref: '#/components/schemas/EnergyBucketDate'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/energy/monthly:
get:
operationId: getEnergyMonthly
summary: Monthly energy totals
description: Totals per month for one year, defaulting to the current year.
tags:
- Energy
parameters:
- in: query
name: year
schema:
type: integer
minimum: 2020
maximum: 2100
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
monthly:
type: array
items:
type: object
properties:
mo:
type: string
description: Month
pk:
type: number
description: Peak temperature (C)
mn:
type: number
description: Minimum temperature (C)
en:
type: number
description: Energy (kWh)
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/energy/annual:
get:
operationId: getEnergyAnnual
summary: Annual energy totals
description: Totals per year.
tags:
- Energy
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
annual:
type: array
items:
type: object
properties:
yr:
type: integer
pk:
type: number
description: Peak temperature (C)
mn:
type: number
description: Minimum temperature (C)
en:
type: number
description: Energy (kWh)
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/update:
post:
operationId: updateFirmware
summary: Update the firmware
description: |
Either upload the firmware image as a multipart form, or send a JSON body
naming a URL for the device to fetch it from (answered with
`{"msg": "started"}`; progress is reported over `/ws`). `GET` is not
supported and returns `405`.
tags:
- Firmware Update
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
firmware:
type: string
format: binary
description: The firmware image; the part name is not significant
application/json:
schema:
type: object
properties:
url:
type: string
description: Location of the firmware image to fetch
responses:
'200':
$ref: '#/components/responses/UpdateSuccessful'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'405':
description: Only `POST` is accepted
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The update failed
/migrate/status:
get:
operationId: getMigrateStatus
summary: Flash migration progress and diagnostics
description: >-
State of an in-place 16MB repartition. Progress is also pushed over `/ws`.
On builds without migration support it returns `{"active": false}`.
tags:
- Diagnostics
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
active:
type: boolean
state:
type: string
enum:
- idle
- manifest
- app
- migrator
conn_open:
type: boolean
awaiting:
type: boolean
body_complete:
type: boolean
has_redirect:
type: boolean
has_pending:
type: boolean
dl_pos:
type: integer
dl_total:
type: integer
app_off:
type: integer
failed:
type: boolean
fail_code:
type: integer
fail_where:
type: string
free_heap:
type: integer
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/migrate/coredump:
get:
operationId: getMigrateCoredump
summary: Summary of the last panic, as seen by the migrator
description: Decoded summary of the last panic, read by the flash migration code.
tags:
- Diagnostics
responses:
'200':
description: >-
`task`, `pc`, `bt` and `corrupted` when a core dump exists, otherwise
`coredump` set to `none` (or `n/a` on unsupported builds).
content:
application/json:
schema:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/migrate/expand16mb:
post:
operationId: expandTo16mb
summary: Repartition a 16MB module that was flashed with the 4MB layout
description: Starts the migration; progress is available from `/migrate/status` and `/ws`.
tags:
- Diagnostics
requestBody:
content:
application/json:
schema:
type: object
properties:
url:
type: string
description: Release manifest to use instead of the default
dry_run:
type: boolean
default: false
responses:
'200':
description: Migration started
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Started:
value:
msg: started
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'405':
description: Only `POST` is accepted
'409':
description: A migration is already in progress
'412':
description: This device is not eligible for the migration
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The migration could not be started
/debug:
get:
operationId: getDebugLog
summary: Buffered debug log
description: The recent debug output of the WiFi module. Allows any CORS origin.
tags:
- Diagnostics
responses:
'200':
description: OK
content:
text/plain:
schema:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/debug/console:
get:
operationId: debugConsole
summary: Live debug console
description: >-
WebSocket that streams the debug output as text frames. The upgrade request
is authenticated like any other.
tags:
- Diagnostics
responses:
'101':
description: Switching protocols
'401':
$ref: '#/components/responses/Unauthorized'
/debug/crash:
get:
operationId: getCrashSummary
summary: Decoded summary of the last panic
description: >-
A core dump survives a reboot in flash. `present` is `false` when there is
none. The remaining keys depend on the CPU architecture.
tags:
- Diagnostics
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
present:
type: boolean
size:
type: integer
addr:
type: integer
valid:
type: boolean
task:
type: string
pc:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
delete:
operationId: clearCrashSummary
summary: Clear the stored core dump
description: Clears the stored core dump so the next panic is unambiguous.
tags:
- Diagnostics
responses:
'200':
description: Erased
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Erased:
value:
msg: erased
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The core dump could not be erased
/debug/crash/raw:
get:
operationId: getCrashImage
summary: Download the raw core dump
description: >-
For use with `esp-coredump`. Unlike other operations this one always
answers an unauthenticated request with a `WWW-Authenticate` Basic
challenge (never the JSON `401`), and is not subject to the throttle.
tags:
- Diagnostics
responses:
'200':
description: OK
content:
application/octet-stream:
schema:
type: string
format: binary
'401':
description: Credentials are required, answered with a `WWW-Authenticate` challenge
'404':
description: There is no core dump
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
/debug/crash/report:
get:
operationId: getCrashReport
summary: The crash report, in the form the broker accepts
description: |
Part of the crash report upload: the browser fetches this and POSTs it to
the broker itself, so the charger needs no internet access. Only present
in builds with crash upload enabled. The report carries the reporter id
and a hash of the delete key, so a reporter identity must exist first,
see `/debug/crash/identity`.
tags:
- Diagnostics
responses:
'200':
description: The report, exactly as the broker takes it
content:
application/json:
schema:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
'404':
description: There is no stored crash dump
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'409':
description: No reporter identity has been stored yet
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The report is too large to build
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
/debug/crash/identity:
get:
operationId: getCrashIdentity
summary: The reporter identity and broker URL
description: |
Only present in builds with crash upload enabled. Every method on this
route is for the web app only: it must send `X-Requested-With: OpenEVSE`
even with no password set, because the delete key is what erases this
charger's reports. The delete key is only returned when `key=1` is
requested.
tags:
- Diagnostics
parameters:
- in: query
name: key
required: false
schema:
type: integer
enum:
- 1
description: Also return the delete key
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
reporter_id:
type: string
nullable: true
description: Null when no identity has been stored
delete_key:
type: string
description: Only present when `key=1` was requested
broker:
type: string
description: URL of the crash report broker
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
post:
operationId: storeCrashIdentity
summary: Store the reporter identity generated by the browser
description: Only present in builds with crash upload enabled.
tags:
- Diagnostics
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- reporter_id
- delete_key
properties:
reporter_id:
type: string
delete_key:
type: string
responses:
'200':
description: Stored, or the same identity was already stored
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Stored:
value:
msg: stored
'400':
description: Invalid JSON, reporter id or delete key
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'409':
description: A different identity is already stored
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The identity could not be stored
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
delete:
operationId: forgetCrashIdentity
summary: Forget the reporter identity
description: >-
Only present in builds with crash upload enabled. Call this once the
reports have been erased at the broker.
tags:
- Diagnostics
responses:
'200':
description: Forgotten
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Forgotten:
value:
msg: forgotten
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: The identity could not be removed
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
/evse:
get:
operationId: getEvseSerialLog
summary: Buffered serial traffic with the EVSE controller
description: Allows any CORS origin.
tags:
- Diagnostics
responses:
'200':
description: OK
content:
text/plain:
schema:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
/evse/console:
get:
operationId: evseConsole
summary: Live EVSE serial console
description: >-
WebSocket that streams the serial traffic to and from the EVSE controller
as text frames. The upgrade request is authenticated like any other.
tags:
- Diagnostics
responses:
'101':
description: Switching protocols
'401':
$ref: '#/components/responses/Unauthorized'
/installer/verify:
post:
description: |
Check the installer password. Requires `X-Requested-With: OpenEVSE` and a
JSON body. Wrong passwords are throttled; a locked-out client gets 429 with
`retry_after` seconds.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [password]
properties:
password:
type: string
responses:
'200':
description: Password accepted
content:
application/json:
schema:
type: object
properties:
msg:
type: string
example: ok
'400':
description: Body is not valid JSON
'403':
description: Missing CSRF header, or wrong password
'405':
description: Not a POST request
'429':
description: Locked out after too many failures
operationId: verifyInstaller
tags:
- Installer
summary: Verify the installer password
/installer/password:
post:
description: |
Change the installer password. Requires the current password. The new
password must be 8 to 32 printable ASCII characters.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [current, new]
properties:
current:
type: string
new:
type: string
responses:
'200':
description: Password changed
content:
application/json:
schema:
type: object
properties:
msg:
type: string
example: changed
'400':
description: New password is missing its length or characters
'403':
description: Missing CSRF header, or wrong current password
'429':
description: Locked out after too many failures
'500':
description: Password could not be stored
operationId: changeInstallerPassword
tags:
- Installer
summary: Change the installer password
/installer/maxcurrent:
post:
description: |
Set the hardware maximum current. The controller accepts this only once,
so the reply confirms the command was sent; re-read `/config` to see whether
`max_current_hard` took the new value. Amps must lie between the minimum
current and the lower of the hardware maximum and the installer limit, and
can only come down in practice.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [password, amps]
properties:
password:
type: string
amps:
type: integer
responses:
'200':
description: Command sent to the controller
content:
application/json:
schema:
type: object
properties:
msg:
type: string
example: sent
requested:
type: integer
previous:
type: integer
'400':
description: Amps out of range (the body gives the allowed min and max), or bad JSON
'403':
description: Missing CSRF header, or wrong password
'429':
description: Locked out after too many failures
operationId: setInstallerMaxCurrent
tags:
- Installer
summary: Set the hardware maximum current
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic
description: >-
Admin username and password, sent on every request. Over plain HTTP they
are not encrypted; use HTTPS to protect them in transit.
SessionCookie:
type: apiKey
in: cookie
name: oevse_session
description: >-
Session cookie issued by `POST /login`. State-changing requests that use
it must also send `X-Requested-With: OpenEVSE`.
schemas:
EnergyBucketDate:
type: object
properties:
dt:
type: string
format: date
description: First day of the bucket
pk:
type: number
description: Peak temperature (C)
mn:
type: number
description: Minimum temperature (C)
en:
type: number
description: Energy (Wh)
Message:
title: Informational message
type: object
properties:
msg:
type: string
description: The message
Day:
type: string
enum:
- monday
- tuesday
- wednesday
- thursday
- friday
- saturday
- sunday
x-examples: {}
ScheduleEvent:
type: object
properties:
id:
type: integer
example: 1
state:
$ref: ./models/EvseState.yaml
time:
type: string
format: time
days:
type: array
uniqueItems: true
items:
$ref: '#/components/schemas/Day'
required:
- state
- time
- days
title: ''
LogEvent:
title: LogEvent
type: object
x-examples:
example-1:
value:
time: '2021-08-24T14:15:22Z'
type: information
managerState: active
evseState: 1
evseFlags: 123
pilotState: 2
changed:
- pilot
pilot: 32
energy: 1234.5
elapsed: 123456
temperature: 35.4
tempuratureMax: 50.4
divertMode: 1
properties:
time:
type: string
format: date-time
readOnly: true
type:
type: string
enum:
- information
- notification
- warning
managerState:
$ref: ./models/EvseState.yaml
evseState:
type: integer
minimum: 0
maximum: 255
evseFlags:
type: integer
format: int32
pilotState:
description: >-
J1772 pilot state at the time of the event. 255 for entries written
before this field was recorded.
type: integer
minimum: 0
maximum: 255
changed:
description: >-
Which fields differ from the previous entry, i.e. why this entry
exists. "flags" covers only the meaningful evseFlags bits: the
session-timing and previous-EV-connected bookkeeping bits are
recorded in evseFlags but never create an entry of their own. "boot" means there was no previous entry to compare
against; "periodic" means nothing visible changed and the entry is
the periodic sample of an unchanging state; "notification" means
the advisory id differs from the previous entry's. Empty for
entries written before this field was recorded. The pilot state is
never listed: it is recorded, but a change in it alone does not
create an entry.
type: array
items:
type: string
enum:
- boot
- type
- manager
- state
- flags
- pilot
- divert
- shaper
- periodic
- notification
pilot:
type: integer
format: int32
minimum: 0
energy:
type: number
format: double
minimum: 0
elapsed:
type: integer
format: int32
minimum: 0
temperature:
type: number
format: double
tempuratureMax:
type: number
format: double
divertMode:
type: integer
format: int32
minimum: 1
maximum: 2
exclusiveMaximum: false
rfidTag:
description: >-
RFID tag UID authenticated for this session, when RFID was used.
Absent for entries with no RFID authentication or written before
this field was recorded.
type: string
notification:
description: >-
For a "notification" entry, the advisory id that was raised (e.g.
safety.ground_check). Empty for every other entry type and for
entries written before this field existed. A row is written on the
raise edge only, at most once per advisory id per boot.
type: string
LoadSharingPeerStatus:
type: object
description: Minimal peer status snapshot used for load sharing decisions.
properties:
amp:
type: number
format: double
description: Measured current (amps).
voltage:
type: number
format: double
description: Measured voltage (volts).
pilot:
type: number
format: double
description: Current pilot setpoint (amps).
vehicle:
type: integer
description: 1 if vehicle connected, else 0.
state:
type: integer
description: EVSE state code.
min_current:
type: number
format: double
description: EVSE minimum current (amps).
max_current:
type: number
format: double
description: EVSE configured maximum current (amps).
priority:
type: integer
description: Node-local load sharing priority; lower values win under scarcity.
LoadSharingPeer:
type: object
description: A discovered or configured peer.
properties:
id:
type: string
description: Stable device id.
name:
type: string
description: mDNS instance/hostname if available.
host:
type: string
description: Hostname or IP address used to reach the peer.
ip:
type: string
description: Current resolved IP address.
version:
type: string
description: Firmware version if available.
online:
type: boolean
joined:
type: boolean
description: True if this peer is joined to the load sharing group (manually configured).
last_seen:
type: integer
description: Seconds since boot when the peer was last seen (millis/1000).
status:
$ref: '#/components/schemas/LoadSharingPeerStatus'
LoadSharingAllocation:
type: object
description: Per-member current allocation result.
properties:
id:
type: string
description: Member id.
target_current:
type: number
format: double
minimum: 0
description: Allocated current (amps).
reason:
type: string
enum:
- idle
- equal_share
- connected_min
- min_subset
- insufficient
- offline
- failsafe_disabled
description: Why this allocation was computed.
required:
- target_current
LoadSharingStatus:
type: object
description: Runtime status for local load sharing.
properties:
enabled:
type: boolean
group_id:
type: string
computed_at:
type: string
format: date-time
failsafe_active:
type: boolean
online_count:
type: integer
offline_count:
type: integer
peers:
type: array
items:
$ref: '#/components/schemas/LoadSharingPeer'
allocations:
type: array
items:
$ref: '#/components/schemas/LoadSharingAllocation'
responses:
UpdateSuccessful:
description: Update successful
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Success message:
value:
msg: done
NotFound:
description: Example response
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Not found:
value:
msg: Not found
Unauthorized:
description: >-
Authentication is required and no valid credentials were supplied.
Requests with `X-Requested-With: OpenEVSE` get a JSON body; others get a
`WWW-Authenticate` Basic challenge.
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Auth required:
value:
msg: auth
TooManyRequests:
description: >-
A wrong password was presented while the brute-force throttle is active.
Correct credentials are never throttled.
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Locked:
value:
msg: locked
Forbidden:
description: >-
Rejected by the CSRF guard. A state-changing request authenticated by
session cookie, or a `GET` to an operation that changes state, is missing
`X-Requested-With: OpenEVSE`. Session-cookie requests get the JSON body;
`GET` actuators get plain text.
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
CSRF:
value:
msg: csrf
text/plain:
schema:
type: string
example: 'Forbidden: use POST or the app'
BadRequest:
description: Example response
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
examples:
Bad JSON:
value:
msg: Could not parse JSON
parameters: {}
tags:
- name: Authentication
- name: Claims
- name: Config
- name: Event Log
- name: Manual Override
- name: Schedule
- name: Status
- name: Tesla
- name: Limit
- name: Boost
- name: Notifications
- name: Energy Meter
- name: Time
- name: Restart
- name: Certificates
- name: Load Sharing
- name: RFID
- name: Cable Temperature
- name: Diagnostics
- name: Energy
- name: Firmware Update
- name: MQTT
- name: Network
- name: RAPI
- name: Relay
- name: System
- name: Installer