P410M: Error processing Energy for device, module will be disabled - no digit after minus sign JSON decode error on get_emeter_data · Issue #1758 · python-kasa/python-kasa · GitHub
On four different TP-Link Tapo P410M smart plugs (used as generation meters for a balcony solar setup), the Energy module consistently fails to parse and gets disabled shortly after startup/reconnect, with a JSON decoding error:
Error querying <ip> for modules 'Time, AutoOff, DeviceModule, Energy, Matter, PowerProtection' after first update: no digit after minus sign: line 1 column 228 (char 227)
Error querying <ip> individually for module query 'get_emeter_data' after first update: no digit after minus sign: line 1 column 99 (char 98)
Error processing Energy for device <ip>, module will be unavailable: get_emeter_data for Energy (error_code=INTERNAL_QUERY_ERROR)
and, on subsequent polls:
Error processing Energy for device <ip>, module will be disabled: get_emeter_data for Energy (error_code=INTERNAL_QUERY_ERROR)
This affects the resulting Home Assistant current_consumption / today's-/month's-consumption sensors, which sit permanently at unknown/unavailable, even though the device is otherwise fully reachable and reports live power correctly in the official Tapo app (confirmed side-by-side: Tapo app showed ~372 W while HA's sensor.<device>_derzeitiger_verbrauch stayed on unknown).
A full homeassistant.reload_config_entry (fresh reconnect) does not resolve it — the error recurs on the very next Energy query.
Affected devices
All four are the same model, P410M, connected locally via KLAP (device_family: SMART.TAPOPLUG, encryption_type: KLAP, login_version: 2, https: false). Since the same error/column offset pattern appears across four physically distinct units at four different IPs, this looks like a firmware/protocol edge case rather than a single defective device.
A fifth device on the same network, a P110M, is unaffected — its Energy/consumption readings work correctly and continuously.
Environment
Home Assistant Core: 2026.9.1 (Supervised/HAOS)
tplink integration manifest requirement: python-kasa[speedups]==0.10.2 (confirmed this is also the current latest release on PyPI — no newer version available to test against)
Devices: TP-Link Tapo P410M (energy-monitoring smart plug), x4, firmware/hw revision not yet captured (happy to pull via kasa CLI if useful)
Set up a TP-Link Tapo P410M smart plug via the local tplink integration (KLAP auth).
Let it run for a normal polling cycle.
Observe the Energy module fail with the JSON decode error above and become permanently disabled until the plug is fully removed and re-added (a config-entry reload alone does not help).
Additional context
Happy to provide more diagnostics (raw device response capture, full debug log excerpt with homeassistant.components.tplink: debug / kasa: debug already enabled) on request.
The energy_mwh field is the culprit — it's not a number, not null, just garbage (/.0*./-) that fails JSON parsing outright (no digit after minus sign / expected a JSON value, depending on where the parser lands in that fragment). Every other field in the same response (current_ma, voltage_mv, power_mw, energy_wh) is valid. This matches the pattern across all four of my affected units, so it looks like a consistent device-firmware-side corruption of that one field rather than anything transport/parsing-specific on the python-kasa side.
As a workaround on my end I ended up writing a small custom sensor platform that talks to the device via kasa.Device.connect() but never calls dev.update() (which triggers the broken Energy module) — instead it queries get_energy_usage + get_current_power directly via dev.protocol.query(...), which both return clean data. Happy to share more details if useful for a fix/fallback.
Follow-up on my earlier comment: I ended up writing that custom sensor platform I mentioned and published it in case it's useful to anyone else hitting this while waiting for a real fix:
It's intentionally minimal — bypasses Device.update()/the broken Energy module entirely and queries get_energy_usage + get_current_power directly for power and daily/monthly energy. No voltage/current (those only ever lived in the broken get_emeter_data payload). Happy to take this down or fold it into a proper fix if that ever lands upstream instead.
Activity
mannymuster commented on Sep 12, 2026
For what it's worth, here's the exact raw (pre-parse) get_emeter_data payload I captured from one of the affected P410M units, reproduced locally:
{"result":{"current_ma":2210,"voltage_mv":234448,"power_mw":-507062,"energy_wh":-1206,"energy_mwh":/.0*./-},"error_code":0}The energy_mwh field is the culprit — it's not a number, not null, just garbage (/.0*./-) that fails JSON parsing outright (no digit after minus sign / expected a JSON value, depending on where the parser lands in that fragment). Every other field in the same response (current_ma, voltage_mv, power_mw, energy_wh) is valid. This matches the pattern across all four of my affected units, so it looks like a consistent device-firmware-side corruption of that one field rather than anything transport/parsing-specific on the python-kasa side.
As a workaround on my end I ended up writing a small custom sensor platform that talks to the device via kasa.Device.connect() but never calls dev.update() (which triggers the broken Energy module) — instead it queries get_energy_usage + get_current_power directly via dev.protocol.query(...), which both return clean data. Happy to share more details if useful for a fix/fallback.
mannymuster commented on Sep 12, 2026
Follow-up on my earlier comment: I ended up writing that custom sensor platform I mentioned and published it in case it's useful to anyone else hitting this while waiting for a real fix:
https://github.com/mannymuster/ha-tapo-direct
It's intentionally minimal — bypasses Device.update()/the broken Energy module entirely and queries get_energy_usage + get_current_power directly for power and daily/monthly energy. No voltage/current (those only ever lived in the broken get_emeter_data payload). Happy to take this down or fold it into a proper fix if that ever lands upstream instead.