feat: Werks-Token im Einrichtungsdialog vorbelegen

Die Firmware antwortet im Auslieferungszustand und nach jedem Werksreset mit
dem oeffentlichen Werks-Token aabbccddeeff0011223344 - genau wie ninlight.
Wie ha-ninlight belegt die Integration es deshalb im Dialog vor, beim
manuellen Einrichten, beim Fund per mDNS und in den Texten zur
Token-Erneuerung.
This commit is contained in:
ninharp
2026-09-22 17:48:53 +02:00
parent adec66a6a2
commit 5ac410a416
5 changed files with 109 additions and 37 deletions
+4 -3
View File
@@ -23,6 +23,7 @@ from .api import NinstripAuthError, NinstripClient, NinstripError, NinstripRespo
from .const import ( from .const import (
CONF_SCAN_INTERVAL, CONF_SCAN_INTERVAL,
DEFAULT_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
DEFAULT_TOKEN,
DOMAIN, DOMAIN,
MAX_SCAN_INTERVAL, MAX_SCAN_INTERVAL,
MIN_SCAN_INTERVAL, MIN_SCAN_INTERVAL,
@@ -94,7 +95,7 @@ class NinstripConfigFlow(ConfigFlow, domain=DOMAIN):
data_schema=vol.Schema( data_schema=vol.Schema(
{ {
vol.Required(CONF_HOST, default=self._host): str, vol.Required(CONF_HOST, default=self._host): str,
vol.Required(CONF_TOKEN): str, vol.Required(CONF_TOKEN, default=DEFAULT_TOKEN): str,
} }
), ),
errors=errors, errors=errors,
@@ -117,7 +118,7 @@ class NinstripConfigFlow(ConfigFlow, domain=DOMAIN):
async def async_step_discovery_confirm( async def async_step_discovery_confirm(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Das Token steht einmalig auf der seriellen Konsole - es muss von Hand herein.""" """Vorbelegt mit dem Werks-Token - ein frisch onboardetes Geraet antwortet damit."""
errors: dict[str, str] = {} errors: dict[str, str] = {}
if user_input is not None: if user_input is not None:
info, errors = await self._async_probe(self._host, user_input[CONF_TOKEN]) info, errors = await self._async_probe(self._host, user_input[CONF_TOKEN])
@@ -125,7 +126,7 @@ class NinstripConfigFlow(ConfigFlow, domain=DOMAIN):
return await self._async_finish(self._host, user_input[CONF_TOKEN], info) return await self._async_finish(self._host, user_input[CONF_TOKEN], info)
return self.async_show_form( return self.async_show_form(
step_id="discovery_confirm", step_id="discovery_confirm",
data_schema=vol.Schema({vol.Required(CONF_TOKEN): str}), data_schema=vol.Schema({vol.Required(CONF_TOKEN, default=DEFAULT_TOKEN): str}),
description_placeholders={"name": self._discovered_name, "host": self._host}, description_placeholders={"name": self._discovered_name, "host": self._host},
errors=errors, errors=errors,
) )
+5
View File
@@ -6,6 +6,11 @@ from typing import Final
DOMAIN: Final = "ninstrip" DOMAIN: Final = "ninstrip"
# Oeffentliches Werks-Token der Firmware (NL_DEFAULT_TOKEN), dasselbe wie bei
# ninlight. Ein Geraet im Auslieferungszustand und nach jedem Werksreset
# antwortet damit - deshalb belegt der Einrichtungsdialog es vor.
DEFAULT_TOKEN: Final = "aabbccddeeff0011223344"
CONF_SCAN_INTERVAL: Final = "scan_interval" CONF_SCAN_INTERVAL: Final = "scan_interval"
DEFAULT_PORT: Final = 3333 DEFAULT_PORT: Final = 3333
+49 -16
View File
@@ -3,18 +3,25 @@
"step": { "step": {
"user": { "user": {
"title": "ninstrip hinzufügen", "title": "ninstrip hinzufügen",
"description": "Adresse und API-Token des Geräts. Das Token wird beim ersten Start einmalig auf der seriellen Konsole ausgegeben; ein Werksreset erzeugt ein neues.", "description": "Adresse und API-Token des Geräts. Im Auslieferungszustand und nach einem Werksreset gilt das Werks-Token, das bereits eingetragen ist. Wer ein eigenes gesetzt hat, trägt es hier ein.",
"data": { "host": "Adresse", "token": "API-Token" } "data": {
"host": "Adresse",
"token": "API-Token"
}
}, },
"discovery_confirm": { "discovery_confirm": {
"title": "ninstrip gefunden", "title": "ninstrip gefunden",
"description": "{name} unter {host}. Bitte das API-Token eingeben.", "description": "{name} unter {host}. Bitte das API-Token eingeben.",
"data": { "token": "API-Token" } "data": {
"token": "API-Token"
}
}, },
"reauth_confirm": { "reauth_confirm": {
"title": "Token erneuern", "title": "Token erneuern",
"description": "Das Gerät unter {host} weist das gespeicherte Token ab. Nach einem Werksreset hat es ein neues.", "description": "Das Gerät unter {host} weist das gespeicherte Token ab. Nach einem Werksreset gilt wieder das Werks-Token.",
"data": { "token": "API-Token" } "data": {
"token": "API-Token"
}
} }
}, },
"error": { "error": {
@@ -32,24 +39,50 @@
"step": { "step": {
"init": { "init": {
"title": "Einstellungen", "title": "Einstellungen",
"data": { "scan_interval": "Abfrageabstand (Sekunden)" } "data": {
"scan_interval": "Abfrageabstand (Sekunden)"
}
} }
} }
}, },
"entity": { "entity": {
"select": { "preset": { "name": "Szene" } }, "select": {
"button": { "restart": { "name": "Neu starten" } }, "preset": {
"name": "Szene"
}
},
"button": {
"restart": {
"name": "Neu starten"
}
},
"sensor": { "sensor": {
"rssi": { "name": "WLAN-Signal" }, "rssi": {
"uptime": { "name": "Laufzeit" }, "name": "WLAN-Signal"
"free_heap": { "name": "Freier Speicher" }, },
"power_scale": { "name": "Leistungsbegrenzung" } "uptime": {
"name": "Laufzeit"
},
"free_heap": {
"name": "Freier Speicher"
},
"power_scale": {
"name": "Leistungsbegrenzung"
}
} }
}, },
"exceptions": { "exceptions": {
"auth_failed": { "message": "Das Gerät hat das Token abgelehnt." }, "auth_failed": {
"device_busy": { "message": "Das Gerät ist beschäftigt - vermutlich läuft eine Aktualisierung." }, "message": "Das Gerät hat das Token abgelehnt."
"request_failed": { "message": "Das Gerät hat die Anfrage abgewiesen: {message}" }, },
"cannot_connect": { "message": "Das Gerät ist nicht erreichbar." } "device_busy": {
"message": "Das Gerät ist beschäftigt - vermutlich läuft eine Aktualisierung."
},
"request_failed": {
"message": "Das Gerät hat die Anfrage abgewiesen: {message}"
},
"cannot_connect": {
"message": "Das Gerät ist nicht erreichbar."
}
} }
} }
+49 -16
View File
@@ -3,18 +3,25 @@
"step": { "step": {
"user": { "user": {
"title": "ninstrip hinzufügen", "title": "ninstrip hinzufügen",
"description": "Adresse und API-Token des Geräts. Das Token wird beim ersten Start einmalig auf der seriellen Konsole ausgegeben; ein Werksreset erzeugt ein neues.", "description": "Adresse und API-Token des Geräts. Im Auslieferungszustand und nach einem Werksreset gilt das Werks-Token, das bereits eingetragen ist. Wer ein eigenes gesetzt hat, trägt es hier ein.",
"data": { "host": "Adresse", "token": "API-Token" } "data": {
"host": "Adresse",
"token": "API-Token"
}
}, },
"discovery_confirm": { "discovery_confirm": {
"title": "ninstrip gefunden", "title": "ninstrip gefunden",
"description": "{name} unter {host}. Bitte das API-Token eingeben.", "description": "{name} unter {host}. Bitte das API-Token eingeben.",
"data": { "token": "API-Token" } "data": {
"token": "API-Token"
}
}, },
"reauth_confirm": { "reauth_confirm": {
"title": "Token erneuern", "title": "Token erneuern",
"description": "Das Gerät unter {host} weist das gespeicherte Token ab. Nach einem Werksreset hat es ein neues.", "description": "Das Gerät unter {host} weist das gespeicherte Token ab. Nach einem Werksreset gilt wieder das Werks-Token.",
"data": { "token": "API-Token" } "data": {
"token": "API-Token"
}
} }
}, },
"error": { "error": {
@@ -32,24 +39,50 @@
"step": { "step": {
"init": { "init": {
"title": "Einstellungen", "title": "Einstellungen",
"data": { "scan_interval": "Abfrageabstand (Sekunden)" } "data": {
"scan_interval": "Abfrageabstand (Sekunden)"
}
} }
} }
}, },
"entity": { "entity": {
"select": { "preset": { "name": "Szene" } }, "select": {
"button": { "restart": { "name": "Neu starten" } }, "preset": {
"name": "Szene"
}
},
"button": {
"restart": {
"name": "Neu starten"
}
},
"sensor": { "sensor": {
"rssi": { "name": "WLAN-Signal" }, "rssi": {
"uptime": { "name": "Laufzeit" }, "name": "WLAN-Signal"
"free_heap": { "name": "Freier Speicher" }, },
"power_scale": { "name": "Leistungsbegrenzung" } "uptime": {
"name": "Laufzeit"
},
"free_heap": {
"name": "Freier Speicher"
},
"power_scale": {
"name": "Leistungsbegrenzung"
}
} }
}, },
"exceptions": { "exceptions": {
"auth_failed": { "message": "Das Gerät hat das Token abgelehnt." }, "auth_failed": {
"device_busy": { "message": "Das Gerät ist beschäftigt - vermutlich läuft eine Aktualisierung." }, "message": "Das Gerät hat das Token abgelehnt."
"request_failed": { "message": "Das Gerät hat die Anfrage abgewiesen: {message}" }, },
"cannot_connect": { "message": "Das Gerät ist nicht erreichbar." } "device_busy": {
"message": "Das Gerät ist beschäftigt - vermutlich läuft eine Aktualisierung."
},
"request_failed": {
"message": "Das Gerät hat die Anfrage abgewiesen: {message}"
},
"cannot_connect": {
"message": "Das Gerät ist nicht erreichbar."
}
} }
} }
@@ -3,7 +3,7 @@
"step": { "step": {
"user": { "user": {
"title": "Add ninstrip", "title": "Add ninstrip",
"description": "Host and API token of the device. The token is printed once on the serial console at first boot; a factory reset creates a new one.", "description": "Host and API token of the device. A factory-new or factory-reset device uses the default token, which is prefilled. If you set your own, enter it here.",
"data": { "data": {
"host": "Host", "host": "Host",
"token": "API token" "token": "API token"
@@ -18,7 +18,7 @@
}, },
"reauth_confirm": { "reauth_confirm": {
"title": "Renew token", "title": "Renew token",
"description": "The device at {host} rejects the stored token. After a factory reset it has a new one.", "description": "The device at {host} rejects the stored token. After a factory reset the default token applies again.",
"data": { "data": {
"token": "API token" "token": "API token"
} }