{"id":48752172,"url":"https://github.com/plexus-oss/plexus-python","last_synced_at":"2026-04-27T20:00:54.545Z","repository":{"id":332522356,"uuid":"1112494591","full_name":"plexus-oss/plexus-python","owner":"plexus-oss","description":"Python SDK for streaming telemetry from any device to Plexus — sensors, CAN bus, MAVLink, cameras, MQTT","archived":false,"fork":false,"pushed_at":"2026-04-27T19:05:19.000Z","size":376,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-27T19:29:02.412Z","etag":null,"topics":["aerospace","embedded","hardware","iot","observability","python","python-sdk","raspberry-pi","real-time","robotics","sensor-data","telemetry"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/plexus-oss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-08T17:47:47.000Z","updated_at":"2026-04-27T19:05:23.000Z","dependencies_parsed_at":"2026-04-02T21:04:05.721Z","dependency_job_id":null,"html_url":"https://github.com/plexus-oss/plexus-python","commit_stats":null,"previous_names":["plexus-oss/agent","plexus-oss/plexus-python"],"tags_count":40,"template":false,"template_full_name":null,"purl":"pkg:github/plexus-oss/plexus-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plexus-oss%2Fplexus-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plexus-oss%2Fplexus-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plexus-oss%2Fplexus-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plexus-oss%2Fplexus-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plexus-oss","download_url":"https://codeload.github.com/plexus-oss/plexus-python/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plexus-oss%2Fplexus-python/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32352406,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-27T17:12:42.749Z","status":"ssl_error","status_checked_at":"2026-04-27T17:12:41.658Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aerospace","embedded","hardware","iot","observability","python","python-sdk","raspberry-pi","real-time","robotics","sensor-data","telemetry"],"created_at":"2026-04-12T20:12:23.187Z","updated_at":"2026-04-27T20:00:54.537Z","avatar_url":"https://github.com/plexus-oss.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# plexus-python\n\n**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.\n\n[![PyPI](https://img.shields.io/pypi/v/plexus-python)](https://pypi.org/project/plexus-python/)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)\n\n## Quick Start\n\n```bash\npip install plexus-python\n```\n\n```python\nfrom plexus import Plexus\n\npx = Plexus(api_key=\"plx_xxx\", source_id=\"device-001\")\npx.send(\"temperature\", 72.5)\n```\n\nGet an API key at [app.plexus.company](https://app.plexus.company) → Devices → Add Device.\n\n## Device identity\n\nEvery 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:\n\n```bash\ncurl -sL https://app.plexus.company/setup | bash -s -- \\\n  --key plx_xxx --name drone-01\n```\n\nThe 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`.\n\n**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.\"\n\nIn normal code, you usually just pass `source_id=...` explicitly to `Plexus(...)` and never have to think about it.\n\n## Usage\n\n```python\nfrom plexus import Plexus\n\npx = Plexus(source_id=\"rig-01\")   # reads PLEXUS_API_KEY from env\n\n# Numbers\npx.send(\"engine.rpm\", 3450)\npx.send(\"coolant.temperature\", 82.3, tags={\"unit\": \"C\"})\n\n# Strings, bools, objects, arrays — all JSON-serializable\npx.send(\"vehicle.state\", \"RUNNING\")\npx.send(\"motor.enabled\", True)\npx.send(\"position\", {\"x\": 1.5, \"y\": 2.3, \"z\": 0.8})\n\n# Batch\npx.send_batch([\n    (\"temperature\", 72.5),\n    (\"pressure\", 1013.25),\n])\n\n# Named run for grouping related data\nwith px.run(\"thermal-cycle-001\"):\n    while running:\n        px.send(\"temperature\", read_temp())\n```\n\n## Bring Your Own Protocol\n\nThis package ships no adapters, auto-detection, or daemons — just the client. Use whatever library you'd use anyway and pipe values into `px.send()`.\n\n```python\n# MAVLink (pymavlink)\nfor msg in conn:\n    if msg.get_type() == \"ATTITUDE\":\n        px.send(\"attitude.roll\", msg.roll)\n\n# CAN (python-can)\nfor msg in bus:\n    px.send(f\"can.0x{msg.arbitration_id:x}\", int.from_bytes(msg.data, \"big\"))\n\n# MQTT (paho-mqtt)\ndef on_message(_c, _u, msg):\n    px.send(msg.topic.replace(\"/\", \".\"), float(msg.payload))\n\n# I2C sensor (Adafruit CircuitPython)\npx.send(\"temperature\", bme.temperature)\n```\n\nSee [`examples/`](examples/) for runnable versions of each.\n\n## Reliability\n\nEvery 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:\n\n```python\npx = Plexus(persistent_buffer=True)\n```\n\nPoint counts and flush:\n\n```python\npx.buffer_size()\npx.flush_buffer()\n```\n\n## Transport\n\nBy default the SDK connects over a **WebSocket** to `/ws/device` on the gateway — same wire protocol as the C SDK. This gives you:\n\n- lower-latency streaming of telemetry,\n- live command delivery from the UI / API to the device.\n\nIf the socket is unavailable, sends transparently fall back to `POST /ingest` so no data is lost.\n\n```python\n# default — ws with http fallback\npx = Plexus()\n\n# force http (legacy)\npx = Plexus(transport=\"http\")\n```\n\n### Handling commands\n\nRegister a handler before the first `send()` so the command is advertised in the auth frame:\n\n```python\ndef reboot(name, params):\n    delay = params.get(\"delay_s\", 0)\n    # ... reboot logic ...\n    return {\"ok\": True, \"delay\": delay}\n\npx = Plexus()\npx.on_command(\"reboot\", reboot, description=\"reboot the device\")\npx.send(\"temperature\", 72.5)   # opens the socket, waits for auth\n```\n\nThe 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).\n\n## Environment Variables\n\n| Variable                | Description                  | Default                          |\n| ----------------------- | ---------------------------- | -------------------------------- |\n| `PLEXUS_API_KEY`        | API key (required)           | none                             |\n| `PLEXUS_GATEWAY_URL`    | HTTP ingest URL              | `https://plexus-gateway.fly.dev` |\n| `PLEXUS_GATEWAY_WS_URL` | WebSocket URL              | `wss://plexus-gateway.fly.dev`   |\n\n## Architecture\n\n```\nYour code ── px.send() ── HTTP POST /ingest ──\u003e plexus-gateway ──\u003e ClickHouse + Dashboard\n```\n\nOne 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.\n\n## License\n\nApache 2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplexus-oss%2Fplexus-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplexus-oss%2Fplexus-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplexus-oss%2Fplexus-python/lists"}