Metadata-Version: 2.4
Name: depz-sensor-viewer
Version: 0.1.3
Summary: Local standalone launcher for the DEPZ sensor web viewer — bypasses browser WebSerial by talking to devices over pyserial.
Author: DEPZ
License: MIT
Project-URL: Homepage, https://github.com/depz-ai/depz-sensor-sdk-and-viewer
Project-URL: Repository, https://github.com/depz-ai/depz-sensor-sdk-and-viewer
Keywords: depz,sensor,viewer,webserial,pyserial,vl53l8,bno086,hc-sr04
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Classifier: Intended Audience :: Developers
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: depz-sensor-sdk
Requires-Dist: aiohttp>=3.9
Requires-Dist: pyserial>=3.5
Dynamic: license-file

# depz-sensor-viewer

A local, standalone launcher for the DEPZ sensor web viewer. It runs a tiny
localhost web server that serves the viewer SPA **and** talks to your DEPZ
sensors over [`pyserial`](https://pyserial.readthedocs.io/) — so the UI never
touches the browser **WebSerial** API.

## Why

The hosted viewer uses the browser's WebSerial API, which means:

- a **port-picker prompt** every time you connect,
- **Chrome-only** support, and
- failures under some enterprise / managed-Chrome policies that disable
  WebSerial entirely.

This launcher sidesteps all of that. The Python process opens the serial port
locally with `pyserial` and pipes raw bytes to the SPA over a WebSocket. No
prompt, no WebSerial, works in any browser pointed at `localhost`.

## Install & run

```bash
pipx install depz-sensor-viewer
depz-sensor-viewer
```

This starts the server on `http://127.0.0.1:8765` (or a random free port if
8765 is taken) and opens your browser. Options:

```
depz-sensor-viewer --port 9000       # pick a port (0 = random free)
depz-sensor-viewer --host 127.0.0.1  # bind address (localhost by default)
depz-sensor-viewer --no-browser      # just start the server
```

Press **Ctrl-C** to stop.

## Linux: serial permissions

On Linux, opening `/dev/ttyACM*` requires membership in the `dialout` group.
If devices don't open, add yourself and re-login:

```bash
sudo usermod -aG dialout $USER
# then log out and back in (or: newgrp dialout)
```

The viewer diagnoses this for you at `GET /api/permissions` and shows the fix in
the UI. `ModemManager` can also briefly grab a freshly-plugged CDC device; retry
after a second, or `sudo systemctl mask ModemManager` if it persists.

## HTTP / WebSocket contract

The bundled SPA detects standalone mode and drives devices through this fixed
interface (served by `depz_sensor_viewer.server`):

| Endpoint | Response |
| --- | --- |
| `GET /api/health` | `{"standalone": true, "version": "<pkg>"}` |
| `GET /api/devices` | `{"devices": [{path, usb_vid, usb_pid, serial, sensor_type, usb_model_hint}]}` |
| `GET /api/permissions` | `{ok, os, issues, fix: {command, explanation}}` |
| `WS /ws/serial?path=<port>&baud=<n>` | raw byte pipe (see below) |

`/api/devices` enumerates only ports whose USB identity is a known DEPZ device
(via the SDK's USB table); it does **not** open the ports. `baud` defaults to
`115200` and is nominal for the CDC-ACM devices.

**`/ws/serial` framing:**

- client → server **binary** frame → `serial.write(bytes)`
- server → client **binary** frame ← bytes read from the serial port
- **text** frames carry JSON control/status:
  - server: `{"opened": true, "path": ..., "baud": ...}` right after open
  - server: `{"error": "...", "code": "...", "fix": {...}}` on failure
    (permission errors include the per-OS `fix`)
  - client: `{"cmd": "set_baud", "baud": <int>}` (optional)

The port is closed when the WebSocket closes.

## Building the bundled SPA

The wheel ships the built SPA under `depz_sensor_viewer/_static/`. To (re)build
it from the repo's TypeScript viewer:

```bash
python packages/depz-sensor-viewer/scripts/bundle_spa.py
```

This runs `bun run build` at the repo root and copies `dist/client/**` into
`_static/`. Pass `--skip-build` to copy an existing `dist/client/` without
rebuilding. Until you run it, the package ships a placeholder `index.html` that
points here — the API endpoints above are live regardless.

## License

MIT
