https://github.com/plexus-oss/plexus-python
Python SDK for streaming telemetry from any device to Plexus — sensors, CAN bus, MAVLink, cameras, MQTT
https://github.com/plexus-oss/plexus-python
aerospace embedded hardware iot observability python python-sdk raspberry-pi real-time robotics sensor-data telemetry
Last synced: 15 days ago
JSON representation
Python SDK for streaming telemetry from any device to Plexus — sensors, CAN bus, MAVLink, cameras, MQTT
- Host: GitHub
- URL: https://github.com/plexus-oss/plexus-python
- Owner: plexus-oss
- License: apache-2.0
- Created: 2025-12-08T17:47:47.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-04-27T19:05:19.000Z (15 days ago)
- Last Synced: 2026-04-27T19:29:02.412Z (15 days ago)
- Topics: aerospace, embedded, hardware, iot, observability, python, python-sdk, raspberry-pi, real-time, robotics, sensor-data, telemetry
- Language: Python
- Size: 367 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# plexus-python
**Thin Python SDK for [Plexus](https://plexus.company).** Send telemetry to the Plexus gateway in one line. Storage, dashboards, alerts, and fleet management live in the platform — this package just ships your data.
[](https://pypi.org/project/plexus-python/)
[](LICENSE)
## Quick Start
```bash
pip install plexus-python
```
```python
from plexus import Plexus
px = Plexus(api_key="plx_xxx", source_id="device-001")
px.send("temperature", 72.5)
```
Get an API key at [app.plexus.company](https://app.plexus.company) → Devices → Add Device.
## Device identity
Every device needs a unique `source_id`. The recommended way to set one on a real host is the bootstrap script, which requires a device name up front:
```bash
curl -sL https://app.plexus.company/setup | bash -s -- \
--key plx_xxx --name drone-01
```
The name must match `^[a-z0-9][a-z0-9_-]{1,62}$`. `setup.sh` refuses to run without `--name` (or without a TTY to prompt for one) — this is deliberate, because the previous `hostname` fallback silently merged telemetry from cloned SD-card images that all booted as `raspberrypi`.
**If two devices end up requesting the same name**, the gateway auto-suffixes: the first connection gets `drone-01`, the second gets `drone-01_2`, the third `drone-01_3`, and so on. The SDK logs the rename at INFO and persists the assigned name to `~/.plexus/config.json` so the device keeps its identity across reboots. Under the hood, a per-installation UUID (`install_id`, lazily generated on first run) is what lets the gateway tell "same device reconnecting" from "different device claiming the same name."
In normal code, you usually just pass `source_id=...` explicitly to `Plexus(...)` and never have to think about it.
## Usage
```python
from plexus import Plexus
px = Plexus(source_id="rig-01") # reads PLEXUS_API_KEY from env
# Numbers
px.send("engine.rpm", 3450)
px.send("coolant.temperature", 82.3, tags={"unit": "C"})
# Strings, bools, objects, arrays — all JSON-serializable
px.send("vehicle.state", "RUNNING")
px.send("motor.enabled", True)
px.send("position", {"x": 1.5, "y": 2.3, "z": 0.8})
# Batch
px.send_batch([
("temperature", 72.5),
("pressure", 1013.25),
])
# Named run for grouping related data
with px.run("thermal-cycle-001"):
while running:
px.send("temperature", read_temp())
```
## Bring Your Own Protocol
This package ships no adapters, auto-detection, or daemons — just the client. Use whatever library you'd use anyway and pipe values into `px.send()`.
```python
# MAVLink (pymavlink)
for msg in conn:
if msg.get_type() == "ATTITUDE":
px.send("attitude.roll", msg.roll)
# CAN (python-can)
for msg in bus:
px.send(f"can.0x{msg.arbitration_id:x}", int.from_bytes(msg.data, "big"))
# MQTT (paho-mqtt)
def on_message(_c, _u, msg):
px.send(msg.topic.replace("/", "."), float(msg.payload))
# I2C sensor (Adafruit CircuitPython)
px.send("temperature", bme.temperature)
```
See [`examples/`](examples/) for runnable versions of each.
## Reliability
Every send buffers locally before hitting the network, retries with exponential backoff, and keeps your data safe across outages. Enable SQLite persistence to survive restarts and power loss:
```python
px = Plexus(persistent_buffer=True)
```
Point counts and flush:
```python
px.buffer_size()
px.flush_buffer()
```
## Transport
By default the SDK connects over a **WebSocket** to `/ws/device` on the gateway — same wire protocol as the C SDK. This gives you:
- lower-latency streaming of telemetry,
- live command delivery from the UI / API to the device.
If the socket is unavailable, sends transparently fall back to `POST /ingest` so no data is lost.
```python
# default — ws with http fallback
px = Plexus()
# force http (legacy)
px = Plexus(transport="http")
```
### Handling commands
Register a handler before the first `send()` so the command is advertised in the auth frame:
```python
def reboot(name, params):
delay = params.get("delay_s", 0)
# ... reboot logic ...
return {"ok": True, "delay": delay}
px = Plexus()
px.on_command("reboot", reboot, description="reboot the device")
px.send("temperature", 72.5) # opens the socket, waits for auth
```
The SDK sends an `ack` frame before invoking the handler, then a `result` frame with whatever the handler returns (or an `error` frame if it raises).
## Environment Variables
| Variable | Description | Default |
| ----------------------- | ---------------------------- | -------------------------------- |
| `PLEXUS_API_KEY` | API key (required) | none |
| `PLEXUS_GATEWAY_URL` | HTTP ingest URL | `https://plexus-gateway.fly.dev` |
| `PLEXUS_GATEWAY_WS_URL` | WebSocket URL | `wss://plexus-gateway.fly.dev` |
## Architecture
```
Your code ── px.send() ── HTTP POST /ingest ──> plexus-gateway ──> ClickHouse + Dashboard
```
One thin path. No agent, no daemon, no adapters. If you want the full HardwareOps platform — dashboards, alerts, RCA, fleet views — that's the web UI at app.plexus.company. This package gets your data there.
## License
Apache 2.0