Aufgebaut wie ha-ninlight: die Abbilder liegen unter ninstrip_firmware im Konfigurationsverzeichnis, Home Assistant liefert sie selbst unter einer einmaligen Adresse aus, und zwar genau die Bytes, die vorher geprueft wurden. Dazu firmware.py (Anhaenger, Versionsreihenfolge, WLAN-Pruefung, Katalog), http.py, urls.py und shared.py. Drei Unterschiede zu ninlight, alle aus der Firmware heraus: * Verglichen wird device_type - die Platine -, nicht product. Letzteres ist bei allen Platinen 'ninstrip'; die Abbilder unterscheiden sich aber hart, die K1Y invertiert das Datensignal und die KL-RGBC3 nicht. Firmware ohne dieses Feld bekommt gar kein Angebot. * Die WLAN-Pruefung sucht die SSID unverpackt: ein ESP32-Abbild ist nicht gzip-verpackt wie das des W600. An einem mit sdkconfig.local gebauten Abbild gegengeprueft - die SSID steht genau einmal NUL-gerahmt drin. * Der Fehlerschluessel heisst device_url_too_long statt lamp_url_too_long. Dazu das Testgeruest, das es hier bisher gar nicht gab: 204 Tests in tests_unit/ (ohne HA-Instanz) und tests/ (mit), Abdeckung 97 %, dazu scripts/check.sh als Ersatz fuer die fehlende CI. Die Nutzdaten in tests_unit/samples.py sind an der Stehlampe abgefragt. Beim Gegenpruefen per Mutation fiel auf, dass die Platinenpruefung unmittelbar vor dem Senden ungeprueft war - der Test dazu ist jetzt da und faellt, wenn man die Pruefung entfernt.
160 lines
4.6 KiB
JSON
160 lines
4.6 KiB
JSON
{
|
||
"config": {
|
||
"step": {
|
||
"user": {
|
||
"title": "Add ninstrip",
|
||
"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": {
|
||
"host": "Host",
|
||
"token": "API token"
|
||
}
|
||
},
|
||
"discovery_confirm": {
|
||
"title": "ninstrip found",
|
||
"description": "{name} at {host}. Please enter the API token.",
|
||
"data": {
|
||
"token": "API token"
|
||
}
|
||
},
|
||
"reauth_confirm": {
|
||
"title": "Renew token",
|
||
"description": "The device at {host} rejects the stored token. After a factory reset the default token applies again.",
|
||
"data": {
|
||
"token": "API token"
|
||
}
|
||
}
|
||
},
|
||
"error": {
|
||
"cannot_connect": "Device unreachable",
|
||
"invalid_auth": "Token rejected",
|
||
"not_ninstrip": "No ninstrip answering there",
|
||
"unknown": "Unexpected error"
|
||
},
|
||
"abort": {
|
||
"already_configured": "This device is already configured",
|
||
"reauth_successful": "Token renewed"
|
||
}
|
||
},
|
||
"options": {
|
||
"step": {
|
||
"init": {
|
||
"title": "Settings",
|
||
"data": {
|
||
"scan_interval": "Polling interval (seconds)",
|
||
"device_base_url": "Address devices use to reach Home Assistant"
|
||
},
|
||
"data_description": {
|
||
"device_base_url": "Only needed for firmware updates if devices cannot reach Home Assistant's internal URL over plain http, e.g. http://192.168.1.50:8123"
|
||
}
|
||
}
|
||
},
|
||
"error": {
|
||
"invalid_base_url": "Must be a plain http:// address with host and optional port, without path.",
|
||
"base_url_too_long": "Address too long – the device accepts at most 255 characters for the whole firmware URL."
|
||
}
|
||
},
|
||
"entity": {
|
||
"select": {
|
||
"preset": {
|
||
"name": "Scene"
|
||
},
|
||
"sacn_on_timeout": {
|
||
"name": "sACN on stream end",
|
||
"state": {
|
||
"release": "Return to previous image",
|
||
"hold": "Hold last image"
|
||
}
|
||
}
|
||
},
|
||
"button": {
|
||
"restart": {
|
||
"name": "Restart"
|
||
}
|
||
},
|
||
"sensor": {
|
||
"rssi": {
|
||
"name": "Wi-Fi signal"
|
||
},
|
||
"uptime": {
|
||
"name": "Uptime"
|
||
},
|
||
"free_heap": {
|
||
"name": "Free memory"
|
||
},
|
||
"power_scale": {
|
||
"name": "Power limiting"
|
||
}
|
||
},
|
||
"switch": {
|
||
"sacn_enabled": {
|
||
"name": "sACN receiver"
|
||
}
|
||
},
|
||
"number": {
|
||
"sacn_universe": {
|
||
"name": "sACN universe"
|
||
},
|
||
"sacn_start_slot": {
|
||
"name": "sACN start slot"
|
||
}
|
||
},
|
||
"binary_sensor": {
|
||
"sacn_active": {
|
||
"name": "sACN active"
|
||
},
|
||
"sacn_multicast": {
|
||
"name": "sACN multicast joined"
|
||
}
|
||
},
|
||
"update": {
|
||
"firmware": {
|
||
"name": "Firmware"
|
||
}
|
||
}
|
||
},
|
||
"exceptions": {
|
||
"auth_failed": {
|
||
"message": "The device rejected the token."
|
||
},
|
||
"device_busy": {
|
||
"message": "The device is busy - an update is probably running."
|
||
},
|
||
"request_failed": {
|
||
"message": "The device rejected the request: {message}"
|
||
},
|
||
"cannot_connect": {
|
||
"message": "The device is unreachable."
|
||
},
|
||
"update_busy": {
|
||
"message": "An update is already running on the device."
|
||
},
|
||
"no_update": {
|
||
"message": "No firmware update is available."
|
||
},
|
||
"no_http_url": {
|
||
"message": "Devices can only reach Home Assistant over plain http. Set the address in the integration options."
|
||
},
|
||
"device_url_too_long": {
|
||
"message": "The firmware URL is longer than the 255 characters the device accepts."
|
||
},
|
||
"image_invalid": {
|
||
"message": "The firmware file changed or is no longer valid."
|
||
},
|
||
"wifi_guard": {
|
||
"message": "This firmware does not contain the Wi-Fi credentials for {ssid}; the device would lose its network connection."
|
||
},
|
||
"update_failed": {
|
||
"message": "The update failed: {reason}"
|
||
},
|
||
"update_timeout": {
|
||
"message": "The device did not report the new firmware within 12 minutes."
|
||
}
|
||
},
|
||
"issues": {
|
||
"wifi_guard": {
|
||
"title": "Firmware for {name} not offered",
|
||
"description": "The firmware folder contains {version} for {device_type}, but without Wi-Fi credentials for {ssid}. The device only knows its Wi-Fi from its firmware and would lose its network connection after this update, so the update is not offered. A device that was put on the network through onboarding instead gets the update without this reservation."
|
||
}
|
||
}
|
||
}
|