OTA-Update per MQTT
Neues Kommando <PREFIX>/cmd/ota loest ota.update() aus: laedt die Projektdateien per HTTP, erst als temporaere .ota-Dateien, und benennt sie nur bei vollem Erfolg um. Schlaegt ein Download fehl, bleiben die bisherigen Dateien unveraendert -- die Anzeige laeuft mit der alten Version weiter (Grundsatz wie beim Rest von MQTT: eine fehlgeschlagene OTA darf das Geraet nicht unbrauchbar machen). Am Gerät bestätigt, inkl. echtem Reboot: leere/ungueltige Payload, unbekanntes Kommando und nicht erreichbarer Server werden abgefangen, die Poll-Schleife laeuft unbeeinflusst weiter. Ein echtes Update mit allen Projektdateien kam sauber ueber main.py-Autostart wieder hoch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zTSXLK4w32PoCmFgSzp68
This commit is contained in:
+50
-1
@@ -111,7 +111,7 @@ class TestVerbinden(MqttTest):
|
||||
def test_connect_abonniert_und_meldet_online(self):
|
||||
self.assertTrue(self.bridge.connect())
|
||||
self.assertTrue(self.bridge.connected)
|
||||
self.assertEqual(self.client.subscribed, ["ga/set/+"])
|
||||
self.assertEqual(self.client.subscribed, ["ga/set/+", "ga/cmd/+"])
|
||||
self.assertIn(("ga/status/online", b"1", True), self.client.published)
|
||||
|
||||
def test_last_will_meldet_offline(self):
|
||||
@@ -537,5 +537,54 @@ class TestSchleifenanbindung(MqttTest):
|
||||
self.assertEqual(gezeigt[0], "RA 18h36.9m")
|
||||
|
||||
|
||||
class TestOta(MqttTest):
|
||||
"""cmd/ota loest den injizierten on_ota-Rueckruf aus (siehe ota.py,
|
||||
run_esp32.make_on_ota). Die eigentliche OTA-Logik wird hier nicht
|
||||
getestet -- nur, dass mqtt.py sie zuverlaessig anstoesst, ohne dabei die
|
||||
Anzeige mitzureissen."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.ota_aufrufe = []
|
||||
self.bridge._on_ota = self.ota_aufrufe.append
|
||||
self.bridge.connect()
|
||||
|
||||
def test_cmd_topic_wird_abonniert(self):
|
||||
self.assertIn("ga/cmd/+", self.client.subscribed)
|
||||
|
||||
def test_ota_wird_mit_payload_ausgeloest(self):
|
||||
self.sende("ga/cmd/ota", "http://192.168.1.50:8000")
|
||||
self.assertEqual(self.ota_aufrufe, ["http://192.168.1.50:8000"])
|
||||
|
||||
def test_ota_ohne_payload_wird_trotzdem_ausgeloest(self):
|
||||
"""Leere Nachricht heisst: config.OTA_URL gilt (make_on_ota)."""
|
||||
self.sende("ga/cmd/ota", "")
|
||||
self.assertEqual(self.ota_aufrufe, [""])
|
||||
|
||||
def test_unbekanntes_kommando_wird_verworfen(self):
|
||||
self.sende("ga/cmd/reboot", "")
|
||||
self.assertEqual(self.ota_aufrufe, [])
|
||||
self.assertTrue(any("unbekanntes Kommando" in m for m in self.meldungen))
|
||||
|
||||
def test_ohne_handler_wird_nur_protokolliert(self):
|
||||
self.bridge._on_ota = None
|
||||
self.sende("ga/cmd/ota", "http://x")
|
||||
self.assertTrue(any("kein Handler" in m for m in self.meldungen))
|
||||
|
||||
def test_kaputter_on_ota_bricht_nichts_ab(self):
|
||||
"""Ein Fehler beim OTA-Anstossen darf den Empfang nicht toeten."""
|
||||
def kaputt(_):
|
||||
raise RuntimeError("absichtlich")
|
||||
self.bridge._on_ota = kaputt
|
||||
self.sende("ga/cmd/ota", "http://x") # darf nicht werfen
|
||||
self.assertTrue(any("OTA fehlgeschlagen" in m for m in self.meldungen))
|
||||
|
||||
def test_set_topics_bleiben_unberuehrt(self):
|
||||
"""Das gruppenbasierte Routing (set/cmd) darf set/ nicht veraendern."""
|
||||
self.sende("ga/set/hell_prozent", "60")
|
||||
self.assertEqual(settings.load(self.pfad)["hell_prozent"], 60)
|
||||
self.assertEqual(self.ota_aufrufe, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
|
||||
Reference in New Issue
Block a user