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:
+57
-2
@@ -460,6 +460,61 @@ class TestWatchdogInDerSchleife(unittest.TestCase):
|
||||
self.assertEqual(len(self._lauf(Client())), 1)
|
||||
|
||||
|
||||
class TestOnOta(unittest.TestCase):
|
||||
"""run_esp32.make_on_ota: baut den MQTT-Rueckruf fuer cmd/ota.
|
||||
|
||||
ota.update() selbst wird hier durch eine Attrappe ersetzt -- getestet
|
||||
wird nur, ob make_on_ota die richtige URL und den Watchdog-Feed
|
||||
durchreicht, nicht der Download (siehe test_ota.py dafuer).
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
import ota
|
||||
import run_esp32
|
||||
self.ota = ota
|
||||
self.run_esp32 = run_esp32
|
||||
self.aufrufe = []
|
||||
|
||||
def fake_update(url, files=None, log=None, feed=None, getter=None):
|
||||
self.aufrufe.append((url, feed))
|
||||
|
||||
self._alt_update = ota.update
|
||||
ota.update = fake_update
|
||||
self.addCleanup(lambda: setattr(ota, "update", self._alt_update))
|
||||
|
||||
import config
|
||||
self._alt_ota_url = getattr(config, "OTA_URL", "")
|
||||
self.addCleanup(lambda: setattr(config, "OTA_URL", self._alt_ota_url))
|
||||
self.config = config
|
||||
|
||||
def test_payload_wird_als_url_verwendet(self):
|
||||
ausloesen = self.run_esp32.make_on_ota()
|
||||
ausloesen("http://192.168.1.50:8000")
|
||||
self.assertEqual(self.aufrufe, [("http://192.168.1.50:8000", None)])
|
||||
|
||||
def test_leere_payload_faellt_auf_config_ota_url_zurueck(self):
|
||||
"""Genau wie set/mount_host: leer heisst "die Vorgabe aus config.py"."""
|
||||
self.config.OTA_URL = "http://vorgabe:8000"
|
||||
ausloesen = self.run_esp32.make_on_ota()
|
||||
ausloesen("")
|
||||
self.assertEqual(self.aufrufe, [("http://vorgabe:8000", None)])
|
||||
|
||||
def test_ohne_jede_url_wird_nichts_ausgeloest(self):
|
||||
self.config.OTA_URL = ""
|
||||
meldungen = []
|
||||
ausloesen = self.run_esp32.make_on_ota(log=meldungen.append)
|
||||
ausloesen("")
|
||||
self.assertEqual(self.aufrufe, [])
|
||||
self.assertTrue(any("keine URL" in m for m in meldungen))
|
||||
|
||||
def test_feed_wird_durchgereicht(self):
|
||||
"""Ein Update ueber viele Dateien darf den Watchdog nicht ausloesen."""
|
||||
feed = lambda: None
|
||||
ausloesen = self.run_esp32.make_on_ota(feed=feed)
|
||||
ausloesen("http://x")
|
||||
self.assertIs(self.aufrufe[0][1], feed)
|
||||
|
||||
|
||||
class TestLdrLeser(unittest.TestCase):
|
||||
"""run_esp32.make_ldr_reader: ADC lesen, auf 12 bit bringen, spiegeln."""
|
||||
|
||||
@@ -557,8 +612,8 @@ class TestWifi(unittest.TestCase):
|
||||
DEPLOYED = (
|
||||
"ticks.py", "migra.py", "config.py", "settings.py", "transport.py",
|
||||
"display.py", "coords.py", "lx200.py", "mount_client.py", "brightness.py",
|
||||
"displayloop.py", "wifi.py", "mqtt.py", "run_esp32.py", "probe.py",
|
||||
"main.py",
|
||||
"displayloop.py", "wifi.py", "mqtt.py", "ota.py", "run_esp32.py",
|
||||
"probe.py", "main.py",
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user