Update README for server/client split

Reflect new layout (server/, client/), separate requirements.txt files,
python -m server.main entry point, and server/install.sh location.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 18:35:03 +02:00
co-authored by Claude Sonnet 4.6
parent ca459c5ce9
commit 5b1de48b75
+46 -58
View File
@@ -7,45 +7,40 @@ A PyQt5 GUI client visualises the data in real time.
## Repository layout ## Repository layout
``` ```
collect.py — collector daemon (entry point)
gui_client.py — PyQt5 GUI client
client.py — minimal CLI test client
server/ server/
main.py — collector entry point (CLI + credential loading)
collect.py — run() main loop
data_model.py — domain extractors, collect_snapshot() data_model.py — domain extractors, collect_snapshot()
network.py — TCP push server (accept loop + broadcast) network.py — TCP push server (accept loop + broadcast)
storage_helpers.py — load / save JSON store, midnight rotation helpers storage_helpers.py — load / save JSON store, midnight rotation helpers
we_connect.py — CarConnectivity login and vehicle selection we_connect.py — CarConnectivity login and vehicle selection
log_config.py — logging setup log_config.py — logging setup
utils/ requirements.txt — server dependencies
jay_diff.pyJSON diff / merge library install.sh systemd user service installer (server only)
client/
gui_client.py — PyQt5 GUI client
client.py — minimal CLI test client
requirements.txt — client dependencies
credentials/ — JSON config files (gitignored) credentials/ — JSON config files (gitignored)
requirements.txt
``` ```
## Requirements ## Server
``` ### Install (systemd user service)
pip install carconnectivity carconnectivity-connector-volkswagen PyQt5 pyqtgraph
```
Or use the installer which also sets up a systemd user service:
```bash ```bash
cd server
chmod +x install.sh
./install.sh ./install.sh
# then edit ~/.config/we_monitor/credentials.json
systemctl --user start we_monitor
``` ```
--- ### Manual start
## Collector (`collect.py`)
Connects via CarConnectivity, polls selected domains at a configurable interval, and
appends timestamped records to a rotating JSON log. Simultaneously runs a TCP
push server so clients receive every new snapshot the moment it is collected.
### Quick start
```bash ```bash
python collect.py -c credentials/my_car.json pip install -r server/requirements.txt
python -m server.main -c credentials/my_car.json
``` ```
Username and password can also be supplied via environment variables Username and password can also be supplied via environment variables
@@ -54,7 +49,7 @@ Username and password can also be supplied via environment variables
```bash ```bash
export WC_USER=me@example.com export WC_USER=me@example.com
export WC_PASS=secret export WC_PASS=secret
python collect.py -c credentials/my_car.json python -m server.main -c credentials/my_car.json
``` ```
### Credentials file ### Credentials file
@@ -166,27 +161,33 @@ The first message sent to a new client is always `jay_diff_full({}, record[0])`,
i.e. an `update_add` containing every field of the first record. Subsequent i.e. an `update_add` containing every field of the first record. Subsequent
messages contain only the fields that changed. messages contain only the fields that changed.
Enable `--diff` on the server **and** on the client (`client.py --diff` or the Enable `--diff` on the server **and** on the client (`client/client.py --diff` or the
**Diff mode** checkbox in the GUI Connector tab). Both sides must agree — a **Diff mode** checkbox in the GUI Connector tab). Both sides must agree — a
mismatch produces garbled output. mismatch produces garbled output.
--- ---
## GUI client (`gui_client.py`) ## Client
```bash ```bash
python gui_client.py pip install -r client/requirements.txt
```
### GUI client (`client/gui_client.py`)
```bash
python client/gui_client.py
``` ```
Dark-theme PyQt5 application. Three tabs: **Connector**, **Dashboard**, Dark-theme PyQt5 application. Three tabs: **Connector**, **Dashboard**,
**Plot**. Connection settings, trace selections, and time windows are persisted **Plot**. Connection settings, trace selections, and time windows are persisted
to `~/.config/we_monitor/plot_settings.json` and restored on the next launch. to `~/.config/we_monitor/plot_settings.json` and restored on the next launch.
### Connector tab #### Connector tab
| Control | Description | | Control | Description |
|---------|-------------| |---------|-------------|
| **Host / Port** | Address of the `collect.py` push server | | **Host / Port** | Address of the push server |
| **Diff mode** checkbox | Must match the server's `--diff` setting | | **Diff mode** checkbox | Must match the server's `--diff` setting |
| **Connect / Disconnect** | Opens or closes the TCP connection | | **Connect / Disconnect** | Opens or closes the TCP connection |
| **Status** | Shows `Connected`, `Disconnected`, or the error message | | **Status** | Shows `Connected`, `Disconnected`, or the error message |
@@ -195,18 +196,18 @@ Clicking **Connect** saves the current host, port, and diff-mode setting. All
plot data is cleared on each connect so the fresh server history is never mixed plot data is cleared on each connect so the fresh server history is never mixed
with stale local data. with stale local data.
### Dashboard tab #### Dashboard tab
Live tree view of the most recent snapshot. The hierarchy mirrors the JSON Live tree view of the most recent snapshot. The hierarchy mirrors the JSON
structure: domain → status object → field, with value and unit shown inline. structure: domain → status object → field, with value and unit shown inline.
Expand / collapse state is preserved across updates. Expand / collapse state is preserved across updates.
### Plot tab #### Plot tab
Eight plots arranged in a 4 × 2 scrollable grid. Each plot is independent and Eight plots arranged in a 4 × 2 scrollable grid. Each plot is independent and
has its own control bar. has its own control bar.
#### Control bar (per plot) **Control bar (per plot)**
| Control | Description | | Control | Description |
|---------|-------------| |---------|-------------|
@@ -216,7 +217,7 @@ has its own control bar.
| **Time window spinner** | Show the last 124 hours of data (per plot) | | **Time window spinner** | Show the last 124 hours of data (per plot) |
| **Date label** (right-aligned) | Shows the calendar date(s) of the visible window — single date or `YYYY-MM-DD YYYY-MM-DD` when the view spans midnight | | **Date label** (right-aligned) | Shows the calendar date(s) of the visible window — single date or `YYYY-MM-DD YYYY-MM-DD` when the view spans midnight |
#### Plot area **Plot area**
* **X axis** — time of day in `HH:MM:SS` format; no locale-dependent date prefix. * **X axis** — time of day in `HH:MM:SS` format; no locale-dependent date prefix.
* **Y axis** — auto-zooms to fit the visible data on the first draw after connect * **Y axis** — auto-zooms to fit the visible data on the first draw after connect
@@ -227,7 +228,7 @@ has its own control bar.
sampled value for every active trace, with a colored bullet and unit. sampled value for every active trace, with a colored bullet and unit.
* **Legend** — trace names and colors, shown inside the plot area. * **Legend** — trace names and colors, shown inside the plot area.
#### Mouse interaction **Mouse interaction**
| Action | Effect | | Action | Effect |
|--------|--------| |--------|--------|
@@ -236,13 +237,11 @@ has its own control bar.
| Click + drag | Pan the view | | Click + drag | Pan the view |
| Right-click | pyqtgraph context menu (export, auto-range, …) | | Right-click | pyqtgraph context menu (export, auto-range, …) |
--- ### CLI test client (`client/client.py`)
## CLI test client (`client.py`)
```bash ```bash
python client.py --host 192.168.1.10 --port 9999 python client/client.py --host 192.168.1.10 --port 9999
python client.py --diff # reconstruct from diff stream python client/client.py --diff # reconstruct from diff stream
``` ```
Connects to the push server and prints every incoming snapshot as Connects to the push server and prints every incoming snapshot as
@@ -258,11 +257,15 @@ pretty-printed JSON with a running record counter.
## Module reference ## Module reference
### `collect.py` ### `server/main.py`
Entry point for the collector daemon. Parses CLI / credentials-file arguments, Entry point for the collector daemon. Parses CLI / credentials-file arguments
establishes the CarConnectivity session, starts the push server, and runs the main and calls `run()`.
poll loop.
### `server/collect.py`
`run(args)` — establishes the CarConnectivity session, starts the push server,
and runs the main poll loop.
### `server/data_model.py` ### `server/data_model.py`
@@ -315,22 +318,7 @@ Authenticates and returns a connected `CarConnectivity` instance.
`select_vehicle(wc, vin) → (vehicle, error)` `select_vehicle(wc, vin) → (vehicle, error)`
Returns the target vehicle (first available if `vin` is empty) or an error string. Returns the target vehicle (first available if `vin` is empty) or an error string.
### `utils/jay_diff.py` ### `client/gui_client.py` — class overview
Lightweight JSON diff / patch library.
`jay_diff_full(old, new, combine_upd_add=True) → dict`
Computes a diff between two dicts. With `combine_upd_add=True` (the default
used everywhere in this project) the result uses `update_add` for all additions
and updates, plus a separate `delete` key. Returns `{}` when nothing changed.
`jay_merge_full(old, diff) → dict`
Applies a `jay_diff_full` diff to `old`. The diff structure is self-describing:
if `diff` contains none of the known diff keys (`update`, `update_add`, `add`,
`delete`) and `old` is empty, `diff` is returned as-is (full-snapshot
passthrough for bootstrap).
### `gui_client.py` — class overview
| Class | Role | | Class | Role |
|-------|------| |-------|------|