Add OTA Update Support¶
This tutorial extends a running Py ToloMEO service with OTA update capability. By the end you will have a service that accepts OTA commands, manages a download task, and transitions through the OTA state machine.
What you will do
Build a service with an OTAPlugin that responds to NotifyOTAUpdate,
StartOTADownload, and InstallOTAUpdate commands, manages a download
task, and transitions through the OTA state machine.
Source reference: examples/ota_mock/
Prerequisites
- A working service (see Build Your First Service) or a fresh project
- Py ToloMEO installed with all dependencies
1. Subclass OTAPlugin¶
OTAPlugin handles the download lifecycle automatically. It is abstract, so a
concrete plugin must implement four methods:
| Method | Purpose |
|---|---|
connect |
Assign self.id; reach whatever performs the flash |
disconnect |
Release that connection |
install_update |
Perform the actual firmware flash |
monitor_update |
Track progress and report success or failure |
Leaving connect or disconnect out makes the class uninstantiable, with a
TypeError naming the one you missed.
Create my_service/ota_plugin.py:
import asyncio
import logging
from tolomeo.plugins.ota import OTAPlugin
class MyOTAPlugin(OTAPlugin):
async def connect(self) -> bool:
self.id = "swupdate"
self._logger.info("OTA plugin connected")
return True
async def disconnect(self) -> bool:
return True
async def install_update(self) -> None:
"""Trigger the actual firmware install and start monitoring."""
self._logger.info("Starting installation from %s", self.fsm.pending_update.download_path)
self.fsm.install()
# Start a task to monitor progress
await self.task_manager.add_task("monitor_update", self.monitor_update, auto_restart=False)
async def monitor_update(self) -> None:
"""Simulate or perform the installation and report success/failure."""
self._logger.info("Monitoring update in state: %s", self.state)
# Replace the sleep + fsm.update_success() with real install logic
await asyncio.sleep(5)
self.fsm.update_success()
self._logger.info("Installation succeeded")
2. Wire the Plugin into a Service¶
Create my_service/ota_service.py:
from tolomeo.services.ota import OTAService
from .ota_plugin import MyOTAPlugin
class MyOTAService(OTAService):
class Meta:
plugin_class = MyOTAPlugin
3. Choose Where Files Land¶
OTAPlugin declares OTAOptions as its Meta.config_model, so your subclass
inherits three keys without writing any parsing. Put them in the service's
configuration file, keyed by the plugin id set in connect:
[plugins.swupdate]
download_dir = "/data/firmware"
download_filename = "image.swu"
checkpoint_file = "/data/firmware/ota-state.yml"
All three are optional and default to /data/tolomeo/… (or the system temp
directory when /data does not exist), so a deployment shipping no file behaves
as before.
If your plugin needs options of its own, extend the inherited model rather than replacing it:
from pathlib import Path
from tolomeo.plugins.ota.models import OTAOptions
class MyOTAOptions(OTAOptions):
socket_path: Path = Path("/run/swupdate.sock")
class MyOTAPlugin(OTAPlugin):
class Meta:
config_model = MyOTAOptions
Replacing it with an unrelated model raises TypeError at import time:
OTAPlugin itself reads download_dir, so the fields it relies on have to
survive.
4. Wire the Entry Point¶
Create my_service/ota_main.py:
import asyncio
import logging
from .ota_service import MyOTAService
logging.basicConfig(level=logging.INFO)
async def main() -> None:
service = MyOTAService("swupdate")
try:
await service.run()
except asyncio.CancelledError:
pass
if __name__ == "__main__":
asyncio.run(main())
5. Run the Service¶
6. Walk Through the Update Flow¶
Open a NATS subscriber to watch status events:
Step 1 — Notify an update¶
Send NotifyOTAUpdate with the firmware URL. Status transitions to update_available.
Step 2 — Start the download¶
Send StartOTADownload. Status transitions: downloading → update_ready.
Step 3 — Install¶
Send InstallOTAUpdate. Status transitions: updating → update_successful.
For the exact NATS command syntax and full payload schemas, see Trigger OTA Updates via NATS.
7. Handle a Download Failure¶
If the download fails (bad URL, network error), the FSM moves to download_failed.
Send AbortOTAUpdate to return to update_available and retry.
See Recover from Failures for the full command.
Next Steps¶
- OTA State Machine reference — all states and transitions
- OTA Mechanism explanation — checkpoint persistence and power-loss recovery