Skip to main content

aio-sensorthings

An asyncio Python library and SDK for OGC SensorThings API (STA) servers such as FROST-Server.

Summary

aio-sensorthings copies data from one SensorThings server to another and keeps it up to date in near real time:

  • Bootstrap (HTTP): reads Locations, Things, Sensors, ObservedProperties and Datastreams from the source in dependency order and creates them on the target. Source IDs are rewritten to target IDs, and the mapping is kept in a state store.
  • Scoped sync: limit the copy to a subgraph via an STA navigation path (ObservedProperties(5674)/Datastreams) or an OData filter. Dependencies are resolved automatically.
  • Live data (MQTT): new Observations, entity changes and newly created entities are streamed from the source's MQTT broker to the target.
  • Pluggable parts: state store (in-memory or Redis), transformers, target (STA HTTP or Kafka), and Basic/OAuth authentication. Multiple instances can share the work through a Redis-based federation coordinator.
  • Observability: optional OpenTelemetry spans and metrics (API only, no SDK dependency).

Historical Observations are not copied during bootstrap; only Observations published after the MQTT subscription starts are forwarded.

The code follows a hexagonal layout (models, ports, adapters, pipeline, config, transforms) under src/aio_sensorthings/.

Installation

Requires Python 3.13 or 3.14.

pip install "aio-sensorthings[all]"   # or pick extras: http, mqtt, redis, kafka, otel

Usage

Scoped copy of all Datastreams of one ObservedProperty from a remote server to a local FROST-Server, followed by live MQTT forwarding (state kept in Redis):

import asyncio

import redis.asyncio as aioredis

from aio_sensorthings import (
    EntitySyncer,
    IdentityTransformer,
    ProcessRunner,
    RedisStateStore,
    STAClient,
    STAEntityType,
    STAHttpSource,
    STAHttpTarget,
    STAMqttSubscriber,
    StandaloneCoordinator,
)
from aio_sensorthings.config import MqttSettings


async def main() -> None:
    redis_client = aioredis.from_url("redis://localhost:6379/0", decode_responses=True)
    mqtt = MqttSettings(broker_host="iot.hamburg.de", broker_port=1883, qos=1, clean_session=False)

    async with (
        STAClient("https://iot.hamburg.de") as source_client,
        STAClient("http://localhost:8080/FROST-Server") as target_client,
    ):
        syncer = EntitySyncer(
            source=STAHttpSource(source_client),
            target=STAHttpTarget(target_client),
            state=RedisStateStore(redis_client, key_prefix="demo"),
            transformer=IdentityTransformer(),
            entity_types=[
                STAEntityType.LOCATION,
                STAEntityType.THING,
                STAEntityType.SENSOR,
                STAEntityType.OBSERVED_PROPERTY,
                STAEntityType.DATASTREAM,
            ],
            sync_by="name",
            source_mqtt=STAMqttSubscriber(mqtt),
            coordinator=StandaloneCoordinator(),
        )

        runner = ProcessRunner(
            syncer,
            bootstrap_kwargs={
                "entry_point": STAEntityType.DATASTREAM,
                "nav_path": "ObservedProperties(5674)/Datastreams",
            },
        )
        await runner.run()  # bootstrap, then subscribe until SIGINT/SIGTERM


asyncio.run(main())

More examples (federation, OpenTelemetry) are in examples/.


Prerequisites

For development, install one of the following package managers once on your machine before proceeding:

Package manager Install
pixi (default) curl -fsSL https://pixi.sh/install.sh | bash — pixi.sh
poetry pipx install poetry — python-poetry.org

For Docker workflows you also need Docker.


Development setup

pixi

Pixi manages Python, all conda-forge and PyPI dependencies, and the virtual environment in a single tool. The environment lives at .pixi/envs/ and is not committed to git.

# Install all dependencies (creates the environment from pixi.lock)
pixi install

# Open a shell with dev tools available
pixi shell -e dev

# Or run a single task without activating the shell
pixi run -e dev test
pixi run -e dev lint
pixi run -e dev format
pixi run -e dev typecheck

Add a new dependency:

pixi add numpy                     # conda-forge, runtime
pixi add --pypi requests           # PyPI, runtime
pixi add --feature dev pytest-mock # dev-only

Update all dependencies and regenerate the lockfile:

pixi update
git add pixi.lock && git commit -m 'chore: update pixi lockfile'

Common development tasks

All tasks are available via make. Run make with no arguments to execute lock → build-image → run-local.

Command What it does
make test Run the test suite
make lint Lint with ruff
make format Auto-format with ruff
make typecheck Type-check with mypy
make lock Update the dependency lockfile
make build-image Build the Docker image
make push-registry Push the image to the container registry
make run-local Start the app locally via docker-compose
make build-docs Build the Jupyter Book documentation
make clean-docs Remove the documentation build output

Pre-commit hooks

Code quality checks run automatically on every git commit. Install the hooks once after cloning:

pre-commit install

Run all hooks manually at any time:

pre-commit run --all-files

The hooks cover: ruff (lint + auto-fix), ruff-format, mypy, and standard file hygiene (trailing whitespace, YAML validity, merge-conflict markers, large files).


Docker

Copy .env.example to .env and fill in the values (copier does this automatically on first scaffold):

cp .env.example .env
Command What it does
make build-image Builds ${CONTAINER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
make run-local Runs the image via docker-compose-dev.yaml
make push-registry Pushes to the container registry

Key Docker files:

File Purpose
Dockerfile Multi-stage build (adapts to the chosen package manager)
docker-compose-build.yaml CI/CD image build
docker-compose-dev.yaml Local dev/test run with secrets and env vars
docker-compose.yaml Production deployment spec

Project structure

├── src/aio_sensorthings/                    # importable package
├── tests/                                   # pytest test suite
├── docs/                                    # Jupyter Book documentation
├── data/                                    # local data
├── assets/                                  # images, figures
├── reports/                                 # generated reports and outputs
├── examples/                                # usage examples
├── scratch/                                 # throwaway experiments (not tested)
└── secrets/                                 # local credentials — never committed

VS Code setup

Recommended extensions (install via the Extensions panel or .vscode/extensions.json):

Extension Purpose
ms-python.python + ms-python.pylance Python language support
charliermarsh.ruff Linting and formatting (replaces black + isort)
ms-python.mypy-type-checker Type checking

Formatting on save and import sorting are pre-configured in .vscode/settings.json.


Updating the template

To pull in improvements from the project template into this project:

copier update . --trust

Copier merges template changes with your local edits. Review the diff carefully before committing.

Metadata

Release files for aio-sensorthings 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aio-sensorthings 0.5.0
File Size Uploaded
aio_sensorthings-0.5.0.tar.gz 573.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aio-sensorthings 0.5.0
File Interpreter ABI Platform
aio_sensorthings-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 645.4 kB

Release files / aio_sensorthings-0.5.0.tar.gz

Download URL aio_sensorthings-0.5.0.tar.gz
Size 573.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d5a9512f9035a33892967a3ec35694c27f7a292b45f0cca0a383be9b11ccde52
BLAKE2b-256 checksum
How to use checksums
997b0b4998b82b707b12f7ee42a72ba34e9a272b3a5f7e6fd91ab9a0f80766aa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / aio_sensorthings-0.5.0-py3-none-any.whl

Download URL aio_sensorthings-0.5.0-py3-none-any.whl
Size 72.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7f46acc1857f2bf18266a0cfdaf519a1e87cb30f712a14226e033ad4764b0843
BLAKE2b-256 checksum
How to use checksums
04f1e096454aa11a3ee3b8bf05ec36984b38a71b35cd5a67c0c4b81be3a0c4e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.8.0

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page