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.
This commit is contained in:
@@ -97,13 +97,38 @@ class NinstripConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
errors=errors,
|
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:
|
async def async_step_zeroconf(self, discovery_info: ZeroconfServiceInfo) -> ConfigFlowResult:
|
||||||
"""Gefunden ueber _ninstrip._tcp."""
|
"""Gefunden ueber _ninstrip._tcp."""
|
||||||
self._host = discovery_info.host
|
self._host = discovery_info.host
|
||||||
props = discovery_info.properties or {}
|
props = discovery_info.properties or {}
|
||||||
mac = props.get("mac")
|
mac = props.get("mac")
|
||||||
if mac:
|
if mac and (kennung := self._kennung_aus_mdns(str(mac))):
|
||||||
await self.async_set_unique_id(format_mac(str(mac)))
|
await self.async_set_unique_id(kennung)
|
||||||
self._abort_if_unique_id_configured(updates={CONF_HOST: self._host})
|
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._discovered_name = str(props.get("name") or discovery_info.name.split(".")[0])
|
||||||
self.context["title_placeholders"] = {"name": self._discovered_name}
|
self.context["title_placeholders"] = {"name": self._discovered_name}
|
||||||
|
|||||||
@@ -296,3 +296,46 @@ async def test_neu_konfigurieren_lehnt_ein_anderes_geraet_ab(
|
|||||||
assert ergebnis["type"] is FlowResultType.ABORT
|
assert ergebnis["type"] is FlowResultType.ABORT
|
||||||
assert ergebnis["reason"] == "wrong_device"
|
assert ergebnis["reason"] == "wrong_device"
|
||||||
assert eintrag.data[CONF_HOST] == HOST_K1Y
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user