Releases: plexus-oss/plexus-python
0.5.2 — DX hardening for hardware engineers
[0.5.2] - 2026-05-21 - DX hardening for hardware engineers
Fixed
- Error messages now reference
plexus initinstead of the non-existentplexus start. close()now attempts to flush any buffered points before tearing down the transport,
preventing silent data loss on graceful shutdown.persistent_bufferdefault changed fromFalsetoTrue— store-and-forward is now
on by default, matching the~/.plexus/config.jsondefault and the right choice for
field hardware. Passpersistent_buffer=Falseto opt out (e.g. in test fixtures).
Added
send_batch()now accepts 3-tuples(metric, value, timestamp)alongside the existing
2-tuple form. Per-point timestamps let sensors on different interrupt timers share a
single batch call. 2-tuples continue to use the sharedtimestampargument.on_command()now warns immediately via stderr if called after the WebSocket has already
authenticated, making the "register before first send()" ordering requirement visible
rather than silently broken.source_idis validated against^[a-z0-9][a-z0-9_-]{1,62}$at construction time.
Invalid names now raiseValueErrorwith a clear message instead of failing obscurely
at the gateway.
Changed
_say()/_QUIETconsolidated into a new internalplexus/_log.pymodule.
Previously duplicated verbatim betweenclient.pyandws.py.
0.5.1 — Binary video frames + non-blocking send
[0.5.1] - 2026-05-19 - Binary video frames + non-blocking send
Performance
send_video_frame()now sends a compact binary WebSocket frame instead of
JSON+base64. The binary header encodes source_id, camera_id, width, height,
and timestamp_ms; the JPEG payload follows raw. Eliminates the 33% base64
wire overhead, reducing per-frame bandwidth by ~25% and raising the
sustainable FPS ceiling from ~15–20 fps to ~20–25 fps at 1280×720 quality 85.- Gateway decodes the binary header and re-encodes as JSON+base64 before
relaying to browsers — no changes required in the frontend, data_api, or any
other consumer.
Reliability
send_video_frame()is now non-blocking. Frames are placed into a
queue.Queue(maxsize=2)drained by a dedicatedplexus-videodaemon thread.
When the queue is full (sender backlogged) frames are dropped rather than
blocking the capture pipeline, preventing deadlocks at any FPS.stop()/close()now exits cleanly within 0.5 s regardless of in-flight
sends. Previously a slow or hung WebSocket write could stall shutdown
indefinitely.
Changed
- Removed
import base64fromclient.py(no longer needed on the send path). send_video_frame()callsws.send_video_frame_async()instead of the
internalws._send_frame().
Wire protocol
- Gateway handles both binary frames (SDK ≥ 0.5.1) and legacy JSON text frames
transparently — older SDKs continue to work unchanged.
0.5.0 — Security hardening, dep cleanup, Python 3.10+ only
[0.5.0] - 2026-05-19 - Security hardening, dep cleanup, Python 3.10+ only
Security
- Removed
requests(and its transitive depsurllib3,idna) entirely —
replaced with stdliburllib.request. Closes 6 Dependabot alerts (#6, #9,
#10, #11, #12, #13, #19) by eliminating the vulnerability surface rather than
patching it. - Bumped
Pillow>=12.2.0(fixes #14, #15, #16, #17, #18, #20 — OOB write,
FITS decompression bomb, font integer overflow, PDF parsing DoS, and related
CVEs). - Bumped
pytest>=9.0.3in dev deps (fixes #7).
Changed
- Dropped Python 3.8 and 3.9 support — both are past EOL and the patched
versions of Pillow and pytest all require>=3.10.requires-pythonis now
>=3.10. - CI matrix: removed 3.8/3.9 runners, added 3.13.
0.4.9 — Video input broadening and wire safety
[0.4.9] - 2026-05-19 - Video input broadening and wire safety
Added
send_video_framenow accepts raw bytes/bytearray: JPEG bytes are passed
through without re-encoding (zero CPU cost on hardware that outputs JPEG
natively); other formats (PNG, BMP, WebP) are decoded via Pillow and
re-encoded as JPEG. Installplexus-python[video]for Pillow support.stream_camera(url, camera_id, fps, quality)— streams from any
FFmpeg-supported source (RTSP, video file, capture device). Requires FFmpeg
on$PATH. Returns athreading.Event; call.set()to stop.read_mjpeg_frames(pipe)— public generator that parses raw MJPEG byte
streams (e.g. FFmpeg stdout) into individual JPEG frames by SOI/EOI markers.
Useful for custom FFmpeg pipelines before handing off tosend_video_frame.- Optional
videoextras group:pip install plexus-python[video]installs
Pillow for non-JPEG input decoding and automatic oversized-frame downsampling.
Changed
- Frames that would exceed the gateway's 1 MB wire limit are automatically
re-encoded at a proportionally lower quality. A one-time warning is printed
to stderr; subsequent frames are silently clamped. stream_cameraraisesPlexusErrorsynchronously (before spawning a thread)
when FFmpeg is not found, rather than silently dying in the background.- Minimum
requestsbumped to>=2.32.4(fixes CVE inextract_zipped_paths). - Minimum
Pillowbumped to>=11.2.1(fixes OOB write, FITS decompression bomb,
font integer overflow, PDF parsing DoS). - Dropped Python 3.8 support (EOL October 2024); minimum is now Python 3.9.
0.4.8 — Video input broadening and wire safety
v0.4.8: video input broadening and wire safety (#7) ## What does this PR do? <!-- Brief description of the change --> ## Why? <!-- What problem does this solve? Link to issue if applicable. --> Closes # ## Checklist - [ ] Tests pass (`pytest`) - [ ] Linting passes (`ruff check .`) - [ ] New code has type hints - [ ] Updated CHANGELOG.md (if user-facing change) - [ ] Tested on target hardware (if applicable)
v0.4.7: video streaming API
See CHANGELOG.md
v0.4.6
Clock QA improvements
v0.4.5 — Stderr status output
Same code as the failed v0.4.4 (which 4xx'd in CI on a stray f-prefix), now lint-clean.
Added
[plexus] … status lines on stderr at every meaningful state change so scripts that don't configure the logging module still tell the user what's going on. Set PLEXUS_QUIET=1 to suppress.
✓ Connected to gateway as <source_id>on first WS auth✓ Reconnected as <source_id>after a drop✓ First N points landed (via ws|http)on first successful send⚠ WebSocket unavailable, falling back to POST /ingeston WS failure✗ Auth rejected by gateway: …/✗ Gateway rejected the API key (401)on auth failures, with aplexus whoamihint⏸ Send failed, buffering points locally (N queued)when offline✓ Sending again (drained the local buffer)on recovery
v0.4.4 — Stderr status output
Added
[plexus] … status lines on stderr at every meaningful state change so scripts that don't configure the logging module still tell the user what's going on. Set PLEXUS_QUIET=1 to suppress.
✓ Connected to gateway as <source_id>on first WS auth✓ Reconnected as <source_id>after a drop✓ First N points landed (via ws|http)on first successful send⚠ WebSocket unavailable, falling back to POST /ingeston WS failure✗ Auth rejected by gateway: …/✗ Gateway rejected the API key (401)on auth failures, with aplexus whoamihint⏸ Send failed, buffering points locally (N queued)when offline✓ Sending again (drained the local buffer)on recovery
Why
Users running python my_script.py saw nothing — by default Python's logging module emits at WARNING and above only on the console, so a silent SDK was indistinguishable from "everything's working" until they checked the dashboard. This makes the trip from python my_script.py to "first row visible in the UI" auditable in one terminal.
v0.4.3 — Re-release of 0.4.2 with correct __version__
Re-release of 0.4.2. The 0.4.2 wheel shipped with plexus.__version__ == "0.4.1" because the tag was cut one commit before the __init__.py bump landed. 0.4.3 is the same code with the version metadata correct. 0.4.2 has been yanked.
See the 0.4.2 release notes for the actual changes (branded CLI auth success page + 10s auto-redirect to the app).