# we_monitor Periodically polls a VW WeConnect vehicle via the `weconnect` library, stores timestamped records as JSON, and pushes live data to connected TCP clients. A PyQt5 GUI client visualises the data in real time. ## Repository layout ``` collect.py — collector daemon (entry point) gui_client.py — PyQt5 GUI client client.py — minimal CLI test client server/ data_model.py — domain extractors, collect_snapshot() network.py — TCP push server (accept loop + broadcast) storage_helpers.py— load / save JSON store, midnight rotation helpers we_connect.py — WeConnect login and vehicle selection log_config.py — logging setup credentials/ — JSON config files (gitignored) requirements.txt ``` ## Requirements ``` pip install weconnect PyQt5 pyqtgraph ``` Or use the installer which also sets up a systemd user service: ```bash ./install.sh ``` ## Collector (`collect.py`) Connects to WeConnect, polls selected domains at a configurable interval, and appends 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 python collect.py -c credentials/my_car.json ``` Username and password can also be supplied via environment variables (override the credentials file): ```bash export WC_USER=me@example.com export WC_PASS=secret python collect.py -c credentials/my_car.json ``` ### Credentials file All connection and collection settings live in a single JSON file: ```json { "credentials": { "username": "me@example.com", "password": "secret" }, "vin": "WVWZZZE1ZMP000000", "domains": ["charging", "measurements", "readiness"], "interval_s": 300, "host": "0.0.0.0", "port": 9999, "log_dir": "./logs" } ``` | Key | Default | Description | |-----|---------|-------------| | `credentials.username` | — | WeConnect / MyVolkswagen email | | `credentials.password` | — | WeConnect password | | `vin` | first vehicle | Target VIN | | `domains` | `["charging","measurements","readiness"]` | Domains to collect (list or `"all"`) | | `interval_s` | `300` | Collection interval in seconds | | `host` | `"0.0.0.0"` | Push server bind address | | `port` | `9999` | Push server TCP port | | `log_dir` | `"./logs"` | Directory for rotating JSON log files | ### CLI reference CLI flags override the corresponding credentials file value when given. | Flag | Overrides | Description | |------|-----------|-------------| | `-c FILE` | — | Credentials / settings file | | `-u / -p` | `credentials.username/password` | Username / password (also `WC_USER` / `WC_PASS`) | | `-d DOMAINS` | `domains` | Domains to collect (comma-separated or `all`) | | `-i SECONDS` | `interval_s` | Collection interval in seconds | | `--host HOST` | `host` | Push server bind address | | `--port PORT` | `port` | Push server TCP port | | `--log-dir DIR` | `log_dir` | Directory for rotating log files | | `--max-records N` | — | Keep only the last N records per file | | `--diff` | — | Send diffs instead of full snapshots (must match client setting) | | `--dry` | — | Skip WeConnect; run push server only (for testing) | | `-v` | — | Verbose / debug logging | | `--list-domains` | — | Print available domains and exit | **Available domains:** `charging`, `climatisation`, `measurements`, `readiness`, `parking`, `access` ### Log rotation A new file is started automatically after midnight UTC; previous files are kept intact. On startup and on each new client connection the collector sends the last 24 hours of records, so clients always receive a full rolling window regardless of when midnight falls. ### Output format Log files are named `YYYY_MM_DD_HH_MM_SS_.json` and contain: ```json { "meta": { "vin": "...", "created_at": "...", "interval_seconds": 300 }, "records": [ { "ts": "2025-05-25T10:00:00+00:00", "charging": { "batteryStatus": { "currentSOC": { "value": 80, "unit": "%" }, "cruisingRangeElectric": { "value": 310, "unit": "km" } } } } ] } ``` Physical values are always `{"value": …, "unit": …}` objects. ## Push server protocol The server listens on the `host`:`port` from the credentials file (default `0.0.0.0:9999`). * On connect: sends all records from the last 24 hours, sorted by timestamp. * On each new poll: sends the new snapshot immediately. All messages are **newline-delimited JSON** (one record per line, `\n` terminated). ### Diff mode (`--diff`) When enabled on the server, the history burst is compressed: the first record is sent in full, subsequent records as `jay_diff_full` diffs (`{"_diff": true, "update_add": {…}, "delete": {…}}`). Live snapshots are also diffed against the previous broadcast. Enable `--diff` on the client side (`client.py`) or check **Diff mode** in the GUI Connector tab to reconstruct full snapshots from the stream. Both sides must agree — a mismatch produces garbled output. ## GUI client (`gui_client.py`) ```bash python gui_client.py ``` Dark theme. Three tabs: ### Connector tab Enter host and port, then click **Connect**. Settings are saved automatically and restored on the next launch. All plot data is cleared on each connect so fresh server history is never mixed with stale local data. ### Dashboard tab Live tree view of the latest snapshot — domain → status object → field, showing value and unit. Expand state is preserved across updates. ### Plot tab 8 plots (4 × 2) in a scrollable grid. Each plot has its own control bar: * **Time window spinner** — show the last 1–24 hours (per plot). * **Dropdown + `+`** — select a signal and add it as a trace (max 4 per plot). * **Colored chips (`name ×`)** — click to remove a trace. * Zoom / pan with mouse; crosshair cursor tracks mouse position. * When available data is less than the selected window, the x-axis adjusts to the actual data extent — no empty gap on the left. Trace selections, time windows, and connection settings are persisted to `~/.config/we_monitor/plot_settings.json` and restored automatically on the next launch. ## CLI test client (`client.py`) ```bash python client.py --host 192.168.1.10 --port 9999 ``` Connects to the push server and prints every incoming snapshot as pretty-printed JSON with a running record counter.