Skip to content

three-phase: entity unique_id follows primary name, not alias — old entity orphaned with no deprecated mapping #681

Description

@martinarva

On a three-phase Deye (SENSOR_DEFINITIONS: three-phase), the HA entity for register 133 changed its unique_id:

<serial>_generator_port_usage   ->   <serial>_aux_port_usage

HA entity-registry timestamps date this to 2026-08-15, ~08:00 (UTC+3): the old entry's modified_at is 07:36:35 and the new entity's created_at is 08:30:10.

What I verified

The definition itself is unchanged in current edge:

SelectRWSensor(
    133,
    "AUX port usage",
    options={0: "Generator", 1: "Smartload", 2: "Micro Inverter"},
    alias="Generator Port Usage",
),

Sensor.id is slug(self.name) and a_sensor.py builds "unique_id": f"{dev_id}_{sensor.id}", so the id follows the primary name. That matches the docstring:

alias: str | tuple[str, ...] | None = None
"""Alternate name(s); each is registered in ``SensorDefinitions.all`` under ``slug(name)``."""

i.e. alias is a config-lookup alias (so generator_port_usage can be written in SENSORS:), not an entity-ID source. So the current behaviour may well be intended — I could not find a commit that changed it, and I could not diff against the previous edge build (it had already been pruned locally).

The practical problem

Whatever the cause, HA created a new entity and left the old one as a permanent unavailable orphan. Anything referencing the old entity ID silently stops working, with no log line and no migration.

SensorDefinitions.deprecated ({"old_name": "new_name"}) exists for exactly this case, but the three-phase map is empty:

# three_phase_common.py:677
SENSORS.deprecated.update(
    {
        # "priority_mode": "priority_load",
    }
)

Question

Would you consider populating deprecated whenever a sensor's effective entity ID changes — including cases where an entity previously resolved via its alias? That would let HA migrate the entity instead of orphaning it.

If aliases are never meant to surface as entity IDs, then a short note in the docs would also help: it is easy to assume that alias="Generator Port Usage" is what produces select.<prefix>_generator_port_usage.

Reproduction

from sunsynk.definitions import import_defs
d = import_defs("three-phase")          # -> three_phase_lv (251 entries)
s = d.all["aux_port_usage"]
g = d.all["generator_port_usage"]
(s.name, s.address, s.id)   # ('AUX port usage', (133,), 'aux_port_usage')
(g.name, g.address, g.id)   # ('AUX port usage', (133,), 'aux_port_usage')
s is g                      # True
dict(d.deprecated)          # {}

Both lookup keys resolve to the same object, whose id — and therefore unique_id — is aux_port_usage. Anyone whose entity was previously ..._generator_port_usage gets a second entity and an orphan.

Environment

  • addon 52208e3 (ghcr.io/kellerza/hass-addon-sunsynk-multi:edge)
  • SENSOR_DEFINITIONS: three-phase, which maps to three_phase_lv (via import_defs); register 133 comes from three_phase_common
  • single inverter, MQTT discovery to Home Assistant
  • Happy to test a patch.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions