Skip to content

API Reference

AtmosPyre provides a layered API architecture that separates interfaces (what you can extend) from implementations (what's provided out-of-the-box).

Understanding the API Structure

The API documentation is organized into two main sections:

Core Interfaces

These are the foundational classes that define AtmosPyre's architecture. Use these when you want to understand how the library works or when creating custom implementations.

  • Sensor - Base class for all sensor instruments. Handles Modbus RTU communication and tag-based reading.
  • SensorLogger - Automated data logging with daily file organization and metadata generation.
  • LoggerScheduler - Manages multiple loggers with pluggable scheduling backends.

Extension Interfaces

These define the extension points where you can plug in your own implementations. If you want to add support for new sensors, file formats, or schedulers, start here:

Sensor Extensions

  • ReadTag System - Type-based dispatch system for sensor measurements. Create custom tags for new measurement types.

SensorLogger Extensions

  • Writer Interface - Strategy pattern for data file formats. Implement this to add support for new file formats (e.g., HDF5, NetCDF).
  • Saver Interface - Strategy pattern for metadata persistence. Implement this for custom metadata formats (e.g., YAML, TOML).

LoggerScheduler Extensions

  • Backend Interface - Pluggable scheduler backends. Implement this to use different scheduling libraries (e.g., APScheduler, Celery).

What's the Difference?

Type Purpose When to Use
Core Interfaces Main classes you instantiate and use Building logging applications
Extension Interfaces Protocols/base classes you implement Adding new sensors, formats, or backends

Quick Start Examples

Using the API (Core Interfaces)

The core interfaces define how components interact. Here's the abstract pattern:

from atmospyre.sensors.sensor import Sensor
from atmospyre.loggers import SensorLogger
from atmospyre.scheduler import LoggerScheduler

# sensor: any Sensor implementation
# tags: list of ReadTag instances valid for that sensor
# backend_tag: scheduler backend dispatch tag

logger = SensorLogger(
    sensor=sensor,
    tags=tags,
    interval_seconds=60,
    output_path='./data'
)

scheduler = LoggerScheduler(scheduler_dispatch_tag=backend_tag)
scheduler.add_logger(logger)
scheduler.run()

Complete working example using built-in components:

# Import core interfaces
from atmospyre.loggers import SensorLogger
from atmospyre.scheduler import LoggerScheduler

# Import built-in implementations
from atmospyre.sensors.implementations.gmp252 import GMP252, CO2, TEMPERATURE
from atmospyre.scheduler.backends.schedule import ScheduleTag

# Use a built-in sensor implementation
sensor = GMP252(port='/dev/ttyUSB0', slave_address=1)

# Use the core SensorLogger interface
logger = SensorLogger(
    sensor=sensor,
    tags=[CO2, TEMPERATURE],
    interval_seconds=60,
    output_path='./data'
)

# Use the core LoggerScheduler interface
scheduler = LoggerScheduler(scheduler_dispatch_tag=ScheduleTag)
scheduler.add_logger(logger)
scheduler.run()

The core interfaces (Sensor, SensorLogger, LoggerScheduler) define the contracts. The built-in implementations (GMP252, CO2, TEMPERATURE, ScheduleTag) fulfill those contracts.

Extending the API (Extension Interfaces)

from atmospyre.sensors.read_tag.read_tag import ReadTag
from atmospyre.sensors.read_tag.metadata import ReadTagMetadata

# Create a custom measurement type
class Humidity(ReadTag):
    metadata = ReadTagMetadata(
        description="Relative humidity",
        unit="%",
        min_interval=10,
        data_type="float"
    )

# Implement the sensor-specific read function
from multipledispatch import Dispatcher
_read = Dispatcher('_read', namespace=my_sensor_namespace)

@_read.register(minimalmodbus.Instrument, Humidity)
def _(instrument: minimalmodbus.Instrument, tag: Humidity) -> float:
    return instrument.read_register(0x0103, functioncode=3) / 100.0