Power & battery¶
This guide covers the four power-side jobs: read the battery, make the percentage tell the truth on your hardware, blank the panel without shutting anything down, and deep-sleep the whole board for a while.
Check the battery¶
Everything battery-related is in the device state:
The three battery keys in the response:
They build on each other:
batteryPinMillivolts- what the ESP32 measured at its own pin, after the board's resistor divider has already dropped the voltage. This is not your battery voltage.batteryVoltage- the reconstructed cell voltage:batteryPinMillivolts / 1000 × batteryDividerRatio.batteryPercent- state of charge, looked up frombatteryVoltageon a Li-Ion discharge curve.
All three refresh every 2 seconds, and all three are present only when pinBattery is >= 0.
The ESP32 default is GPIO 34, so a board on the default wiring has them. Set pinBattery: -1 (an AWTRIX 2
mainboard, for example) and the keys disappear from the response entirely - they are not reported
as 0 or null. The built-in Battery app also drops out of the rotation on such a board.
Full per-field table: Device state → Battery fields.
How the percentage is derived¶
There is no fuel gauge in the hardware. The percentage is inferred from batteryVoltage alone,
read off a resting Li-Ion discharge curve:
| Cell voltage | Reported |
|---|---|
| ≥ 4.20 V | 100 % |
| 3.84 V | 50 % |
| 3.73 V | 20 % |
| ≤ 3.27 V | 0 % |
Voltages between the listed points are interpolated; anything past either end stops at 100 % or 0 % rather than running off the scale.
Two consequences:
- The percentage sags under load and recovers. The curve describes a resting cell, and the panel is a bursty load.
- You cannot get a runtime estimate from it. Time-to-empty needs a fuel gauge, and this hardware has none.
Between 90 % and 20 % the cell only moves about 0.4 V, so a small error in the voltage moves the percentage a long way. That is what the next section is for.
Calibrate the divider ratio¶
If batteryVoltage looks wrong, do not try to patch the percentage - fix the ratio it is
derived from. batteryDividerRatio is V_cell / V_pin for your board's resistor divider.
You do not need a meter. A full Li-Ion cell rests at ~4.2 V, and that is the one voltage you know without an instrument.
Step 1 - charge AWTRIX fully, then read the pin side:
Step 2 - compute ratio = 4.2 / (batteryPinMillivolts / 1000).
Step 3 - write it:
curl -X PUT http://<awtrix-ip>/api/v1/system \
-H "Content-Type: application/json" \
-d '{"batteryDividerRatio":1.79}'
It applies live on the next 2-second sensor tick - no reboot.
The default is 1.79, calibrated for the Ulanzi TC001. If yours is a stock TC001 and the
reading is far off, suspect the cell before the ratio. Accepted values run from 0.1 to 10;
anything outside that range, including 0 or a negative number, is rejected with a 422.
Field table: System configuration → Sensor calibration.
Get told when the battery is low¶
On a board with a battery pin, GET /api/v1/device always carries a lowBattery boolean. Point
lowBatteryThreshold at a percentage and the flag reads true once batteryPercent drops below
that level (strictly less than, not inclusive):
curl -X PUT http://<awtrix-ip>/api/v1/system \
-H "Content-Type: application/json" \
-d '{"lowBatteryThreshold": 15}'
The default is 0, which turns the signal off and leaves lowBattery permanently false. The
flag tracks the same 2-second percentage as the other battery keys, and like them it disappears
from the response on a board with pinBattery: -1. In Home Assistant the same signal shows up as a
Low battery binary sensor (below).
Field table: System configuration → Sensor calibration.
Turn the matrix off and on¶
This is the light switch, not the power switch. The LEDs go black; the ESP32, Wi-Fi, MQTT and the app loop all keep running, and AWTRIX stays fully reachable over HTTP.
curl -X PATCH http://<awtrix-ip>/api/v1/display \
-H "Content-Type: application/json" \
-d '{"power":false}'
Back on:
curl -X PATCH http://<awtrix-ip>/api/v1/display \
-H "Content-Type: application/json" \
-d '{"power":true}'
Read the current state from GET /api/v1/display (power) or GET /api/v1/device
(matrixPower) - both are booleans that are false while the panel is blanked.
Three things that interact with it:
- From AWTRIX itself: a double-press of the select button (the middle one) toggles the
matrix on and off. It is the only way to wake the display without the network, and it is skipped
while
blockNavigationis on. - Wakeup notifications punch through. A notification sent with
"wakeup": truerenders even while the matrix is powered off, for as long as it is the active notification. See App & notification payload → Notification-only keys. - Brightness is unaffected. Blanking does not touch your brightness or auto-brightness settings; it just stops drawing. For brightness itself see Brightness & sensors.
Full route details: HTTP API → Display.
Deep sleep¶
Deep sleep powers the ESP32 down for a fixed duration. Unlike blanking the matrix, AWTRIX is gone while it sleeps: no HTTP, no MQTT, no apps.
curl -X POST http://<awtrix-ip>/api/v1/device/sleep \
-H "Content-Type: application/json" \
-d '{"durationMs":60000}'
durationMs is required and must be a positive integer in milliseconds. There is no value that
means "sleep forever" or "sleep until I say so" - 0 is rejected with a 422. A valid request
answers 200 {"ok":true} first and the board goes down only after that response has been written,
so your client gets its confirmation and then the connection drops. Full route details, including
every status code: HTTP API → POST /api/v1/device/sleep.
A sleeping board is off the network
Nothing on the network reaches AWTRIX while it sleeps and there is no cancel, so without
physical access a one-hour sleep means an hour of silence. Two wake sources are armed: the timer and
the select button. The button only works when pinBtnSelect sits on a wake-capable GPIO,
which the default wiring uses; move it elsewhere and the timer is the only way back. See
GPIO & boards → The pin map.
The matrix is blanked just before the board goes down. On wake the ESP32 boots fresh - the same sequence as a power cycle, so it re-reads its configuration, reconnects Wi-Fi, and the matrix comes back on (a blanked panel is a runtime state and does not survive).
The same command exists over MQTT as the device/sleep op - see
MQTT topics → Command topics.
Battery in Home Assistant¶
When the board has a battery pin, the Home Assistant integration exposes three entities automatically:
| Entity | Device class | Unit | Source |
|---|---|---|---|
| Battery | battery |
% | batteryPercent |
| Battery voltage | voltage |
V | batteryVoltage |
| Low battery | battery |
- | lowBattery |
The Low battery binary sensor only trips once you set a non-zero lowBatteryThreshold (see
Get told when the battery is low). All three disappear on a
board with pinBattery: -1. The pin-side batteryPinMillivolts is not exposed as an HA entity - read
it from GET /api/v1/device when calibrating.
See Home Assistant.
Related¶
- Device state - every field in
GET /api/v1/device - System configuration → Sensor calibration -
batteryDividerRatio - GPIO & boards → The pin map -
pinBattery, and why it must be an ADC1 pin - Brightness & sensors - the light sensor and auto-brightness
- HTTP API → Display - the matrix power route