Koenkk / Koenkk/zigbee2mqtt

Energy sensor variants (energy_today/yesterday/month) all get the same Home Assistant entity name

Open
#32,681 0 comments 4 reactions 0 assignees View on GitHub

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)
  1. Pair any device exposing more than one kWh numeric (e.g. SONOFF S60ZBTPF, but this
    affects every device with energy_today / energy_yesterday / energy_month).
  2. mosquitto_sub -t 'homeassistant/sensor/<ieee>/+/config' — every energy config has
    "name": null.
  3. 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_name is spread into discovery_payload from NUMERIC_DISCOVERY_LOOKUP and
    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_yesterday state_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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.