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.
- {plexus_python-0.4.6 → plexus_python-0.4.7}/CHANGELOG.md +22 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/PKG-INFO +1 -1
- plexus_python-0.4.7/examples/.python-version +1 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/README.md +1 -0
- plexus_python-0.4.7/examples/mac_metrics.py +81 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/mqtt.py +1 -1
- plexus_python-0.4.7/examples/pyproject.toml +15 -0
- plexus_python-0.4.7/examples/uv.lock +740 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/__init__.py +1 -1
- {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/client.py +61 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/ws.py +0 -1
- {plexus_python-0.4.6 → plexus_python-0.4.7}/pyproject.toml +1 -1
- {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/workflows/ci.yml +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/.github/workflows/publish.yml +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/.gitignore +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/AGENTS.md +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/API.md +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/CODE_OF_CONDUCT.md +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/CONTRIBUTING.md +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/LICENSE +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/README.md +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/SECURITY.md +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/basic.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/can.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/i2c_bme280.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/examples/mavlink.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/buffer.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/cli.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/plexus/config.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/scripts/plexus.service +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/scripts/scan_buses.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/scripts/setup.sh +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_basic.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_buffer.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_config.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_retry.py +0 -0
- {plexus_python-0.4.6 → plexus_python-0.4.7}/tests/test_ws.py +0 -0
- {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
|
|
@@ -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)
|
|
@@ -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
|
+
]
|