Skip to content

Metrics and SenML

Python API reference for the tolomeo.metrics and tolomeo.senml modules.

A Metric describes one value a plugin publishes; SenMLRecordBuilder turns metric values into the SenML records sent over NATS. For the wire format, see SenML Format.


Metric

tolomeo.metrics.Metric dataclass

A single metric definition used to describe sensor data.

Metrics are declared in a plugin's Meta.out_metrics or Meta.in_metrics. They define the shape of data the plugin produces or consumes.

Attributes:

Name Type Description
name str

Human-readable metric name (e.g. "temperature").

unit str

Unit of measure (e.g. "°C"). Use an empty string when the metric is dimensionless.

type MetricType

MetricType — TEMPORAL for instantaneous readings, INCREMENTAL for cumulative counters.

data_type MetricDataType

MetricDataType — NUMBER, STRING, or BOOLEAN.

id str

Unique identifier, equal to name. Set automatically via __post_init__.

Source code in tolomeo/metrics.py
@dataclass
class Metric:
    """A single metric definition used to describe sensor data.

    Metrics are declared in a plugin's ``Meta.out_metrics`` or
    ``Meta.in_metrics``. They define the shape of data the plugin
    produces or consumes.

    Attributes:
        name: Human-readable metric name (e.g. ``"temperature"``).
        unit: Unit of measure (e.g. ``"°C"``). Use an empty string when
            the metric is dimensionless.
        type: `MetricType` — ``TEMPORAL`` for instantaneous readings,
            ``INCREMENTAL`` for cumulative counters.
        data_type: `MetricDataType` — ``NUMBER``, ``STRING``, or ``BOOLEAN``.
        id: Unique identifier, equal to ``name``. Set automatically
            via ``__post_init__``.

    """

    id: str = field(init=False, repr=False)
    name: str
    unit: str
    type: MetricType = field(default=MetricType.TEMPORAL)
    data_type: MetricDataType = field(default=MetricDataType.NUMBER)

    def __post_init__(self) -> None:
        self.id = self.name  # short_hash(self.name)

    # --- Concrete Methods ---

    @staticmethod
    def custom_dict(kv_pairs: Any) -> Dict:
        """Drop private fields when a metric is rendered as a dict.

        Args:
            kv_pairs: The field name/value pairs, as passed by `asdict`.

        Returns:
            The pairs whose name does not start with an underscore.

        """
        return {k: v for k, v in kv_pairs if not k.startswith("_")}

    def as_dict(self) -> Dict:
        """Render the metric as a plain dict, without its private fields."""
        return asdict(self, dict_factory=self.custom_dict)

as_dict()

Render the metric as a plain dict, without its private fields.

Source code in tolomeo/metrics.py
def as_dict(self) -> Dict:
    """Render the metric as a plain dict, without its private fields."""
    return asdict(self, dict_factory=self.custom_dict)

custom_dict(kv_pairs) staticmethod

Drop private fields when a metric is rendered as a dict.

Parameters:

Name Type Description Default
kv_pairs Any

The field name/value pairs, as passed by asdict.

required

Returns:

Type Description
Dict

The pairs whose name does not start with an underscore.

Source code in tolomeo/metrics.py
@staticmethod
def custom_dict(kv_pairs: Any) -> Dict:
    """Drop private fields when a metric is rendered as a dict.

    Args:
        kv_pairs: The field name/value pairs, as passed by `asdict`.

    Returns:
        The pairs whose name does not start with an underscore.

    """
    return {k: v for k, v in kv_pairs if not k.startswith("_")}

tolomeo.metrics.MetricType

Bases: IntEnum

Wire representation of how a metric's values accumulate.

Source code in tolomeo/metrics.py
class MetricType(IntEnum):
    """Wire representation of how a metric's values accumulate."""

    TEMPORAL = 0
    INCREMENTAL = auto()

tolomeo.metrics.MetricDataType

Bases: IntEnum

Wire representation of a metric's value type.

Source code in tolomeo/metrics.py
class MetricDataType(IntEnum):
    """Wire representation of a metric's value type."""

    STRING = 0
    NUMBER = auto()
    BOOLEAN = auto()

SenML

tolomeo.senml.SenMLRecord dataclass

A single SenML record representing one measurement or command.

Follows the SenML (Sensor Measurement Lists) JSON format defined in RFC 8428. Only the fields relevant to the ToloMEO protocol are used.

Attributes:

Name Type Description
bn str

Base name — identifies the source device or service.

n str

Name — identifies the specific measurement or command.

t float | None

Timestamp as a Unix epoch float (optional).

u str | None

Unit of measure (optional, e.g. "°C").

v float | None

Numeric value (optional).

vs str | None

String value — used for JSON-encoded command payloads (optional).

Source code in tolomeo/senml.py
@dataclass
class SenMLRecord:
    """A single SenML record representing one measurement or command.

    Follows the SenML (Sensor Measurement Lists) JSON format defined in
    RFC 8428. Only the fields relevant to the ToloMEO protocol are used.

    Attributes:
        bn: Base name — identifies the source device or service.
        n: Name — identifies the specific measurement or command.
        t: Timestamp as a Unix epoch float (optional).
        u: Unit of measure (optional, e.g. ``"°C"``).
        v: Numeric value (optional).
        vs: String value — used for JSON-encoded command payloads (optional).

    """

    bn: str
    n: str
    t: float | None = None
    u: str | None = None
    v: float | None = None
    vs: str | None = None

    # --- Concrete Methods ---

    @staticmethod
    def custom_dict(kv_pairs: Any) -> Dict:
        """Drop unset fields when a record is rendered as a dict.

        Args:
            kv_pairs: The field name/value pairs, as passed by `asdict`.

        Returns:
            The pairs whose value is not None. SenML omits absent fields
            rather than sending them as nulls.

        """
        return {k: v for k, v in kv_pairs if v is not None}

    def as_dict(self) -> Dict:
        """Render the record as a plain dict, omitting its unset fields."""
        return asdict(self, dict_factory=self.custom_dict)

as_dict()

Render the record as a plain dict, omitting its unset fields.

Source code in tolomeo/senml.py
def as_dict(self) -> Dict:
    """Render the record as a plain dict, omitting its unset fields."""
    return asdict(self, dict_factory=self.custom_dict)

custom_dict(kv_pairs) staticmethod

Drop unset fields when a record is rendered as a dict.

Parameters:

Name Type Description Default
kv_pairs Any

The field name/value pairs, as passed by asdict.

required

Returns:

Type Description
Dict

The pairs whose value is not None. SenML omits absent fields

Dict

rather than sending them as nulls.

Source code in tolomeo/senml.py
@staticmethod
def custom_dict(kv_pairs: Any) -> Dict:
    """Drop unset fields when a record is rendered as a dict.

    Args:
        kv_pairs: The field name/value pairs, as passed by `asdict`.

    Returns:
        The pairs whose value is not None. SenML omits absent fields
        rather than sending them as nulls.

    """
    return {k: v for k, v in kv_pairs if v is not None}

tolomeo.senml.SenMLRecordBuilder

Builds a single SenMLRecord from raw data using a configurable factory.

Parameters:

Name Type Description Default
base_name str

The bn field value for all records built by this instance.

required
factory_class Type[SenMLRecordFactory]

The SenMLRecordFactory subclass used to construct records.

required
Source code in tolomeo/senml.py
class SenMLRecordBuilder:
    """Builds a single `SenMLRecord` from raw data using a configurable factory.

    Args:
        base_name: The ``bn`` field value for all records built by this instance.
        factory_class: The `SenMLRecordFactory` subclass used to construct records.

    """

    def __init__(self, base_name: str, factory_class: Type[SenMLRecordFactory]) -> None:
        self._base_name = base_name
        self._factory_class = factory_class

    # --- Concrete Methods ---

    def build(self, data: Any) -> SenMLRecord:
        """Build one `SenMLRecord` from ``data`` using the configured factory.

        Args:
            data: Raw input passed to ``factory_class.create``.

        Returns:
            A fully constructed `SenMLRecord`.

        """
        return self._factory_class.create(self._base_name, data)

build(data)

Build one SenMLRecord from data using the configured factory.

Parameters:

Name Type Description Default
data Any

Raw input passed to factory_class.create.

required

Returns:

Type Description
SenMLRecord

A fully constructed SenMLRecord.

Source code in tolomeo/senml.py
def build(self, data: Any) -> SenMLRecord:
    """Build one `SenMLRecord` from ``data`` using the configured factory.

    Args:
        data: Raw input passed to ``factory_class.create``.

    Returns:
        A fully constructed `SenMLRecord`.

    """
    return self._factory_class.create(self._base_name, data)

Errors

tolomeo.exceptions.MetricNotFoundError

Bases: ValueError

Base exception raised when a metric is not found.

Source code in tolomeo/exceptions.py
class MetricNotFoundError(ValueError):
    """Base exception raised when a metric is not found."""

    ...

tolomeo.exceptions.DuplicateMetricError

Bases: ValueError

Raised when attempting to add a metric that already exists.

Parameters:

Name Type Description Default
id str

The id of the metric.

required
name str

The name of the metric.

required
Source code in tolomeo/exceptions.py
class DuplicateMetricError(ValueError):
    """Raised when attempting to add a metric that already exists.

    Args:
        id: The id of the metric.
        name: The name of the metric.

    """

    def __init__(self, id: str, name: str):
        super().__init__(f"Duplicate metric ID: {id} (name: {name})")