Development Examples

Complete, working examples

PoseLab ships with runnable example plugins and metric scripts. Copy any of them into the matching directory (Settings › Device Plugins or Settings › Metric Scripts), press Reload, and use Run Mock or Test Metrics to see them work — no hardware needed.

A complete device plugin

Cadence Sensor — crank RPM from standard Cycling Speed & Cadence (CSC) frames. Exercise it with the built-in CSC crank mock (characteristic 2a5b). It shows metadata, per-frame decoding of a rolling counter, and returning several named values at once:

"""Pedal speed plugin: crank RPM from CSC-style BLE frames."""
import logging

log = logging.getLogger(__name__)

META = {
    "display_name": "Cadence Sensor",
    "version": "1.0.0",
    "description": "Crank cadence (RPM) from a CSC speed/cadence sensor.",
    "service_uuid": "00001816-0000-1000-8000-00805f9b34fb",  # Cycling Speed and Cadence
    "data_char": "00002a5b-0000-1000-8000-00805f9b34fb",     # CSC Measurement (notify)
    "stream_on_connect": True,
    "is_pose_provider": False,
}

_last = None


def process(frame):
    global _last
    payload = frame.get("payload") or []
    if len(payload) < 5 or not (payload[0] & 0x02):
        return None
    revs = payload[1] | (payload[2] << 8)
    event_time = payload[3] | (payload[4] << 8)
    if _last is None:
        _last = (revs, event_time)
        return None
    d_revs = (revs - _last[0]) & 0xFFFF
    d_time = (event_time - _last[1]) & 0xFFFF
    _last = (revs, event_time)
    if d_time == 0:
        return None
    rpm = d_revs / (d_time / 1024.0) * 60.0
    return {
        "cadence_rpm": rpm,
        "total_revs": float(revs),
        "last_rev_ms": event_time / 1.024,
    }

Plugin catalogue

The example plugins that ship in examples/plugins/:

File What it demonstrates
pedal_speed.py Crank cadence (RPM) from CSC frames; returning multiple named values.
race_posture_sensor.py Decodes the mock back-angle stream and explicitly exposes pitch/sway measurements for a paired metric.
pose_mock.py A 3D pose provider that plays back a captured clip (or a synthetic walking loop).
pressure_map.py + pressure_map.frag A viz provider: reassembles a fragmented 32×32 pressure stream and renders it with a user fragment shader. Note, this does not represent a pressure map but illustrates the graphical pipeline, how it's used, and how to work around BLE bandwidth limitations for a rich display.

Metric-script examples

File What it demonstrates
trunk_lean.py Trunk-lean angle from vertical, smoothed over the pose window, with an optional-numpy mean.
posture_consistency.py Consumes a paired plugin's named values and turns five seconds of pitch/sway history into a 0–100 consistency score.

Walkthrough: a plugin and a metric together

The race_posture_sensor.py plugin and the posture_consistency.py metric are designed to work as a pair — the plugin exposes sensor values, the metric consumes them. To try it:

  1. Copy race_posture_sensor.py into your plugin directory and posture_consistency.py into your metric-script directory.
  2. Press Reload in both Settings sections.
  3. Start Run Mock for “Race Posture Sensor (Example)” in the Devices panel.
  4. Run pose capture so the custom metric is evaluated.
No device id hard-coded

The metric discovers the compatible device from its exposed value names, so nothing needs to be wired by id — a good pattern for scripts meant to travel between machines.