plexus-python 0.1.0__py3-none-any.whl
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/__init__.py +31 -0
- plexus/__main__.py +4 -0
- plexus/adapters/__init__.py +122 -0
- plexus/adapters/base.py +409 -0
- plexus/adapters/ble.py +257 -0
- plexus/adapters/can.py +439 -0
- plexus/adapters/can_detect.py +174 -0
- plexus/adapters/mavlink.py +642 -0
- plexus/adapters/mavlink_detect.py +192 -0
- plexus/adapters/modbus.py +622 -0
- plexus/adapters/mqtt.py +350 -0
- plexus/adapters/opcua.py +607 -0
- plexus/adapters/registry.py +206 -0
- plexus/adapters/serial_adapter.py +547 -0
- plexus/buffer.py +257 -0
- plexus/cameras/__init__.py +57 -0
- plexus/cameras/auto.py +239 -0
- plexus/cameras/base.py +189 -0
- plexus/cameras/picamera.py +171 -0
- plexus/cameras/usb.py +143 -0
- plexus/cli.py +783 -0
- plexus/client.py +465 -0
- plexus/config.py +169 -0
- plexus/connector.py +666 -0
- plexus/deps.py +246 -0
- plexus/detect.py +1238 -0
- plexus/importers/__init__.py +25 -0
- plexus/importers/rosbag.py +778 -0
- plexus/sensors/__init__.py +118 -0
- plexus/sensors/ads1115.py +164 -0
- plexus/sensors/adxl345.py +179 -0
- plexus/sensors/auto.py +290 -0
- plexus/sensors/base.py +412 -0
- plexus/sensors/bh1750.py +102 -0
- plexus/sensors/bme280.py +241 -0
- plexus/sensors/gps.py +317 -0
- plexus/sensors/ina219.py +149 -0
- plexus/sensors/magnetometer.py +239 -0
- plexus/sensors/mpu6050.py +162 -0
- plexus/sensors/sht3x.py +139 -0
- plexus/sensors/spi_scan.py +164 -0
- plexus/sensors/system.py +261 -0
- plexus/sensors/vl53l0x.py +109 -0
- plexus/streaming.py +743 -0
- plexus/tui.py +642 -0
- plexus_python-0.1.0.dist-info/METADATA +470 -0
- plexus_python-0.1.0.dist-info/RECORD +50 -0
- plexus_python-0.1.0.dist-info/WHEEL +4 -0
- plexus_python-0.1.0.dist-info/entry_points.txt +2 -0
- plexus_python-0.1.0.dist-info/licenses/LICENSE +190 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: plexus-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Send sensor data to Plexus in one line of code
|
|
5
|
+
Project-URL: Homepage, https://plexus.dev
|
|
6
|
+
Project-URL: Documentation, https://docs.plexus.dev
|
|
7
|
+
Project-URL: Repository, https://github.com/plexus-oss/plexus-python
|
|
8
|
+
Project-URL: Issues, https://github.com/plexus-oss/plexus-python/issues
|
|
9
|
+
Author-email: Plexus <hello@plexus.dev>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: hardware,iot,observability,robotics,sensors,telemetry
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Topic :: System :: Hardware
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Requires-Dist: click>=8.0.0
|
|
27
|
+
Requires-Dist: requests>=2.28.0
|
|
28
|
+
Requires-Dist: websockets>=12.0
|
|
29
|
+
Provides-Extra: all
|
|
30
|
+
Requires-Dist: asyncua>=1.0.0; extra == 'all'
|
|
31
|
+
Requires-Dist: bleak>=0.21.0; extra == 'all'
|
|
32
|
+
Requires-Dist: cantools>=39.0.0; extra == 'all'
|
|
33
|
+
Requires-Dist: mcap>=1.0.0; extra == 'all'
|
|
34
|
+
Requires-Dist: numpy>=1.20.0; extra == 'all'
|
|
35
|
+
Requires-Dist: opencv-python>=4.8.0; extra == 'all'
|
|
36
|
+
Requires-Dist: paho-mqtt>=1.6.0; extra == 'all'
|
|
37
|
+
Requires-Dist: psutil>=5.9.0; extra == 'all'
|
|
38
|
+
Requires-Dist: pymavlink>=2.4.0; extra == 'all'
|
|
39
|
+
Requires-Dist: pymodbus>=3.5.0; extra == 'all'
|
|
40
|
+
Requires-Dist: pyserial>=3.5; extra == 'all'
|
|
41
|
+
Requires-Dist: python-can>=4.0.0; extra == 'all'
|
|
42
|
+
Requires-Dist: rich>=13.0.0; extra == 'all'
|
|
43
|
+
Requires-Dist: rosbags>=0.9.0; extra == 'all'
|
|
44
|
+
Requires-Dist: smbus2>=0.4.0; extra == 'all'
|
|
45
|
+
Requires-Dist: spidev>=3.5; extra == 'all'
|
|
46
|
+
Provides-Extra: ble
|
|
47
|
+
Requires-Dist: bleak>=0.21.0; extra == 'ble'
|
|
48
|
+
Provides-Extra: camera
|
|
49
|
+
Requires-Dist: numpy>=1.20.0; extra == 'camera'
|
|
50
|
+
Requires-Dist: opencv-python>=4.8.0; extra == 'camera'
|
|
51
|
+
Provides-Extra: can
|
|
52
|
+
Requires-Dist: cantools>=39.0.0; extra == 'can'
|
|
53
|
+
Requires-Dist: python-can>=4.0.0; extra == 'can'
|
|
54
|
+
Provides-Extra: dev
|
|
55
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
56
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
57
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
58
|
+
Provides-Extra: mavlink
|
|
59
|
+
Requires-Dist: pymavlink>=2.4.0; extra == 'mavlink'
|
|
60
|
+
Provides-Extra: modbus
|
|
61
|
+
Requires-Dist: pymodbus>=3.5.0; extra == 'modbus'
|
|
62
|
+
Provides-Extra: mqtt
|
|
63
|
+
Requires-Dist: paho-mqtt>=1.6.0; extra == 'mqtt'
|
|
64
|
+
Provides-Extra: opcua
|
|
65
|
+
Requires-Dist: asyncua>=1.0.0; extra == 'opcua'
|
|
66
|
+
Provides-Extra: picamera
|
|
67
|
+
Requires-Dist: picamera2>=0.3.12; extra == 'picamera'
|
|
68
|
+
Provides-Extra: ros
|
|
69
|
+
Requires-Dist: mcap>=1.0.0; extra == 'ros'
|
|
70
|
+
Requires-Dist: rosbags>=0.9.0; extra == 'ros'
|
|
71
|
+
Provides-Extra: sensors
|
|
72
|
+
Requires-Dist: smbus2>=0.4.0; extra == 'sensors'
|
|
73
|
+
Provides-Extra: serial
|
|
74
|
+
Requires-Dist: pyserial>=3.5; extra == 'serial'
|
|
75
|
+
Provides-Extra: spi
|
|
76
|
+
Requires-Dist: spidev>=3.5; extra == 'spi'
|
|
77
|
+
Provides-Extra: system
|
|
78
|
+
Requires-Dist: psutil>=5.9.0; extra == 'system'
|
|
79
|
+
Provides-Extra: tui
|
|
80
|
+
Requires-Dist: rich>=13.0.0; extra == 'tui'
|
|
81
|
+
Description-Content-Type: text/markdown
|
|
82
|
+
|
|
83
|
+
# Plexus Agent
|
|
84
|
+
|
|
85
|
+
**Open-source Python SDK for hardware observability and telemetry.** Stream sensor data, CAN bus, MAVLink, cameras, and MQTT from any device to [Plexus](https://plexus.company) — the HardwareOps platform for real-time monitoring and fleet management.
|
|
86
|
+
|
|
87
|
+
[](https://pypi.org/project/plexus-python/)
|
|
88
|
+
[](LICENSE)
|
|
89
|
+
|
|
90
|
+
<!-- terminal demo placeholder -->
|
|
91
|
+
|
|
92
|
+
## Quick Start
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install plexus-python
|
|
96
|
+
plexus start
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
That's it. The CLI walks you through sign-up, detects your hardware, and starts streaming. If you already have an API key, pass it directly:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
plexus start --key plx_xxxxx
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Get an API key from [app.plexus.company](https://app.plexus.company) → Devices → Add Device.
|
|
106
|
+
|
|
107
|
+
## What You Get
|
|
108
|
+
|
|
109
|
+
- **Live terminal dashboard** — real-time TUI enabled by default (like htop for your hardware)
|
|
110
|
+
- **Auto-detect hardware** — sensors, cameras, CAN interfaces found and configured automatically
|
|
111
|
+
- **12+ sensor drivers** — IMU, environmental, current/power, ADC, magnetometer, GPS, and more
|
|
112
|
+
- **Adapters** — CAN bus (with DBC decoding), MAVLink (drones/UAVs), MQTT bridge, USB cameras
|
|
113
|
+
- **Offline buffering** — local buffer with automatic retry when network drops
|
|
114
|
+
|
|
115
|
+
Works on any Linux system — Raspberry Pi, edge compute nodes, test rigs, fleet vehicles, ground stations.
|
|
116
|
+
|
|
117
|
+
## Install
|
|
118
|
+
|
|
119
|
+
**macOS** (recommended — avoids Python environment issues):
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
brew install pipx
|
|
123
|
+
pipx install plexus-python
|
|
124
|
+
plexus start
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Linux / Raspberry Pi** (one-line setup):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
curl -sL https://app.plexus.company/setup | bash -s -- --key plx_xxxxx
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Manual** (any platform with Python 3.8+):
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
python3 -m venv ~/.plexus-env
|
|
137
|
+
source ~/.plexus-env/bin/activate
|
|
138
|
+
pip install plexus-python
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> **Note:** Modern macOS and Debian/Ubuntu block `pip install` system-wide ([PEP 668](https://peps.python.org/pep-0668/)). Use `pipx`, the curl script, or a virtual environment instead of running `pip install` directly.
|
|
142
|
+
|
|
143
|
+
| Extra | What it adds |
|
|
144
|
+
| ------------ | --------------------------------- |
|
|
145
|
+
| `[sensors]` | I2C sensors (IMU, environmental) |
|
|
146
|
+
| `[can]` | CAN bus with DBC decoding |
|
|
147
|
+
| `[mavlink]` | MAVLink for drones/UAVs |
|
|
148
|
+
| `[mqtt]` | MQTT bridge |
|
|
149
|
+
| `[camera]` | USB cameras (OpenCV) |
|
|
150
|
+
| `[picamera]` | Raspberry Pi Camera Module |
|
|
151
|
+
| `[serial]` | Serial/UART (GPS, custom devices) |
|
|
152
|
+
| `[tui]` | Live terminal dashboard |
|
|
153
|
+
| `[system]` | System health (psutil) |
|
|
154
|
+
| `[all]` | Everything |
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pip install plexus-python[all] # install everything at once
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Usage
|
|
161
|
+
|
|
162
|
+
### Authentication
|
|
163
|
+
|
|
164
|
+
| Method | How to get it | Used by |
|
|
165
|
+
| ----------------- | --------------------------------------------------------- | ------------------------------------ |
|
|
166
|
+
| API key (`plx_*`) | Dashboard → Devices → Add Device, or Settings → Developer | `plexus start` and `Plexus()` client |
|
|
167
|
+
|
|
168
|
+
Two ways to authenticate:
|
|
169
|
+
|
|
170
|
+
1. **Interactive (default):** `plexus start` — sign up or sign in directly in the terminal
|
|
171
|
+
2. **API key:** `plexus start --key plx_xxxxx` — skip the interactive prompt
|
|
172
|
+
|
|
173
|
+
Credentials are stored in `~/.plexus/config.json` or can be set via environment variables:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
export PLEXUS_API_KEY=plx_xxxxx
|
|
177
|
+
export PLEXUS_ENDPOINT=https://app.plexus.company # default
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### CLI Reference
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
plexus start [--key KEY] [--device-id ID] Set up and stream
|
|
184
|
+
plexus reset Clear config and start over
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### plexus start
|
|
188
|
+
|
|
189
|
+
Set up and start streaming. Handles auth, hardware detection, and sensor selection. The live terminal dashboard (TUI) is enabled by default. Automatically switches to headless mode when output is piped or in a non-TTY environment.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
plexus start # Interactive setup with live TUI
|
|
193
|
+
plexus start --key plx_xxx # Use an API key directly
|
|
194
|
+
plexus start --device-id my-drone # Set device identifier
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
| Flag | Description |
|
|
198
|
+
| ------------- | --------------------------------------- |
|
|
199
|
+
| `-k, --key` | API key (skips interactive auth prompt) |
|
|
200
|
+
| `--device-id` | Device ID from dashboard |
|
|
201
|
+
|
|
202
|
+
`plexus start` handles auth, hardware detection, and sensor selection interactively:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
Found 3 sensors on I2C bus 1:
|
|
206
|
+
|
|
207
|
+
[1] BME280 temperature, humidity, pressure
|
|
208
|
+
[2] MPU6050 accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z
|
|
209
|
+
[3] INA219 bus_voltage, shunt_voltage, current_ma, power_mw
|
|
210
|
+
|
|
211
|
+
Stream all? [Y/n] or enter numbers to select (e.g., 1,3):
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### plexus reset
|
|
215
|
+
|
|
216
|
+
Clear all configuration — API key, device ID, and settings. Run `plexus start` again to set up from scratch.
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
plexus reset # Prompts for confirmation
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Direct HTTP
|
|
223
|
+
|
|
224
|
+
Send data programmatically without the managed agent. Good for scripts, batch uploads, and custom integrations.
|
|
225
|
+
|
|
226
|
+
1. Create an API key at [app.plexus.company](https://app.plexus.company) → Settings → Developer
|
|
227
|
+
2. Send data:
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from plexus import Plexus
|
|
231
|
+
|
|
232
|
+
px = Plexus(api_key="plx_xxxxx", source_id="test-rig-01")
|
|
233
|
+
|
|
234
|
+
# Numeric telemetry
|
|
235
|
+
px.send("engine.rpm", 3450, tags={"unit": "A"})
|
|
236
|
+
px.send("coolant.temperature", 82.3)
|
|
237
|
+
|
|
238
|
+
# State and configuration
|
|
239
|
+
px.send("vehicle.state", "RUNNING")
|
|
240
|
+
px.send("motor.enabled", True)
|
|
241
|
+
px.send("position", {"x": 1.5, "y": 2.3, "z": 0.8})
|
|
242
|
+
|
|
243
|
+
# Batch send
|
|
244
|
+
px.send_batch([
|
|
245
|
+
("temperature", 72.5),
|
|
246
|
+
("pressure", 1013.25),
|
|
247
|
+
("vibration.rms", 0.42),
|
|
248
|
+
])
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
See [API.md](https://github.com/plexus-oss/plexus-python/blob/main/API.md) for curl, JavaScript, Go, and Bash examples.
|
|
252
|
+
|
|
253
|
+
## Sessions
|
|
254
|
+
|
|
255
|
+
Group related data for analysis and playback:
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
with px.session("thermal-cycle-001"):
|
|
259
|
+
while running:
|
|
260
|
+
px.send("temperature", read_temp())
|
|
261
|
+
px.send("vibration.rms", read_accel())
|
|
262
|
+
time.sleep(0.01)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Sensors
|
|
266
|
+
|
|
267
|
+
Auto-detect all connected I2C sensors:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
from plexus import Plexus
|
|
271
|
+
from plexus.sensors import auto_sensors
|
|
272
|
+
|
|
273
|
+
hub = auto_sensors() # finds IMU, environmental, etc.
|
|
274
|
+
hub.run(Plexus()) # streams forever
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Or configure manually:
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
from plexus.sensors import SensorHub, MPU6050, BME280
|
|
281
|
+
|
|
282
|
+
hub = SensorHub()
|
|
283
|
+
hub.add(MPU6050(sample_rate=100))
|
|
284
|
+
hub.add(BME280(sample_rate=1))
|
|
285
|
+
hub.run(Plexus())
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Built-in sensor drivers:
|
|
289
|
+
|
|
290
|
+
| Sensor | Type | Metrics | Interface |
|
|
291
|
+
| -------- | -------------- | --------------------------------------------------------- | ---------- |
|
|
292
|
+
| MPU6050 | 6-axis IMU | accel_x/y/z, gyro_x/y/z | I2C (0x68) |
|
|
293
|
+
| MPU9250 | 6-axis IMU | accel_x/y/z, gyro_x/y/z | I2C (0x68) |
|
|
294
|
+
| BME280 | Environmental | temperature, humidity, pressure | I2C (0x76) |
|
|
295
|
+
| INA219 | Current/Power | bus_voltage, shunt_voltage, current_ma, power_mw | I2C (0x40) |
|
|
296
|
+
| SHT3x | Temp/Humidity | temperature, humidity | I2C (0x44) |
|
|
297
|
+
| BH1750 | Ambient Light | illuminance | I2C (0x23) |
|
|
298
|
+
| VL53L0X | Time-of-Flight | distance_mm | I2C (0x29) |
|
|
299
|
+
| ADS1115 | 16-bit ADC | channel_0, channel_1, channel_2, channel_3 | I2C (0x48) |
|
|
300
|
+
| QMC5883L | Magnetometer | mag_x, mag_y, mag_z, heading | I2C (0x0D) |
|
|
301
|
+
| HMC5883L | Magnetometer | mag_x, mag_y, mag_z, heading | I2C (0x1E) |
|
|
302
|
+
| GPS | GPS Receiver | lat, lon, altitude, speed | Serial |
|
|
303
|
+
| System | System health | cpu.temperature, memory.used_pct, disk.used_pct, cpu.load | None |
|
|
304
|
+
|
|
305
|
+
### Custom Sensors
|
|
306
|
+
|
|
307
|
+
Write a driver for any hardware by extending `BaseSensor`:
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from plexus.sensors import BaseSensor, SensorReading
|
|
311
|
+
|
|
312
|
+
class StrainGauge(BaseSensor):
|
|
313
|
+
name = "StrainGauge"
|
|
314
|
+
description = "Load cell strain gauge via ADC"
|
|
315
|
+
metrics = ["strain", "force_n"]
|
|
316
|
+
|
|
317
|
+
def read(self):
|
|
318
|
+
raw = self.adc.read_channel(0)
|
|
319
|
+
strain = (raw / 4096.0) * self.calibration_factor
|
|
320
|
+
return [
|
|
321
|
+
SensorReading("strain", round(strain, 6)),
|
|
322
|
+
SensorReading("force_n", round(strain * self.k_factor, 2)),
|
|
323
|
+
]
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## CAN Bus
|
|
327
|
+
|
|
328
|
+
Read CAN bus data with optional DBC signal decoding:
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
from plexus import Plexus
|
|
332
|
+
from plexus.adapters import CANAdapter
|
|
333
|
+
|
|
334
|
+
px = Plexus(api_key="plx_xxx", source_id="vehicle-001")
|
|
335
|
+
adapter = CANAdapter(
|
|
336
|
+
interface="socketcan",
|
|
337
|
+
channel="can0",
|
|
338
|
+
dbc_path="vehicle.dbc", # optional: decode signals
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
with adapter:
|
|
342
|
+
while True:
|
|
343
|
+
for metric in adapter.poll():
|
|
344
|
+
px.send(metric.name, metric.value, tags=metric.tags)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Supports socketcan, pcan, vector, kvaser, and slcan interfaces. See `examples/can_basic.py` for more.
|
|
348
|
+
|
|
349
|
+
## MAVLink (Drones / UAVs)
|
|
350
|
+
|
|
351
|
+
Stream telemetry from MAVLink-speaking vehicles — ArduPilot, PX4, and other autopilots:
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
from plexus import Plexus
|
|
355
|
+
from plexus.adapters import MAVLinkAdapter
|
|
356
|
+
|
|
357
|
+
px = Plexus(api_key="plx_xxx", source_id="drone-001")
|
|
358
|
+
adapter = MAVLinkAdapter(
|
|
359
|
+
connection_string="udpin:0.0.0.0:14550", # SITL or GCS
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
with adapter:
|
|
363
|
+
while True:
|
|
364
|
+
for metric in adapter.poll():
|
|
365
|
+
px.send(metric.name, metric.value, tags=metric.tags)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Decoded metrics include attitude (roll/pitch/yaw), GPS, battery, airspeed, RC channels, and more. Supports UDP, TCP, and serial connections. See `examples/mavlink_basic.py` for more.
|
|
369
|
+
|
|
370
|
+
## MQTT Bridge
|
|
371
|
+
|
|
372
|
+
Forward MQTT messages to Plexus:
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
from plexus.adapters import MQTTAdapter
|
|
376
|
+
|
|
377
|
+
adapter = MQTTAdapter(broker="localhost", topic="sensors/#")
|
|
378
|
+
adapter.connect()
|
|
379
|
+
adapter.run(on_data=my_callback)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Buffering and Reliability
|
|
383
|
+
|
|
384
|
+
The client buffers data locally when the network is unavailable:
|
|
385
|
+
|
|
386
|
+
- In-memory buffer (default, up to 10,000 points)
|
|
387
|
+
- Persistent SQLite buffer for surviving restarts
|
|
388
|
+
- Automatic retry with exponential backoff
|
|
389
|
+
- Buffered points are sent with the next successful request
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
# Enable persistent buffering
|
|
393
|
+
px = Plexus(persistent_buffer=True)
|
|
394
|
+
|
|
395
|
+
# Check buffer state
|
|
396
|
+
print(px.buffer_size())
|
|
397
|
+
px.flush_buffer()
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Live Terminal Dashboard
|
|
401
|
+
|
|
402
|
+
The TUI launches by default when you run `plexus start` — no flags needed. It gives you a real-time view of everything streaming from your device, like htop for your hardware. Headless mode is used automatically when output is piped or in a non-TTY environment.
|
|
403
|
+
|
|
404
|
+
Keyboard shortcuts: `q` quit | `p` pause | `s` scroll metrics | `?` help
|
|
405
|
+
|
|
406
|
+
```
|
|
407
|
+
+--------------------------------------------------------------+
|
|
408
|
+
| Plexus Live Dashboard * online ^ 4m 32s |
|
|
409
|
+
+--------------------------------------------------------------+
|
|
410
|
+
| Metric | Value | Rate | Buffer | Status |
|
|
411
|
+
+--------------+----------+--------+--------+------------------+
|
|
412
|
+
| cpu.temp | 62.3 | 1.0 Hz | 0 | * streaming |
|
|
413
|
+
| engine.rpm | 3,450 | 10 Hz | 0 | * streaming |
|
|
414
|
+
| pressure | 1013.2 | 1.0 Hz | 0 | * streaming |
|
|
415
|
+
+--------------+----------+--------+--------+------------------+
|
|
416
|
+
| Throughput: 12 pts/min Total: 847 Errors: 0 |
|
|
417
|
+
+--------------------------------------------------------------+
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Requires the `tui` extra: `pip install plexus-python[tui]`
|
|
421
|
+
|
|
422
|
+
## Troubleshooting
|
|
423
|
+
|
|
424
|
+
**Permission denied on I2C**
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
sudo usermod -aG i2c $USER && reboot
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**API key invalid**
|
|
431
|
+
Verify your key at [app.plexus.company/devices](https://app.plexus.company/devices). Keys start with `plx_`.
|
|
432
|
+
|
|
433
|
+
**No sensors detected**
|
|
434
|
+
Check wiring, pull-up resistors, and that sensors are on I2C bus 1 (default). Set `PLEXUS_I2C_BUS` environment variable to use a different bus.
|
|
435
|
+
|
|
436
|
+
**TUI not showing**
|
|
437
|
+
Install the TUI extra: `pip install plexus-python[tui]`. The TUI also requires a real terminal — it will not render when output is piped or in a non-TTY environment.
|
|
438
|
+
|
|
439
|
+
**Behind a proxy or firewall**
|
|
440
|
+
Set `PLEXUS_ENDPOINT` to your proxy URL. Ensure outbound access on ports 443 and 80.
|
|
441
|
+
|
|
442
|
+
**Something else?**
|
|
443
|
+
Try `plexus reset` to clear config and start fresh, or check the [API docs](https://github.com/plexus-oss/plexus-python/blob/main/API.md) for protocol details.
|
|
444
|
+
|
|
445
|
+
## Architecture
|
|
446
|
+
|
|
447
|
+
```
|
|
448
|
+
Device (plexus start)
|
|
449
|
+
+-- WebSocket -> PartyKit Server -> Dashboard (real-time)
|
|
450
|
+
+-- HTTP POST -> /api/ingest -> ClickHouse (storage)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
- **WebSocket path**: Used by `plexus start` for real-time streaming controlled from the dashboard. Data flows through the PartyKit relay to connected browsers.
|
|
454
|
+
- **HTTP path**: Used by the `Plexus()` client for direct data ingestion. Data is stored in ClickHouse for historical queries.
|
|
455
|
+
|
|
456
|
+
When recording a session, both paths are used — WebSocket for live view, HTTP for persistence.
|
|
457
|
+
|
|
458
|
+
## API Reference
|
|
459
|
+
|
|
460
|
+
See [API.md](https://github.com/plexus-oss/plexus-python/blob/main/API.md) for the full HTTP and WebSocket protocol specification, including:
|
|
461
|
+
|
|
462
|
+
- Request/response formats
|
|
463
|
+
- All message types
|
|
464
|
+
- Code examples in Python, JavaScript, Go, and Bash
|
|
465
|
+
- Error codes
|
|
466
|
+
- Best practices
|
|
467
|
+
|
|
468
|
+
## License
|
|
469
|
+
|
|
470
|
+
Apache 2.0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
plexus/__init__.py,sha256=rZezsihttTOdvSu6yS4IUFxLA6fmGyqetam9U-ntUFA,754
|
|
2
|
+
plexus/__main__.py,sha256=lII5-SW3EnmuL8JPinoJnTGAbwDtGIdL724NTxgtZ3Y,85
|
|
3
|
+
plexus/buffer.py,sha256=3ykybqLs7yMXxQWFajAT8nGe3cs_lW8_6Xvn0vQ69dE,9262
|
|
4
|
+
plexus/cli.py,sha256=FV4Zz4EJ4ZyHGz_U4mriWwFdlqLWE00LIE3yq416rWk,27159
|
|
5
|
+
plexus/client.py,sha256=jtjAVVbOyA0HqInChAiurJ0VxLrfST2b3OlWP1S2a-4,16133
|
|
6
|
+
plexus/config.py,sha256=wc7e9sjX9GWo8M4kuB8stlQaO60KwVNh8vwP21psEmQ,4831
|
|
7
|
+
plexus/connector.py,sha256=a38ZYUvrIZfH8zlKrlcllljz7Yee7MO3CB2Yye2420E,26629
|
|
8
|
+
plexus/deps.py,sha256=qRnGuPV8GjDwxrTV5NU_TKfzmnHy_-hsZnREZx1aeJg,7446
|
|
9
|
+
plexus/detect.py,sha256=iuqk_oazMISdatMAGN8XZkF3GU3wsA_buQkcMptcL04,43385
|
|
10
|
+
plexus/streaming.py,sha256=qhUD066fUJGIiG6CaSGgRQENMVhmfOIH08Z7atzLR1o,28673
|
|
11
|
+
plexus/tui.py,sha256=wrxSWCa-F29Z4kEM7vfUTSr5xVREQAzulkPHPD5nTAQ,24120
|
|
12
|
+
plexus/adapters/__init__.py,sha256=szM0ClEVMxM8Y7bH1Vi01wQmr9AInEKbidhX-kumHWE,3487
|
|
13
|
+
plexus/adapters/base.py,sha256=p6V5zPq0roQ0mN271DhIAyrAklpaTaO2v2ug1CRys-4,12879
|
|
14
|
+
plexus/adapters/ble.py,sha256=13OJMIyt4LCMCXPSXNF-t5euC_vBi3t7NDdEGTZn_A0,9021
|
|
15
|
+
plexus/adapters/can.py,sha256=fjWE1ZXkPZJ_CNBCQf7r21aXHwzmT_i3lIe89GmTzQM,13650
|
|
16
|
+
plexus/adapters/can_detect.py,sha256=QuTG83mi6oEBlFmc8gQUJCAkdokvSqV7xwr0f_Ogd_E,4872
|
|
17
|
+
plexus/adapters/mavlink.py,sha256=9quxKjf5NgwQJCPzEhAXxu1LaSexfSkI2Zt11reCPiU,23130
|
|
18
|
+
plexus/adapters/mavlink_detect.py,sha256=Ianff8Dm2ON_wn-YXx6Etzs8M858Sf_cAXohFTcx5m0,5832
|
|
19
|
+
plexus/adapters/modbus.py,sha256=GuPj_hkA6SBCRqL8_YNEiqbrOq740l5sKL3_E55XcsY,20743
|
|
20
|
+
plexus/adapters/mqtt.py,sha256=ymeLPYKqwes2vRZiGQwJKdfS9t4fOnei0ZsB9kfiNjw,11044
|
|
21
|
+
plexus/adapters/opcua.py,sha256=X7jY8sP_mmfPOdeysyFSszKnA5unCduIW4ib7Gj6fM8,21818
|
|
22
|
+
plexus/adapters/registry.py,sha256=QaB6En-ZVbg7ro4WIsWooBZBoi5nQvirtmu8PNfPpeA,5955
|
|
23
|
+
plexus/adapters/serial_adapter.py,sha256=8wLg7KtKl7LeabMI_07NGPlHpC0vOo1sN-Py8s8fruU,16349
|
|
24
|
+
plexus/cameras/__init__.py,sha256=G1XTDwwi426bVsYRElOKFlyPyNJAHYnlbbpmzaEbB-M,1320
|
|
25
|
+
plexus/cameras/auto.py,sha256=D3_OcDLm96f43UGdx62wMgzhvN_R1_EDuQbrnmjUFkQ,6745
|
|
26
|
+
plexus/cameras/base.py,sha256=36PI0kA3549W49JfagAfS2yVHtanntPP4ebOkS5OR1c,5241
|
|
27
|
+
plexus/cameras/picamera.py,sha256=N1BkbWh959CJ5EzuZmaernN55qaUWsKgjKZ18giiRD0,5195
|
|
28
|
+
plexus/cameras/usb.py,sha256=Kj9gLEZ0IZboV4qhb6x1u1OAuXb89TdTCi6_wvJ282E,4266
|
|
29
|
+
plexus/importers/__init__.py,sha256=pU_t0TxiFvgq-XewMXgUZv27fqmJgJ03Hik4wQ2Brng,553
|
|
30
|
+
plexus/importers/rosbag.py,sha256=hbbxryhbmTl9un1SvYR56Or8783esUQ7jZA9TyyYcDo,26178
|
|
31
|
+
plexus/sensors/__init__.py,sha256=ksXLf9VPi-ioTudCHoLL8gWSx_R0DQu78ZuWtEbmHOk,3069
|
|
32
|
+
plexus/sensors/ads1115.py,sha256=eETzad_czByeEJ-4JuF2ltG2Ovg2DlHzoWJglxQ1TII,4610
|
|
33
|
+
plexus/sensors/adxl345.py,sha256=5LhKAgbAc_SG816PoDRE8-YUm6pHvdcxa423wSICNIc,5481
|
|
34
|
+
plexus/sensors/auto.py,sha256=URYxKi2LYqbV4KHue32koc-O4Kv1yiggfCXM9WAbM3M,8141
|
|
35
|
+
plexus/sensors/base.py,sha256=V1ZevJLFCsRZveQWupiZfZ-C6nClQ4b_7_IdfMVPCPE,14626
|
|
36
|
+
plexus/sensors/bh1750.py,sha256=yvIDMb3Jn1MMJnJOIbUuHPIiH9yGy7A_mP7MqIYkxC4,2811
|
|
37
|
+
plexus/sensors/bme280.py,sha256=A3WqtO-Rv1FlR-r3QI4IILEF3WozR0yPgnUa2iCHAL8,8150
|
|
38
|
+
plexus/sensors/gps.py,sha256=E6OXUM0ho5puneQvWgnAXobM3v2zIJNDfRvIvkvOeIc,9587
|
|
39
|
+
plexus/sensors/ina219.py,sha256=k9rWeR1gec3Gltoy9PVAYrE4Y0Z8HMTdbKTOeYTxRpo,4745
|
|
40
|
+
plexus/sensors/magnetometer.py,sha256=U177kfiAUdsUwc7rrz0MeFJ0ukC_f-_GCWqbHX7hrpI,7384
|
|
41
|
+
plexus/sensors/mpu6050.py,sha256=e-YeOOx-ya-_olacqL759xENGeZDybBP1VctJrgIAl4,4991
|
|
42
|
+
plexus/sensors/sht3x.py,sha256=vCn-qcPKuy1HCjfCvjmGRF0uDJgYVrIpmSsCd9y9jOk,4119
|
|
43
|
+
plexus/sensors/spi_scan.py,sha256=-JbDrux_f7hzK9uqmfsMFFddgaJitQZGLxTve2rOW-g,4169
|
|
44
|
+
plexus/sensors/system.py,sha256=bSA7VnSZouxzemxDdtGi4SNxE5tnmb4M12-N8u7ru4I,9072
|
|
45
|
+
plexus/sensors/vl53l0x.py,sha256=9AFNFErt185ZHqYdw76f3aRmGqqI6KXphlm_1fui1Y0,3117
|
|
46
|
+
plexus_python-0.1.0.dist-info/METADATA,sha256=kzgqWUsZI-FiLO_psgCo1fVKbMBFe0OI0IceIVRErJY,16652
|
|
47
|
+
plexus_python-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
48
|
+
plexus_python-0.1.0.dist-info/entry_points.txt,sha256=YlkOtTn_7Q_IGuJaKdvpU-90dCeBSPx2p_UTGMAz5Zs,43
|
|
49
|
+
plexus_python-0.1.0.dist-info/licenses/LICENSE,sha256=nm3qP1F-JAGcfLpRVtIX24L20LMnRpxmZ2oKZzFpLVo,10755
|
|
50
|
+
plexus_python-0.1.0.dist-info/RECORD,,
|