Energy sensor variants (energy_today/yesterday/month) all get the same Home Assistant entity name
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.7k
- Forks
- 2k
- Avg merge
- 18h 55m
- Merged PRs (30d)
- 35
Description
What happened?
All four energy sensors of a SONOFF S60ZBTPF plug arrive in Home Assistant with the same
entity name, "Energy", because Z2M strips the name field from their discovery payloads.
The exposes themselves are correctly labelled (zigbee2mqtt/bridge/devices):
{"property": "energy", "label": "Energy", "unit": "kWh"}
{"property": "energy_yesterday", "label": "Energy yesterday", "unit": "kWh"}
{"property": "energy_today", "label": "Energy today", "unit": "kWh"}
{"property": "energy_month", "label": "Energy this month", "unit": "kWh"}
But the published discovery configs under homeassistant/sensor/<ieee>/+/config all have
name: null with device_class: energy:
.../energy/config name = None, device_class = energy, state_class = total_increasing
.../energy_yesterday/config name = None, device_class = energy, state_class = total_increasing
.../energy_today/config name = None, device_class = energy, state_class = total_increasing
.../energy_month/config name = None, device_class = energy, state_class = total_increasing
Home Assistant then does what it is documented to do — with name: null it derives the
entity name from the device class — so all four end up as "Energy" (localised; "Energie"
here). Sensors on the same device that have no device class keep their labels and are
named correctly, e.g. linkquality → "Linkquality", overload_protection → "Overload
protection".
What did you expect to happen?
The four sensors to keep their distinct labels, as energy, energy_today,
energy_yesterday and energy_month are semantically different values.
How to reproduce it (minimal and precise)
- Pair any device exposing more than one
kWhnumeric (e.g. SONOFF S60ZBTPF, but this
affects every device withenergy_today/energy_yesterday/energy_month). mosquitto_sub -t 'homeassistant/sensor/<ieee>/+/config'— every energy config has
"name": null.- In HA all four entities display as "Energy". Entity IDs stay unique, so this is a
display/usability issue, not a data issue.
Zigbee2MQTT version
2.12.1 (also present on dev and master — the relevant code is unchanged there)
Adapter firmware version
7.4.5 [GA] (EZSP 13)
Adapter
SONOFF Dongle Plus MG24 (EmberZNet)
Setup
Home Assistant OS 17.3, Zigbee2MQTT add-on
Device database.db entry
Not relevant — this is in the HA discovery layer, not device handling.
Debug log
Not relevant — payload contents shown above.
Notes
Root cause is the interaction of two mechanisms in lib/extension/homeassistant.ts:
1. The unit heuristic assigns device_class by unit alone (dev, ~line 1185):
// If a variable includes Wh, mark it as energy
if (firstExpose.unit && ["Wh", "kWh"].includes(firstExpose.unit)) {
Object.assign(extraAttrs, {device_class: "energy", state_class: "total_increasing"});
}
So energy_today, energy_yesterday and energy_month all receive
device_class: energy purely because their unit is kWh. Note that
NUMERIC_DISCOVERY_LOOKUP has no entries for these three — only for energy.
2. The name is then deleted (dev, ~line 1445):
// Let Home Assistant generate entity name when device_class is present.
// preserve_name allows device_class and explicit name to coexist (e.g. derived sensors).
if (entry.discovery_payload.device_class && !NUMERIC_DISCOVERY_LOOKUP[firstExpose.name]?.preserve_name) {
delete entry.discovery_payload.name;
}
Since NUMERIC_DISCOVERY_LOOKUP["energy_today"] is undefined, preserve_name is
undefined, and the label is dropped.
This is the same class of bug as #32337, fixed for derived weather sensors in #32392 by
introducing preserve_name. The energy_* variants were not covered by that fix.
Suggested direction. Rather than adding preserve_name entries one by one, the
distinction could be made structurally: a device_class inferred solely from the unit
carries no naming information, whereas one from an explicit NUMERIC_DISCOVERY_LOOKUP
entry does. Deleting the name only in the latter case would fix all *_today / *_yesterday
/ *_month style variants at once, while leaving energy, power, current and voltage
(all of which have explicit lookup entries) behaving exactly as today.
Two smaller observations found while tracing this, happy to split them out:
preserve_nameis spread intodiscovery_payloadfromNUMERIC_DISCOVERY_LOOKUPand
never deleted before publishing, so it currently leaks into the published discovery JSON
for the five weather sensors from #32392.- The same unit heuristic also gives
energy_yesterdaystate_class: total_increasing.
That value is fixed for the day and not monotonically increasing, so the state class
looks incorrect for that expose.
I'm happy to open a PR against dev for whichever direction you prefer.
Claude Opus 5, reviewed and supported by Steven Wagner
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/extension/homeassistant.ts at the unit-based device_class heuristic and later name-removal logic, then inspect NUMERIC_DISCOVERY_LOOKUP and the preserve_name handling from #32392. Reproduce the four homeassistant/sensor//+/config payloads with mosquitto_sub; the work is done when the energy variants retain distinct names without changing existing explicit lookup behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100