Skip to content

Architecture

Py ToloMEO organises microservice logic into four layers — service, plugin, messaging, and task. Each layer has a single responsibility and communicates with adjacent layers through well-defined interfaces.

The diagram below gives the high-level picture: how an inbound command travels from the ToloMEO cloud down to a plugin, and how outbound data and events travel back up to the cloud.

Service & plugin architecture — light mode Service & plugin architecture — dark mode

A service is configured declaratively through an inner Meta class — its plugin class, messaging strategy, and commands. These settings are assembled across the whole class hierarchy at class-definition time, so subclasses extend behaviour by declaration rather than by overriding.

Service Layer

The service (built on ServiceBase / NATSService) is the top-level orchestrator and owns four collaborators: the messaging strategy, a command registry, a plugin registry, and a task manager. It:

  • Subscribes to commands.{name}.req and decodes inbound commands
  • Runs a service-level command if one is registered, otherwise routes the command to the plugin identified by the payload's id
  • Publishes command responses to events.params, sensor data to events.data, and OTA/status events to events.infos
  • Publishes a heartbeat to heartbeat.{name}.service; sensor data and info arrive via push — plugins call push_reading/push_info, which invoke a callback the service publishes from. There is no polling loop.

A service holds one plugin class but many plugin instances, keyed by plugin ID. SenML records are built in this layer before publishing — the messaging layer never sees SenML.

Plugin Layer

Plugins encapsulate domain logic and are decoupled from the transport. The two built-in base classes are SensorPlugin (for continuous metric publishing) and OTAPlugin (for firmware update management). Each plugin:

  • Declares its commands, input metrics, and output metrics in a Meta class (output metrics are namespaced with the plugin ID)
  • Manages its own connection lifecycle to hardware or external resources
  • Runs background tasks (e.g. sensor read loops) on its own task manager and pushes readings to the service through a data-available callback — delivery is entirely push-driven, with no polling on the service side

Messaging Layer

NATSMessageStrategy wraps the nats-py client and is pure transport: it serialises payloads to JSON and publishes or subscribes on NATS subjects. It performs no SenML encoding — that belongs to the service layer. The service interacts with it only through the MessageStrategyABC interface. Swapping the strategy class in Meta is the only change needed to use a different transport.

ToloMEO Platform Context

A deployed Py ToloMEO microservice runs on an edge device. It connects to the ToloMEO cloud platform through the connhex-edge-agent (a NATS proxy). The platform sends commands inbound (operator triggers an OTA update) and receives data outbound (sensor readings, OTA status events). Py ToloMEO handles the edge side of this contract.