Server (collect.py --diff): - History burst: first record sent full, subsequent as jay_diff_full diffs - Live broadcast: each snapshot diffed against the previous one - Diff payloads tagged with "_diff": true Client (client.py --diff): - Reconstructs full state via jay_merge_full before printing GUI (Connector tab "Diff mode" checkbox): - TcpReader.connect_to() accepts diff_mode; maintains _state dict - Merges incoming diffs before emitting to Dashboard/Plot - Checkbox state persisted to plot_settings.json Both sides default to off; must be enabled consistently on server and client. utils/__init__.py added so utils.jay_diff is importable as a package. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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:
./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
python collect.py -c credentials/my_car.json
Username and password can also be supplied via environment variables (override the credentials file):
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:
{
"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
Only a handful of flags remain; all vehicle/server settings are in the credentials file.
| Flag | Description |
|---|---|
-c FILE |
Credentials / settings file (required) |
-u / -p |
Override username / password (also WC_USER / WC_PASS) |
-d DOMAINS |
Override domains (comma-separated or all) |
--max-records N |
Keep only the last N records per file |
-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_<VIN>.json and contain:
{
"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).
GUI client (gui_client.py)
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)
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.