# 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 requirements.txt ``` ## Requirements ``` pip install weconnect PyQt5 pyqtgraph ``` ## Collector (`collect.py`) Connects to WeConnect, polls selected domains at a configurable interval, and appends records to a JSON file. Simultaneously runs a TCP push server so clients receive every new snapshot the moment it is collected. ### Quick start ```bash # Credentials via environment variables, all defaults export WC_USER=me@example.com export WC_PASS=secret python collect.py # Credentials from a JSON file, 10-minute interval python collect.py -c credentials/my_car.json -i 600 ``` ### Credentials file (`-c FILE`) ```json { "username": "me@example.com", "password": "secret", "vin": "WVWZZZE1ZMP000000", "domains": ["charging", "measurements", "readiness"] } ``` All keys are optional; explicit CLI flags and `WC_USER` / `WC_PASS` environment variables take precedence. ### CLI reference | Flag | Default | Description | |------|---------|-------------| | `-c FILE` | — | JSON credentials file | | `-u / -p` | `WC_USER` / `WC_PASS` | Username / password | | `--vin` | first vehicle | Target VIN | | `-d DOMAINS` | `charging,measurements,readiness` | Comma-separated domains or `all` | | `-i SECONDS` | `300` | Collection interval | | `-o FILE` | `logs/YYYY_MM_DD_HH_MM_SS_.json` | Output file | | `--max-records N` | unlimited | Keep only last N records | | `--host` | `0.0.0.0` | Push server bind address | | `--port` | `9999` | Push server TCP port | | `-v` | — | Verbose / debug logging | | `--list-domains` | — | Print available domains and exit | **Available domains:** `charging`, `climatisation`, `measurements`, `readiness`, `parking`, `access` ### Log rotation When using the default `logs/` directory the collector automatically starts a new file after midnight UTC, keeping all previous files intact. On startup the full day's records are pre-loaded from disk so newly connected clients receive the complete history. ### Output format Each file is a JSON object: ```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 `--host`:`--port` (default `0.0.0.0:9999`). * On connect: sends all records from the current day, sorted by timestamp. * On each new poll: sends the new snapshot immediately. All messages are **newline-delimited JSON** (one record per line, `\n` terminated). ## GUI client (`gui_client.py`) ```bash python gui_client.py python gui_client.py --host 192.168.1.10 --port 9999 ``` Dark theme matching nmea_client. Three tabs: ### Connector tab Host / port fields, Connect / Disconnect button, and connection status. ### 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: * **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. Trace selections are persisted to `~/.config/we_monitor/plot_settings.json` and restored automatically on the next launch once matching signals arrive. Signal labels use short human-readable names (e.g. *SOC*, *range*, *outside temp*) defined in `_TRACE_LABELS` near the top of `gui_client.py`. ## CLI test client (`client.py`) ```bash python client.py 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.