plexus-python 0.4.6__tar.gz → 0.4.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. {plexus_python-0.4.6 → plexus_python-0.4.7}/CHANGELOG.md +22 -0
  2. {plexus_python-0.4.6 → plexus_python-0.4.7}/PKG-INFO +1 -1
  3. plexus_python-0.4.7/examples/.python-version +1 -0
  4. {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/README.md +1 -0
  5. plexus_python-0.4.7/examples/mac_metrics.py +81 -0
  6. {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/mqtt.py +1 -1
  7. plexus_python-0.4.7/examples/pyproject.toml +15 -0
  8. plexus_python-0.4.7/examples/uv.lock +740 -0
  9. {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/__init__.py +1 -1
  10. {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/client.py +61 -0
  11. {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/ws.py +0 -1
  12. {plexus_python-0.4.6 → plexus_python-0.4.7}/pyproject.toml +1 -1
  13. {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  14. {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  15. {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  16. {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/workflows/ci.yml +0 -0
  17. {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/workflows/publish.yml +0 -0
  18. {plexus_python-0.4.6 → plexus_python-0.4.7}/.gitignore +0 -0
  19. {plexus_python-0.4.6 → plexus_python-0.4.7}/AGENTS.md +0 -0
  20. {plexus_python-0.4.6 → plexus_python-0.4.7}/API.md +0 -0
  21. {plexus_python-0.4.6 → plexus_python-0.4.7}/CODE_OF_CONDUCT.md +0 -0
  22. {plexus_python-0.4.6 → plexus_python-0.4.7}/CONTRIBUTING.md +0 -0
  23. {plexus_python-0.4.6 → plexus_python-0.4.7}/LICENSE +0 -0
  24. {plexus_python-0.4.6 → plexus_python-0.4.7}/README.md +0 -0
  25. {plexus_python-0.4.6 → plexus_python-0.4.7}/SECURITY.md +0 -0
  26. {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/basic.py +0 -0
  27. {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/can.py +0 -0
  28. {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/i2c_bme280.py +0 -0
  29. {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/mavlink.py +0 -0
  30. {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/buffer.py +0 -0
  31. {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/cli.py +0 -0
  32. {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/config.py +0 -0
  33. {plexus_python-0.4.6 → plexus_python-0.4.7}/scripts/plexus.service +0 -0
  34. {plexus_python-0.4.6 → plexus_python-0.4.7}/scripts/scan_buses.py +0 -0
  35. {plexus_python-0.4.6 → plexus_python-0.4.7}/scripts/setup.sh +0 -0
  36. {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_basic.py +0 -0
  37. {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_buffer.py +0 -0
  38. {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_config.py +0 -0
  39. {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_retry.py +0 -0
  40. {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_ws.py +0 -0
  41. {plexus_python-0.4.6 → plexus_python-0.4.7}/uv.lock +0 -0
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.7] - 2026-05-14 - Video streaming API
4
+
5
+ ### Added
6
+
7
+ - `Plexus.send_video_frame(frame, camera_id, quality, timestamp)` — high-level
8
+ API for streaming camera frames. Accepts a numpy array (e.g. from
9
+ `cv2.VideoCapture.read()`), handles JPEG encoding, base64, dimensions, and
10
+ auth wait internally. Requires `transport="ws"` and `opencv-python`.
11
+
12
+ ### Changed
13
+
14
+ - Gateway WebSocket URL (`wss://plexus-gateway.fly.dev`) is now the SDK
15
+ default — no need to pass `ws_url` explicitly.
16
+ - Removed the `[plexus] endpoint: …` line from the connection printout.
17
+
18
+ ### Performance
19
+
20
+ - Eliminated per-frame `buf.tobytes()` copy in `send_video_frame` by passing
21
+ the numpy buffer directly to `base64.b64encode` (buffer protocol).
22
+ - `base64` imported at module level; `cv2` imported once on first call and
23
+ cached, removing repeated import overhead from the hot path.
24
+
3
25
  ## [0.4.5] - 2026-04-27 - Stderr status output (re-release of 0.4.4)
4
26
 
5
27
  Same code as 0.4.4 — the 0.4.4 publish workflow failed lint on a stray
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python
3
- Version: 0.4.6
3
+ Version: 0.4.7
4
4
  Summary: Thin Python SDK for Plexus — send telemetry in one line
5
5
  Project-URL: Homepage, https://plexus.dev
6
6
  Project-URL: Documentation, https://docs.plexus.dev
@@ -0,0 +1 @@
1
+ 3.12
@@ -9,5 +9,6 @@ Each script is standalone — copy into your project, adjust the `source_id`, an
9
9
  | `can.py` | Vehicle CAN bus (with optional DBC decode) | `python-can`, `cantools` |
10
10
  | `mqtt.py` | MQTT broker → Plexus bridge | `paho-mqtt` |
11
11
  | `i2c_bme280.py` | Raspberry Pi environmental sensor | `adafruit-circuitpython-bme280` |
12
+ | `mac_metrics.py` | Mac system metrics + spike/pressure events | `psutil` |
12
13
 
13
14
  The pattern is always the same: use whatever library you'd use anyway, then call `px.send(metric, value)`. Plexus stays out of your decode path.
@@ -0,0 +1,81 @@
1
+ """
2
+ Mac system metrics with structured event logs.
3
+
4
+ Install deps:
5
+ pip install plexus-python psutil
6
+
7
+ Run:
8
+ python examples/mac_metrics.py --api-key plx_xxx
9
+ python examples/mac_metrics.py --api-key plx_xxx --interval 10
10
+ """
11
+
12
+ import argparse
13
+ import time
14
+ import psutil
15
+ from plexus import Plexus
16
+
17
+ parser = argparse.ArgumentParser(description="Stream Mac system metrics to Plexus.")
18
+ parser.add_argument("--api-key", required=True, help="Plexus API key (plx_xxx)")
19
+ parser.add_argument("--interval", type=float, default=5.0, metavar="SECONDS",
20
+ help="Sampling interval in seconds (default: 5)")
21
+ parser.add_argument("--source-id", default="macbook", help="Device source ID (default: macbook)")
22
+ args = parser.parse_args()
23
+
24
+ px = Plexus(api_key=args.api_key, source_id=args.source_id)
25
+
26
+ CPU_SPIKE_THRESHOLD = 80.0 # %
27
+ MEM_PRESSURE_THRESHOLD = 85.0 # %
28
+
29
+ prev_net = psutil.net_io_counters()
30
+ prev_disk = psutil.disk_io_counters()
31
+ prev_charging = None
32
+
33
+ while True:
34
+ net = psutil.net_io_counters()
35
+ disk = psutil.disk_io_counters()
36
+
37
+ cpu = psutil.cpu_percent(interval=None)
38
+ mem = psutil.virtual_memory()
39
+
40
+ # Numeric metrics
41
+ px.send("cpu.percent", cpu)
42
+ px.send("memory.used_gb", mem.used / 1e9)
43
+ px.send("memory.percent", mem.percent)
44
+ px.send("net.rx_mbps", (net.bytes_recv - prev_net.bytes_recv) / 1e6)
45
+ px.send("net.tx_mbps", (net.bytes_sent - prev_net.bytes_sent) / 1e6)
46
+ px.send("disk.read_mbps", (disk.read_bytes - prev_disk.read_bytes) / 1e6)
47
+ px.send("disk.write_mbps", (disk.write_bytes - prev_disk.write_bytes) / 1e6)
48
+ px.send("disk.free_gb", psutil.disk_usage("/").free / 1e9)
49
+
50
+ # Battery metrics + charge-state change event
51
+ battery = psutil.sensors_battery()
52
+ if battery:
53
+ px.send("battery.percent", battery.percent)
54
+ charging = battery.power_plugged
55
+ if charging != prev_charging and prev_charging is not None:
56
+ px.event("battery.state_change", {
57
+ "charging": charging,
58
+ "percent": battery.percent,
59
+ })
60
+ prev_charging = charging
61
+
62
+ # CPU spike event — fires once per interval when threshold is crossed
63
+ if cpu >= CPU_SPIKE_THRESHOLD:
64
+ top = max(psutil.process_iter(["name", "cpu_percent"]), key=lambda p: p.info["cpu_percent"] or 0)
65
+ px.event("cpu.spike", {
66
+ "cpu_percent": cpu,
67
+ "top_process": top.info["name"],
68
+ "top_process_percent": top.info["cpu_percent"],
69
+ })
70
+
71
+ # Memory pressure event
72
+ if mem.percent >= MEM_PRESSURE_THRESHOLD:
73
+ top = max(psutil.process_iter(["name", "memory_percent"]), key=lambda p: p.info["memory_percent"] or 0)
74
+ px.event("memory.pressure", {
75
+ "memory_percent": mem.percent,
76
+ "top_process": top.info["name"],
77
+ "top_process_percent": round(top.info["memory_percent"], 1),
78
+ })
79
+
80
+ prev_net, prev_disk = net, disk
81
+ time.sleep(args.interval)
@@ -41,7 +41,7 @@ def on_message(_client, _userdata, msg):
41
41
  px.send(name, data)
42
42
 
43
43
 
44
- client = mqtt.Client()
44
+ client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
45
45
  client.on_message = on_message
46
46
  client.connect(broker)
47
47
  client.subscribe(topic)
@@ -0,0 +1,15 @@
1
+ [project]
2
+ name = "examples"
3
+ version = "0.1.0"
4
+ description = "Standalone example scripts for plexus-python"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "adafruit-circuitpython-bme280>=2.6.32",
9
+ "cantools>=41.3.1",
10
+ "paho-mqtt>=2.1.0",
11
+ "plexus-python>=0.4.6",
12
+ "psutil>=7.2.2",
13
+ "pymavlink>=2.4.49",
14
+ "python-can>=4.6.1",
15
+ ]