Node Meter Reading List Request
The NodeMeterReadingListRequest retrieves the historical meter readings of a single node over a specified time range. For each sampled timestamp, the response returns the total charged and discharged energy at that point, together with the schedule that was applied and the control state of the node at that moment.
Typical use cases include billing, energy reporting, charts of charge/discharge behaviour, and verifying that a specific schedule was in effect during a given time window.
Preparations
- Launch the development client as described in the FlexConnect API — First Steps.
- Navigate to
http://localhost:8080/ui. - Select the documentation for Node Meter Reading List Request in the left pane.
- Have the identifier of the node ready — use
NodeStateListRequestto discover it if needed.
One node per request
Unlike NodeStateListRequest, this request operates on a single node: filter.id is one identifier, not a list. Repeat the request — or iterate over your nodes — to retrieve readings for multiple nodes.
Request
Run the NodeMeterReadingListRequest using the JSON body below.
- Run the request on
app/v1/NodeMeterReadingListRequest. - Replace the
stringValueinfilter.idwith the identifier of the node you want to query. - Set
startTimeandendTimeto the time range you want to query, in seconds since epoch.startTimeis inclusive,endTimeis exclusive. - Set
pagination.limitto the maximum number of meter-reading samples you want returned in a single response.
Request body
{
"header": {
"reqId": 1234,
"timeoutMs": 10000
},
"pagination": {
"limit": 100
},
"filter": {
"id": {
"type": { "id": 2 },
"stringValue": "54F2664E28EDF2593B9CAAAF041687BEDB0A15C8"
}
},
"startTime": 1779541075,
"endTime": 1779541675
}Request fields
| Field | Description |
|---|---|
header.reqId | Client-defined identifier for the request. The same value is returned in the response header to correlate requests and responses. |
header.timeoutMs | Maximum time (in milliseconds) the platform waits for the result before responding with a timeout error. |
pagination.limit | Maximum number of meter-reading samples returned in a single response. |
pagination.cursorValue | Cursor value returned in a previous response as cursorNext or cursorPrevious. Use it to iterate through large result sets. |
filter.id | Identifier of the single node to query. The identifier type used here determines the type used in the response. |
startTime | Start of the time range to query, in seconds since epoch, inclusive. |
endTime | End of the time range to query, in seconds since epoch, exclusive. |
Response
Response body
{
"header": {
"reqId": 1234
},
"pagination": {
"cursorNext": "1wsf="
},
"nodeMeterReadings": {
"id": {
"type": { "id": 2 },
"stringValue": "54F2664E28EDF2593B9CAAAF041687BEDB0A15C8"
},
"timestamp": [1779541075, 1779541135, 1779541195],
"positiveEnergy": [1000.0, 1001.0, 1002.0],
"negativeEnergy": [2000.0, 2001.0, 2002.0],
"scheduleIds": [582, 582, 583],
"controlStates": [3, 3, 3]
}
}Index-aligned arrays
All arrays in nodeMeterReadings are aligned by index: the value at index i of positiveEnergy, negativeEnergy, scheduleIds, and controlStates belongs to the timestamp at index i of timestamp. In the example above, index 0 describes the sample at 1779541075 — 1000.0 kWh charged, 2000.0 kWh discharged, under schedule 582 in control state POWER (3).
Response fields
| Field | Description |
|---|---|
nodeMeterReadings.id | Identifier of the node. The identifier type matches the type used in filter.id of the request. |
nodeMeterReadings.timestamp | Ordered timestamps of the meter readings, in seconds since epoch. |
nodeMeterReadings.positiveEnergy | Total energy imported by the battery (charged) at the corresponding timestamp, in kWh. This is a running meter total — it monotonically increases over time, never resets. Subtract values from two timestamps to get the energy imported between them. |
nodeMeterReadings.negativeEnergy | Total energy exported by the battery (discharged) at the corresponding timestamp, in kWh. Like positiveEnergy, this is a monotonically increasing meter total. |
nodeMeterReadings.scheduleIds | Identifier of the schedule that was applied at the corresponding timestamp. See scheduleIds special values. |
nodeMeterReadings.controlStates | Control state of the node at the corresponding timestamp. Values match the controlState table on the Power Schedules page, with 1 (IDLE) and 2 (FAILSAFE) also possible here. |
pagination.cursorNext / cursorPrevious | Returned when the result set is larger than limit. Pass either value as pagination.cursorValue in a follow-up request to paginate. |
scheduleIds special values
| Value | Description |
|---|---|
> 0 | The identifier of the power schedule that was applied at the corresponding timestamp. |
0 | No schedule was applied (the node was running its firmware default). |
-1 | The system could not apply a schedule due to a failure. |
Computing energy in a time window
Because positiveEnergy and negativeEnergy are cumulative meter totals, the energy imported or exported between two samples is the difference of the two values:
energyCharged = positiveEnergy[j] - positiveEnergy[i](kWh imported between sampleiand samplej, withi < j).energyDischarged = negativeEnergy[j] - negativeEnergy[i](kWh exported between sampleiand samplej).netEnergy = energyCharged - energyDischarged(positive = net charge, negative = net discharge).
To get the energy across the entire response, subtract the first sample's values from the last sample's values. To get energy across the full requested range when paginating, hold on to the cumulative totals across pages.
Pagination
When the requested time range contains more samples than the pagination.limit, the response includes a pagination.cursorNext value (and, when iterating, a cursorPrevious value).
To retrieve the next page, repeat the request — keeping filter.id, startTime, and endTime unchanged — and pass the received cursorNext value as pagination.cursorValue. Iteration ends when no further cursor is returned.
{
"header": {
"reqId": 1235,
"timeoutMs": 10000
},
"pagination": {
"limit": 100,
"cursorValue": "1wsf="
},
"filter": {
"id": {
"type": { "id": 2 },
"stringValue": "54F2664E28EDF2593B9CAAAF041687BEDB0A15C8"
}
},
"startTime": 1779541075,
"endTime": 1779541675
}Keep the request parameters stable across pages
When paginating, the filter.id, startTime, and endTime of every page must match the original request. Only the pagination.cursorValue changes.