unique_id – Kopplung an name_prefix: Problem und Lösung¶
Zuletzt geändert am 21.03.2026
Diese Seite beschreibt, warum die Ableitung der unique_id aus dem vom Nutzer konfigurierbaren name_prefix ein strukturelles Problem darstellt, welche Auswirkungen das hat und wie eine verlustfreie Migration zur stabilen entry_id-basierten Lösung aussieht.
Das Problem¶
Aktuelle Implementierung¶
In utils.py wird die unique_id einer Entität im Legacy-Modus so gebildet:
# utils.py – generate_sensor_names() – Legacy-Modus
unique_id = f"{name_prefix_lc}_{device_prefix}_{sensor_id}"
# Beispiel: "eu08l_hp1_cop_calc"
Der name_prefix stammt direkt aus dem name-Feld der Config-Entry, das der Nutzer beim Einrichten der Integration selbst eingibt (z. B. "EU08L") und jederzeit über den Config Flow ändern kann.
Warum das ein Problem ist¶
Die unique_id ist das unveränderliche Erkennungsmerkmal einer Entität in Home Assistant. Die Entity Registry verknüpft damit:
- benutzerdefinierte Namen, Icons und Bereichszuweisungen
- Referenzen aus Automationen und Skripten
- Dashboard-Karten
- Statistik- und Historiendaten (über die
entity_id)
Grundregel: Die unique_id darf sich niemals ändern – sonst behandelt HA die Entität als neu und die alte als verwaist.
Weil name_prefix aus einer Nutzereingabe abgeleitet wird, kann er sich ändern. Damit ändert sich die unique_id – mit weitreichenden Folgen.
Auswirkungen bei Namensänderung¶
Szenario: Nutzer benennt die Integration von "EU08L" zu "Meine Wärmepumpe" um.
| Vorher | Nachher | |
|---|---|---|
name_prefix |
eu08l |
meinewärmepumpe |
unique_id |
eu08l_hp1_cop_calc |
meinewärmepumpe_hp1_cop_calc |
Da HA anhand der unique_id Kontinuität erkennt, behandelt es die umbenannten Entitäten als völlig neue Objekte:
- Neue Entitäten werden angelegt – mit den neuen
unique_id-Werten, als wären sie nie dagewesen - Alte Entitäten bleiben als Geister – in der Entity Registry, ohne zugehörigen Code
- Konflikte bei
entity_id– HA hängt_2,_3usw. an (z. B.sensor.hp1_cop_calc_2) - Automationen und Skripte brechen – alle Referenzen auf alte
entity_id-Werte sind ungültig - Dashboard-Karten zeigen „Entität nicht gefunden"
- Benutzerdefinierte Namen, Icons und Bereichszuweisungen gehen verloren
- Statistiken/Historie bleiben an der alten (nun verwaisten) Entität hängen
Das Tückische: Der Nutzer ändert nur einen Anzeigenamen und erwartet keinerlei technische Auswirkung.
Die aktuelle Notlösung in migration.py (migrate_to_legacy_names()) versucht, verwaiste Entitäten zu löschen und Duplikate zu bereinigen – das ist aber ein reaktives Band-Aid, das den Datenverlust nicht verhindert, sondern nur die Registry aufräumt.
Die Lösung: entry_id statt name_prefix¶
Warum entry_id?¶
Die entry_id ist eine vom System automatisch generierte UUID, die beim Anlegen einer Config-Entry einmalig vergeben wird und sich niemals ändert – unabhängig davon, was der Nutzer umbenennt oder rekonfiguriert.
Code-Vergleich¶
# BISHER (instabil – ändert sich mit dem Nutzernamen):
unique_id = f"{name_prefix_lc}_{device_prefix}_{sensor_id}"
# Beispiel: "eu08l_hp1_cop_calc"
# KÜNFTIG (stabil – entry_id ist unveränderliche UUID):
unique_id = f"{entry_id}_{device_prefix}_{sensor_id}"
# Beispiel: "a1b2c3d4e5f6_hp1_cop_calc"
Die entry_id ist in generate_sensor_names() als zusätzlicher Parameter zu ergänzen. Der name_prefix bleibt weiterhin für entity_id und den Anzeigenamen (name) relevant – nur die unique_id wird davon entkoppelt.
Migrationsstrategie ohne Datenverlust¶
Das zentrale Werkzeug: async_update_entity()¶
Home Assistant bietet eine API, die eine unique_id in-place umbenennt. Die Entität bleibt als dasselbe Objekt erhalten – nur ihr interner Schlüssel ändert sich:
entity_registry.async_update_entity(
entity_id, # bestehende entity_id (bleibt gleich)
unique_id=new_uid # neuer unique_id-Wert
)
Dies ist der fundamentale Unterschied zur aktuellen Strategie (async_remove → Neuanlegen), die alle verknüpften Daten unwiederbringlich verliert.
Was bei der Migration erhalten bleibt¶
| Datenschicht | Mechanismus | Ergebnis |
|---|---|---|
| Entity Registry (Namen, Icons, Bereiche) | Registry-Eintrag bleibt, nur unique_id überschrieben |
Erhalten |
| Statistiken / Historiendaten | Recorder ist an entity_id gebunden, nicht unique_id |
Erhalten |
| Automationen und Skripte | Referenzieren entity_id, nicht unique_id |
Erhalten |
| Dashboard-Karten | Referenzieren entity_id, nicht unique_id |
Erhalten |
| Benutzerdefinierte Einstellungen | Im Registry-Eintrag gespeichert, der bleibt | Erhalten |
Solange entity_id unverändert bleibt, bemerkt der Nutzer die Migration nicht.
Migrationsfunktion – Pseudocode¶
async def migrate_v9_unique_id_to_entry_id(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""
Migriert unique_ids von name_prefix-basiert auf entry_id-basiert.
Verwendet async_update_entity() – kein Datenverlust.
"""
entry_id = config_entry.entry_id
name_prefix = normalize_name_prefix(config_entry.data.get("name", ""))
entity_registry = async_get_entity_registry(hass)
registry_entries = entity_registry.entities.get_entries_for_config_entry_id(entry_id)
migrated = 0
for reg_entry in registry_entries:
old_uid = reg_entry.unique_id
# Fall 1: Legacy-Modus – unique_id beginnt mit name_prefix
if name_prefix and old_uid.startswith(name_prefix + "_"):
suffix = old_uid[len(name_prefix) + 1:] # z. B. "hp1_cop_calc"
new_uid = f"{entry_id}_{suffix}"
# Fall 2: Standard-Modus – kein name_prefix-Präfix, entry_id voranstellen
elif not old_uid.startswith(entry_id):
new_uid = f"{entry_id}_{old_uid}"
else:
continue # Bereits migriert
entity_registry.async_update_entity(
reg_entry.entity_id,
unique_id=new_uid
)
_LOGGER.info("unique_id migriert: %s → %s", old_uid, new_uid)
migrated += 1
_LOGGER.info("Migration v9 abgeschlossen: %d unique_id(s) aktualisiert", migrated)
return True
Einordnung in die bestehende Migrationsinfrastruktur¶
Die Integration verfügt über ein versioniertes Migrationssystem (MigrationVersion in const_migration.py, Dispatcher in migration.py). Die neue Migrations-Stufe fügt sich nahtlos ein:
# const_migration.py
class MigrationVersion(IntEnum):
...
REGISTER_ORDER_TERMINOLOGY = 8 # aktuell
UNIQUE_ID_DECOUPLED_FROM_NAME = 9 # neu
Die Migrationsfunktion wird im perform_structured_migration()-Dispatcher als neuer Eintrag für Version 9 registriert. Vor der Migration wird – wie bei allen bisherigen Stufen – ein Registry-Backup via create_registry_backup() erstellt.
Voraussetzungen für die Implementierung¶
generate_sensor_names()inutils.pyerhältentry_idals zusätzlichen Parameter- Alle Aufrufstellen (
sensor.py,climate.py,number.py,migration.py) werden angepasst - Die neue Migrationsfunktion wird in
migration.pyimplementiert und im Dispatcher eingehängt const_migration.pybekommt den neuen Enum-Wert
Status¶
| Aspekt | Status |
|---|---|
| Problem analysiert | Ja |
| Lösung definiert | Ja |
| Implementierung | Offen |
| Ziel-Release | nicht festgelegt |
Verwandte Analyse: integration_analysis.md (H-02)