Battery State of Charge from Voltage
State of charge from raw battery voltage, not percent: chemistry presets, per-cell thresholds, cell-imbalance detection, marked estimate.
Who it’s for
Teams running battery-powered field devices whose hardware reports a raw battery voltage in volts, not a clean 0-100 percent. That covers LoRaWAN and NB-IoT sensors on primary lithium, solar and off-grid nodes on Li-ion or LiFePO4, multi-cell packs and small BMS setups, and lead-acid backup and standby. If your device sends a batteryVoltage telemetry key and you want it to read as a battery on the dashboard, this is the card for that.
What it does
Shows battery voltage as the large, primary reading, inside a battery glyph that fills between the empty and full points you configure. A colour-coded verdict names the state at a glance - OK, Low, Critical, Charging, Imbalance or Stale - so an operator knows whether to act without reading the number.
It replaces the built-in Battery level widget when your device speaks volts, not percent. ThingsBoard’s Battery level widget needs a 0-100 value, which forces a chemistry-specific volts-to-percent conversion in a rule chain or calculated field before anything shows, and most people get that conversion wrong. This card takes the volts directly. Where a percentage is genuinely meaningful it shows one, always marked as an estimate, never as a hard claim.
It is chemistry-aware. Choose Li-SOCl2, CR2032, Li-ion, LiFePO4, alkaline, NiMH or lead-acid, or set your own full and empty voltages. On the flat chemistries - the ones where a single voltage barely predicts state of charge, like the Li-SOCl2 cells in most long-life LoRaWAN sensors - the percentage is suppressed by default rather than published as fiction, and the card can read on plateau or past the knee instead.
For multi-cell packs, bind a minimum and a maximum cell-voltage key and the card derives the spread and raises an Imbalance verdict when the cells drift apart. That spread is the single number that predicts pack failure, and it is invisible in any pack-level voltage reading - a pack averaging a healthy 3.3 V with one cell at 2.9 V is a dying pack, and the pack voltage will not say so.
How to set up
Bind one device and add its telemetry keys:
batteryVoltage- required, the hero value. Nothing renders without it.temperature- optional. Shown in the footer, and used to mark the readingcoldwhen it falls outside the chemistry’s operating window.current- optional. Its sign drives the Charging verdict and the bolt, so no separate charging flag is needed.cellVoltageMinandcellVoltageMax- optional. Bind both to turn on the imbalance check.
Every optional key works the same way, and none of them has a separate toggle: a name in the box switches that part of the card on, an empty box switches it off.
If your device names them differently, override the names under Data. A reading above roughly ten times the full voltage is treated as millivolts automatically, so 3600 and 3.6 both work. Then pick a Chemistry preset and, for a pack, set Cells in series: the card divides the bound voltage by the cell count and works in per-cell volts, so a 4S LiFePO4 pack reading 13.2 V is 3.3 V per cell, about 80%.
What the bar means
The bar is a voltage window, not a charge gauge. It shows where the reading sits between the configured empty and full voltages, and both ends are printed under it so there is no guessing. It is deliberately not the same thing as the percentage estimate: the estimate follows the chemistry’s discharge curve, the bar is linear in volts, and on a curved chemistry the two legitimately differ. Read the bar for position in the window and the estimate for state of charge.
When the card says Check setup
If the reading falls far outside the configured window - above 115 percent of full, or below half of
empty - the card stops interpreting it. The chip reads Check setup, the bar stays empty and the
second line names the window the reading is outside, for example above the 3.00-4.20 V window.
Almost always this means the preset does not match the battery: a pack voltage read against a per-cell preset, the wrong chemistry, or Cells in series left at 1. Fix the preset or the cell count and the card returns to a normal verdict. The card refuses to show a confident full green battery for a reading it cannot place, which is the failure direction that matters on a battery widget.
The estimate also names its own reason when it steps aside: estimate paused, charging while
current is flowing, and estimate paused, above full for a reading off the top of the curve.
How to customize
The settings answer the questions operators actually ask:
- “My sensor reads 3.6 V for years and then dies - what use is a percentage?” None on a flat cell, which is why Li-SOCl2, LiFePO4 and CR2032 ship with the estimate off. Turn on the plateau / knee phrase for a position instead of a proportion.
- “The voltage looks low every time it transmits.” Voltage measured under transmit load is not the resting voltage. Every threshold lives in the chemistry preset, so there is no hidden constant - and on a named chemistry the preset is the answer, which is why those boxes are not on screen. Switch Chemistry preset to Custom to set a Warning and a Critical voltage that suit your duty cycle.
- “Is this pack about to fail?” Bind the two cell-voltage keys. The advisory (amber) and serious (red) imbalance thresholds derive from full voltage - 3 percent and 5 percent, capped at 120 and 200 mV - and are editable in millivolts on the Custom preset.
- “It dims a healthy sensor.” Stale-after-minutes defaults to 0, meaning never. Set it just above your device’s reporting interval - a daily LoRaWAN reporter should not be dimmed at fifteen minutes.
The value’s decimal places come from the data key’s own Decimals setting rather than a second control on the card, the value, estimate-line and title fonts are type: font controls, and the battery glyph, chemistry footer and verdict chip can each be hidden. The est. marker on the estimate is editable text, and the pause above full voltage can be switched off if you would rather the estimate keep running there. The operating temperature window behind the cold marker follows the chemistry preset, and is yours to set on Custom. The card renders as a clean bordered card and follows the widget’s grid size: nothing appears or disappears as it is resized, the type scales, and below about 150 px of height the spacing tightens so the footer stays inside the card rather than being pushed past its bottom edge.
Every setting, and when you see it
The dialog hides what it cannot act on, so this list is longer than what is on screen at any one time. The per-cell voltages and the two imbalance thresholds appear only when Chemistry preset is Custom, because on a named chemistry those numbers are the preset: a box you can type into that is then ignored is worse than no box at all. The same rule runs through the rest - the two estimate controls appear while Estimated percentage is not Off, and the plateau phrase only on the chemistries that have a plateau.
Data keys. Battery voltage key is the hero reading, batteryVoltage out of the box, and
nothing renders without it. When it yields no number the card names which of four things happened:
no key is bound at all, the datasource carries other names, the key is bound and has never
reported, or the last value was not a number - each hint naming the key you configured rather than
telling you to bind a key you have already bound. Temperature key (temperature) feeds the
footer and the cold qualifier. Current key (current) drives the Charging verdict and the
bolt from the sign of the reading, so no separate charging flag is needed. Each of the three is
optional in the same way - a key name switches its part of the card on, an empty box switches
it off - and none of them is gated behind a toggle. Cell imbalance: lowest cell voltage key
and Cell imbalance: highest cell voltage key (cellVoltageMin, cellVoltageMax) turn on the imbalance check when
both are bound, and change nothing when neither is. Device reports voltage in sits with the
keys rather than with the display settings because it is not one: it decides how a raw reading
is interpreted before any threshold is applied. Auto-detect reads anything above roughly ten
times the full voltage as millivolts, so 3600 and 3.6 both work, and the explicit Volts and
Millivolts settings are there for a device the heuristic would get wrong.
Chemistry and pack. Chemistry preset carries the full and empty voltages, the warning and critical points, the discharge curve and the operating temperature window, so on a named chemistry it is the only chemistry decision you make. Cells in series divides the bound voltage by the cell count, so a 4S LiFePO4 pack reading 13.2 V is 3.3 V per cell.
The eight Custom voltages. All per cell, all hidden unless the preset is Custom, and Custom now
opens pre-filled at 3.7 V and 3.0 V rather than blank, so you can see what you are editing away
from. Full voltage, per cell and Empty voltage, per cell are the two ends of the bar.
Warning voltage, per cell and Critical voltage, per cell are the Low and Critical verdict
points - leave either blank to drop that verdict. Operating temperature min, C and Operating
temperature max, C bound the cold qualifier, and blank on either end means no qualifier at all.
Imbalance advisory, mV and Imbalance serious, mV are the amber and red spread between the
lowest and highest cell, and blank derives them from full voltage.
The estimate. Estimated percentage is Auto, On or Off, and Auto is the honest default: on
where voltage tracks charge, off where it does not. Estimate marker text is the est. badge,
editable because its whole job is that nobody reads the number as a measurement. Pause estimate
above full voltage is on by default, so a reading off the top of the curve reads estimate paused, above full rather than inventing a figure. Plateau / knee phrase replaces the
percentage with a position - on plateau or past the knee - and appears only on Li-SOCl2, CR2032
and LiFePO4, the three presets where it does anything.
Display. The card carries no value-format settings of its own. Decimal places come from the
data key’s own Decimals setting, and fall back to two places when it is left on Auto - three
on the flat chemistries, where the third place is the only one any movement shows in.
Show battery glyph off swaps the glyph for a plain reading, and Show verdict chip and
Show chemistry in footer each hide their own part of the card. Stale after (minutes)
defaults to 0, meaning never, and does not accept a negative value. Value font, Estimate
line font and Title font are type: font controls over the hero, the estimate line and the
entity name. Each applies family, size, weight and style, and the unit beside the value follows
the value font rather than a fixed size of its own.
See also
This card answers one device in depth. A fleet asks a different question - which of two hundred devices is worth walking to this week - and that is a different card:
${item-link:d7931040-8f49-11f1-a1b4-f1800178e9a1}
Two things worth wiring up beside it. Raise an alarm on the same batteryVoltage key at the
chemistry’s critical point, so a dying pack is noticed by somebody who was not looking at a
dashboard. And put cellVoltageMin and cellVoltageMax on a time-series chart together: the card
shows you today’s spread, a chart shows you the month it started widening.
Share Your Widget with the Community
Built a custom widget? Export it as a JSON from ThingsBoard and publish it to the IoT Hub through a simple 4-step wizard (Upload, Listing, Readme, Review & Submit). Share it with thousands of ThingsBoard developers worldwide and get featured in the catalog.