Skip to content

fix(naming): Recreate entity IDs proposes what the panel says now - #256

Closed
cayossarian wants to merge 4 commits into
feat/discovery-and-catalog-validationfrom
fix/recreate-entity-ids
Closed

fix(naming): Recreate entity IDs proposes what the panel says now#256
cayossarian wants to merge 4 commits into
feat/discovery-and-catalog-validationfrom
fix/recreate-entity-ids

Conversation

@cayossarian

Copy link
Copy Markdown
Member

Closes #252.

Renaming a circuit in the SPAN app left "Recreate entity IDs" offering the entity
its own ID, so the button appeared to do nothing.

Cause

The registry generates an ID from the user's name override, else
suggested_object_id, else object_id_base. Friendly-names mode writes no
registry name — the panel name arrives as original_name — so the suggestion
decides. construct_single_circuit_entity_id was handing back the entity's
stored ID on every reload, which HA recorded as the suggestion, so the field was
frozen at whatever the circuit was called the day it was added.

Fix

It now computes the ID from current panel data and the naming flags, for existing
entities as much as new ones.

The three constraints, and why each holds

  • Existing entity IDs cannot move. An entity_id set before add is only a
    suggestion: EntityPlatform splits it into suggested_object_id, and for a
    unique_id already on file async_get_or_create routes to
    _async_update_entity with no new_entity_id, then reassigns
    entity.entity_id from the stored entry. Structural, not a matter of care.
  • Unique IDs cannot move. They derive from the description key and never read
    a name. Untouched here.
  • Phase 2 name sync works as before. Untouched. All six registry-name write
    sites are gated on USE_CIRCUIT_NUMBERS, and nothing in that mode changes.

Circuit-numbers mode is deliberately unchanged

There the registry name phase 2 sync writes is both what shows the panel's name
in the UI and what outranks the suggestion during regeneration — one write, two
effects. So Recreate in that mode keeps composing from the name rather than the
tab-based ID. That is pre-existing, and a test pins it so this change cannot
disturb it. Correcting it means rerouting phase 2 sync, which is a product
decision recorded in the design doc, not part of this fix.

Cleanup

The registry lookup was the only use of the unique_id argument, and
existing_entity_id the only use of that parameter on _construct_entity_id.
Both are removed rather than left as parameters that no longer decide anything.

Tests

Eight new cases, all reloading before asserting — asserting straight after
creation exercises the first-add path, where the suggestion is trivially current
and the bug cannot appear. Three demonstrate the bug and fail on the previous
code (sensor suggestion, async_regenerate_entity_id, breaker switch); five are
guards that pass on both (entity ID unmoved, unique ID unmoved, unrenamed circuit
offered its own ID, circuit-numbers mode unchanged).

1261 passing, up from the 1253 baseline. Full prek hook set green, mypy strict
and vulture included.

Renaming a circuit in the SPAN app left "Recreate entity IDs" offering the
entity its own ID, so the button appeared to do nothing (#252).

The registry generates an ID from the user's `name` override, else
`suggested_object_id`, else `object_id_base`. Friendly-names mode writes no
registry `name` -- the panel name arrives as `original_name` -- so the
suggestion decides, and `construct_single_circuit_entity_id` was handing back
the entity's stored ID on every reload. The suggestion was therefore frozen at
whatever the circuit was called the day it was added.

It now computes the ID from current panel data and the naming flags, for
existing entities as much as new ones. That cannot move a live entity ID: an
entity_id set before add is only a suggestion, and for a unique_id already on
file `async_get_or_create` routes to `_async_update_entity` with no
`new_entity_id` before `entity.entity_id` is reassigned from the stored entry.
Unique IDs are untouched -- they derive from the description key and never read
a name.

Circuit-numbers mode is deliberately unchanged, guarded by a test. There the
registry `name` phase 2 sync writes is both what shows the panel's name in the
UI and what outranks the suggestion, so Recreate keeps composing from that name.
Correcting it would mean rerouting phase 2 sync, which is a product decision,
not part of this fix.

The registry lookup was the only use of the `unique_id` argument, and
`existing_entity_id` the only use of that parameter on `_construct_entity_id`;
both are gone rather than left as parameters that no longer decide anything.

Every test reloads before asserting -- asserting straight after creation
exercises the first-add path, where the suggestion is trivially current and the
bug cannot appear. The three that demonstrate the bug fail on the previous code;
the five guards pass on both.
@cayossarian
cayossarian force-pushed the fix/recreate-entity-ids branch from fb7d857 to 87c640e Compare August 23, 2026 04:19
Cut from the entity-id branch so the "Recreate entity IDs" fix gets field time
before it merges. Carries span-panel-api 3.0.1 and both adapters at 1.0.0.
Both are outward facing and neither was recorded. Written against 2.1.0, not a
beta -- the changelog describes the public release, and the steps between betas
are not what a reader is looking for.
Recreate offered 74 renames on a real panel after the previous beta: every
circuit energy sensor, `_consumed_energy` to `_energy_consumed`. Only one of the
74 was a circuit the owner had renamed, and that one was buried.

Installs predating the point where the suffix mapping reached entity ids took
their id from the descriptor name, so they carry `_consumed_energy` while their
own unique id carries `_energy_consumed`. The two have disagreed all along;
recomputing the suggestion is what made the disagreement visible.

The descriptor rename is not the cause and reverting it would not have removed a
single offer: in friendly-names mode the registry holds no `name`, so
`suggested_object_id` -- our suffix-based preset -- outranks `original_name` and
the descriptor name never reaches the id. Measured both ways; the proposal is
`_energy_consumed` either way.

So an existing entity keeps the suffix it shipped with and only the circuit-name
half of its id follows the panel. `LEGACY_ENTITY_ID_SUFFIXES` records the older
spellings; comparing with the suffix removed means a renamed circuit still gets
the computed id, which is the whole point of #252. A trailing segment that merely
looks like a suffix change -- "Kitchen Outlets" renamed to "Kitchen" -- is a
rename and is still offered.

`_construct_entity_id` takes `existing_entity_id` again. It no longer decides
whether to compute an id, only which suffix the computed one carries.

Four tests, two of which fail with preservation disabled. The earlier ones all
built their entities with current code, where preset and live id are the same
string by construction and this could not appear -- which is why it reached a
beta.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant