private@homelab: ~/latest
Home Assistant · Zigbee2MQTT · local control
private@homelab:~$ cat guides/article.md
· ·
6–9 minutes
read

Zigbee2MQTT Battery % Stuck or Wrong: Why It Happens

Diagnose stuck or implausible Zigbee2MQTT battery values: raw scaling, voltage curves, stale reports and model-specific low flags, with practical checks.

A battery value stuck at 100%, jumping between 1% and 100%, or exceeding 100% needs investigation, but it does not identify the cause by itself. Zigbee2MQTT may use a percentage sent by the device, calculate one from reported voltage, or apply a model-specific scaling rule. A stale dashboard value adds another possibility.

This guide explains those paths and gives a practical way to separate a battery problem from reporting, conversion and display problems. It uses current converter code, device documentation and historical reports; no discharge test was performed on physical sensors.

Check the exact model

The available properties, read commands and calculation depend on the matched device definition. Do not apply one sensor’s curve or battery threshold to another just because both use Zigbee.

What battery, voltage, and battery_low actually measure

  • battery is the exposed percentage estimate, normally described as 0–100%. It can originate in firmware or converter math.
  • voltage is reported battery voltage, commonly exposed in millivolts. It can be coarse, delayed or stale; it is not automatically a precise current multimeter reading.
  • battery_low is a separate low-battery indication where the model exposes one. Its source and threshold depend on the device/converter.

These properties need not arrive together or agree perfectly. Check your model’s exposes and the MQTT message documentation. This example is illustrative, not measured data:

zigbee2mqtt/example_sensor
{
  "battery": 100,
  "voltage": 3035,
  "battery_low": false,
  "linkquality": 63,
  "last_seen": "2026-08-18T09:12:03+08:00"
}

last_seen records received device activity when enabled; it does not prove the battery attributes were refreshed in that message. Cached values can be carried into a later publication. Check a fresh battery report or supported read response when you need to know the age of the measurement.

Correct cell types and replacement procedures are a separate subject in our Aqara/Xiaomi battery guide. Our device_temperature versus temperature guide makes a similar distinction between properties that happen to look related.

Why percentage gets stuck near 100% for months

The first path uses the device’s batteryPercentageRemaining attribute. The common battery converter scales the reported value unless that model declares an exception. The second path uses reported voltage and a device-defined conversion curve or minimum/maximum range. A model’s voltageToPercentage setting can deliberately replace the firmware percentage.

The conversion helpers include different mappings. Their output is an estimate based on assumptions, not a direct measure of remaining capacity. Some Xiaomi/Aqara definitions also use their own manufacturer-specific reports, so the generic path is not universal.

Coin-cell voltage can remain relatively flat over a substantial part of discharge. Panasonic’s discharge examples and its CR2032 data also show that conditions matter. Load and temperature affect the reading, and a simple curve cannot establish exactly how many usable days remain.

A long plateau can therefore be plausible, but it is not the only explanation. Issue #8499 describes a Xiaomi sensor reported at 100% with unchanged voltage until failure. That is a useful historical observation of unreliable readings, not proof that every plateau has the same chemistry or firmware cause.

Why it jumps, reads over 100%, or stops updating at all

No first reading. The TS0201 page says its battery percentage can take up to 24 hours to appear. Several other device pages make similar model-specific notes. A first-day gap can be expected for those models; it is not a blanket 24-hour waiting rule for every device or every missing report.

A sleeping device does not answer a request. Battery devices commonly sleep between transmissions. Check whether the property supports /get, then wake the device using its documented procedure while requesting it. If no read is supported, watch the next report. Reconfigure reporting only when the model supports it and is awake; aggressive polling does not turn a report-only attribute into a readable one.

Half-scale or out-of-range values. The Zigbee Cluster Library standard defines the percentage attribute in half-percent units: a raw 200 represents 100%. The common converter divides by two, while dontDividePercentage handles known devices that send a whole-percent value instead. It also ignores the raw 0xFF unknown value. Device-specific converters may handle the attributes differently.

In converter issue #2411, a ROBB/Sunricher switch sent a raw value of 100 and produced 50%; a maintainer added the scaling exception. That is a fixable device-definition mismatch. A value above 100 can involve an invalid raw value, incorrect scaling or a wrong matched definition; it is not proof that halving was simply omitted. Do not halve or double the published value yourself without identifying the actual path.

Large jumps. Issue #21611 describes a TS0203 on Zigbee2MQTT 1.35.3 moving from 1% to 100% after a rename or short press, then returning to 1%. This is one reported observation, not a reliable reset procedure or evidence that renaming repairs battery measurement.

Use several signals

A low-battery flag is useful when present and verified for the model, but it is not automatically more accurate than percentage or voltage. Combine it with fresh reports, expected device activity and periodic functional checks. Treat a critical alert sensor’s health as something to verify, not something one dashboard number guarantees.

For example, current pages list no battery_low for Aqara MCCGQ11LM, T1 MCCGQ12LM, Xiaomi RTCGQ01LM or Tuya TS0041. Aqara E1 MCCGQ14LM does expose it. Aqara P1 motion sensor RTCGQ14LM currently lists battery and voltage without that flag. Check the exact model instead of copying an automation that references a missing property.

Is this a Zigbee2MQTT bug or a device bug?

Either layer can matter. Firmware can send coarse or incorrect measurements, a converter can need a scaling/curve fix, or your display can be showing cached state. Similar symptoms across brands do not prove that the converter is blameless.

Issue #568 records WSDCGQ01LM readings of 99% at 3005mV and 27% at 2955mV. That discussion led to merged PR #191, which changed the toPercentageCR2032 conversion. A converter-side remedy can exist; its accuracy still depends on the measurement and model assumptions.

For an implausible current value, record the Zigbee2MQTT/converter versions, model and manufacturer string, raw battery report, resulting MQTT payload and timing. Compare that with the installed definition and any existing issue for the exact device. A raw-to-output mismatch is more useful evidence than another screenshot saying the percentage looks wrong.

The practical read: use voltage as the sanity check

Voltage is a useful cross-check when it is fresh and meaningful for that device. Compare readings over time under similar conditions; a battery may sag during transmission or cold conditions and recover afterward. A stable, cached or coarsely rounded voltage is not proof of remaining capacity.

Observation Possible explanations Useful next check
100% for months Voltage plateau, rough mapping or stale battery data Confirm a fresh battery attribute and the model’s calculation; compare trends and operation
Above 100% or half-scale Raw value/scaling exception, wrong definition or firmware behavior Capture raw report and conversion; check supported version/model before altering values
No value after pairing Documented report delay, incomplete interview or no successful battery report Check model notes, interview status and supported read/wake procedure
1% → 100% → 1% Reporting, cached display, firmware or converter issue Compare raw reports with MQTT output; jumps alone do not establish cell failure
Low flag with a high percentage Separate thresholds or estimates, stale fields or faulty reporting Verify fresh values, test device operation and assess the correct cell rather than assuming one signal wins

Our last_seen explainer covers why recent traffic and fresh values are different checks. Availability can add another alarm, but a sleepy device’s expected reporting interval and configured timeout must be considered.

When to actually worry vs. when to ignore it

  1. Check freshness first. Compare a new attribute report with the dashboard and MQTT state. Do not mistake a retained value for a new measurement.
  2. Check operation. Trigger a known action safely and confirm it is received. A failed response can be battery, range, mesh, configuration or hardware; it is not automatically proof of an empty cell.
  3. Investigate plausible deterioration. Repeated fresh low readings, a validated low-battery flag or unreliable operation justify checking the contacts and correct replacement cell. Compare a known-good cell if that is the practical way to distinguish a power problem.
  4. Handle conversion errors separately. Preserve the raw evidence, check supported updates and report an exact-model mismatch. Avoid repeated factory resets or renames as a general battery fix.
  5. Plan maintenance for important devices. Follow the model/cell guidance and observed service history, and test leak/security alerts periodically. There is no evidence-based annual replacement interval that fits every sensor and battery chemistry.

In Home Assistant, a useful health check can combine the properties actually exposed with an availability or expected-activity alert. Test it, choose timeouts suitable for the sensor and retain a manual functional check for devices whose failure would matter.

What this covers, what stays local, and what to check

The conversion paths were checked against zigbee-herdsman-converters 26.115.1, used by Zigbee2MQTT 2.14.2. Device pages establish property/read support and reporting notes; the historical issues show particular failures and fixes. None establishes one root cause for every battery anomaly.

Reporting travels over the configured Zigbee/MQTT path. The accuracy of the percentage does not itself decide whether the broker or notification service is local. On your setup, check the exact converter, raw scaling, measurement age and expected device behavior before deciding whether the cell, firmware, definition or display needs attention.

local-firstHome Assistantno-cloud