From d1db07f52269070b6837e7bb24d6da82cdfc84ec Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 26 Sep 2026 01:05:25 +0200 Subject: [PATCH] fix(config_flow): bekanntes Geraet auch an der verkuerzten mDNS-MAC erkennen Die Firmware wirbt im TXT-Satz nur mit den letzten vier Hex-Stellen der MAC. Daraus wurde bisher direkt eine Kennung gebaut - die auf keinen Eintrag passte. Folge: Home Assistant bot nach jedem Neustart dieselben, laengst eingerichteten Strips erneut an, und beim Hinzufuegen kam 'bereits konfiguriert'. Vier Stellen sind keine Kennung, aber ein Hinweis: endet genau ein Eintrag so, ist er gemeint. Bei mehreren bleibt es unentschieden und der Ablauf fragt nach - vier Stellen sind nicht eindeutig genug, um einen fremden Eintrag stillschweigend umzuschreiben. Die Tests sahen das nicht, weil ihr mDNS-Fixture die volle MAC eintrug - eine Welt, die es am Geraet nicht gibt. --- custom_components/ninstrip/config_flow.py | 29 +++++++++++++-- tests/test_config_flow.py | 43 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/custom_components/ninstrip/config_flow.py b/custom_components/ninstrip/config_flow.py index a6da672..ebaa1fb 100644 --- a/custom_components/ninstrip/config_flow.py +++ b/custom_components/ninstrip/config_flow.py @@ -97,13 +97,38 @@ class NinstripConfigFlow(ConfigFlow, domain=DOMAIN): errors=errors, ) + def _kennung_aus_mdns(self, roh: str) -> str | None: + """Die Kennung zu dem, was im TXT-Satz unter "mac" steht. + + Eine volle MAC ist die Kennung. Die Firmware wirbt aber nur mit den + letzten vier Hex-Stellen (`nl_wifi_mac4()`, siehe nl_mdns.c) - daraus + laesst sich keine bauen. Als *Hinweis* taugen sie trotzdem: endet + genau ein eingerichteter Eintrag so, ist er gemeint. + + Bei mehreren Treffern bleibt es unentschieden. Dann lieber nachfragen + als den falschen Eintrag stillschweigend auf eine fremde Adresse + umzuschreiben - vier Stellen sind nicht eindeutig. + """ + kennung = format_mac(roh) + if len(kennung) == 17: + return kennung + kurz = roh.replace(":", "").replace("-", "").lower() + if len(kurz) != 4: + return None + treffer = [ + eintrag.unique_id + for eintrag in self._async_current_entries(include_ignore=False) + if eintrag.unique_id and eintrag.unique_id.replace(":", "").endswith(kurz) + ] + return treffer[0] if len(treffer) == 1 else None + async def async_step_zeroconf(self, discovery_info: ZeroconfServiceInfo) -> ConfigFlowResult: """Gefunden ueber _ninstrip._tcp.""" self._host = discovery_info.host props = discovery_info.properties or {} mac = props.get("mac") - if mac: - await self.async_set_unique_id(format_mac(str(mac))) + if mac and (kennung := self._kennung_aus_mdns(str(mac))): + await self.async_set_unique_id(kennung) self._abort_if_unique_id_configured(updates={CONF_HOST: self._host}) self._discovered_name = str(props.get("name") or discovery_info.name.split(".")[0]) self.context["title_placeholders"] = {"name": self._discovered_name} diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 9928674..15a41fd 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -296,3 +296,46 @@ async def test_neu_konfigurieren_lehnt_ein_anderes_geraet_ab( assert ergebnis["type"] is FlowResultType.ABORT assert ergebnis["reason"] == "wrong_device" assert eintrag.data[CONF_HOST] == HOST_K1Y + + +async def test_mdns_mit_verkuerzter_mac_erkennt_das_bekannte_geraet( + hass: HomeAssistant, mock_client: MagicMock +) -> None: + """Die Firmware wirbt nur mit den letzten vier Hex-Stellen der MAC. + + Genau so steht es im TXT-Satz am Geraet (`mac=ad4c`). Wird daraus eine + Kennung gebaut, passt sie auf keinen Eintrag - und Home Assistant bietet + nach jedem Neustart dasselbe, laengst eingerichtete Geraet erneut an. + """ + eintrag = bestehender_eintrag(hass) + ergebnis = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_ZEROCONF}, data=zeroconf_info(mac="ad4c") + ) + assert ergebnis["type"] is FlowResultType.ABORT + assert ergebnis["reason"] == "already_configured" + assert eintrag.unique_id == MAC_K1Y + + +async def test_mdns_mit_verkuerzter_mac_bleibt_bei_zwei_treffern_unentschieden( + hass: HomeAssistant, mock_client: MagicMock +) -> None: + """Vier Hex-Stellen sind nicht eindeutig. + + Enden zwei eingerichtete Geraete gleich, darf keines davon stillschweigend + auf die neue Adresse umgeschrieben werden - dann wird gefragt. + """ + erster = bestehender_eintrag(hass, "192.168.2.5") + zweiter = MockConfigEntry( + domain=DOMAIN, + title="Zwilling", + unique_id="aa:bb:cc:dd:ad:4c", + data={CONF_HOST: "192.168.2.6", CONF_TOKEN: TOKEN}, + ) + zweiter.add_to_hass(hass) + + ergebnis = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_ZEROCONF}, data=zeroconf_info(mac="ad4c") + ) + assert ergebnis["type"] is FlowResultType.FORM + assert erster.data[CONF_HOST] == "192.168.2.5" + assert zweiter.data[CONF_HOST] == "192.168.2.6"