Commit Graph
29 Commits
Author SHA1 Message Date
jensandClaude Sonnet 4.6 7d9d928bb1 Move time window to per-plot spinbox in control bar
Replaces the single global toolbar spinbox with one QSpinBox per plot,
placed between the '+' button and the trace chips. Each plot has an
independent 1–24 h window; values are persisted in plot_settings.json
under 'time_windows'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:55:00 +02:00
jensandClaude Sonnet 4.6 02814782bf Add global time window control to Plot tab (1–24 h)
A spinbox above the plot grid sets the visible x range for all 8 plots.
Data outside the window is excluded from each redraw; x axis is fixed to
[now - window, now]. Setting is persisted in plot_settings.json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:40:41 +02:00
jensandClaude Sonnet 4.6 dfdf6a550e install.sh: create venv and install dependencies
Creates .venv/ with python3 -m venv if not present, then installs/
updates requirements.txt into it before setting up the systemd service.
The service ExecStart always uses the venv Python.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:22:31 +02:00
jensandClaude Sonnet 4.6 cf6f092cbb Add install.sh: systemd user service setup
Creates ~/.config/we_monitor/credentials.json template (mode 600),
writes ~/.config/systemd/user/we_monitor.service using the project
venv Python, enables linger for boot-time start, and enables the
service. Does not start until credentials are edited.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 22:59:49 +02:00
jensandClaude Sonnet 4.6 a01ae714c8 Add README.md and conversation_recap.md
README covers installation, collector CLI reference, push server protocol,
GUI client usage, and output format.  conversation_recap documents the full
development history: features, bugs fixed, and design decisions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 22:42:00 +02:00
jensandClaude Sonnet 4.6 9d7561ba2f Apply nmea_client dark theme across all tabs and plots
Color palette taken directly from nmea_client/gui/plot_widget.py:
- Background #1a1a2e / #12121e, borders #2a2a4a/#3a3a5e,
  text #aaaacc/#ddddee, connected #4CAF50, error #F44336
- Trace colors: Material palette (blue, red, green, orange, …)
- Plot widget background overridden from "white" to #12121e
- pyqtgraph foreground set to #aaaacc

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 22:34:53 +02:00
jensandClaude Sonnet 4.6 8797c774e9 Plot tab: abbreviated names in combo, 8 plots with scroll, remove toolbar
- Trace selection combos show short labels (_trace_label) with full key
  stored as item data; width auto-adjusts to content (min 120 px)
- Extended plot grid from 4 to 8 (4×2) wrapped in QScrollArea
- Removed reset-zoom toolbar and cursor readout area

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 22:11:01 +02:00
jensandClaude Sonnet 4.6 8f1dc34991 Fix erratic axis scaling when hovering over plots
InfiniteLine was included in auto-range calculations, causing the view
to rescale on every cursor move. Add it with ignoreBounds=True in both
the initial setup and after each redraw.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 21:57:23 +02:00
jensandClaude Sonnet 4.6 9f0bdec9f1 Redesign plot tab: per-plot add/remove traces, persist settings, readable axes
- Replace static combo grid with a control bar above each plot:
  add-trace dropdown + "+" button; active traces shown as colored chips with
  "×" to remove (max 4 per plot)
- Save trace selection to ~/.config/we_monitor/plot_settings.json on every
  change; restore automatically once matching signals arrive from the server
- Remove tiny tick-font overrides so axis labels are readable at default size
- Show received record count in the status bar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 21:51:39 +02:00
jensandClaude Sonnet 4.6 04578781af Eagerly pre-load today's disk records into push_store_ref at startup
Previously load_today() was only called if in-memory records were empty,
which was never true once the first WeConnect poll had completed.
Now disk records are loaded unconditionally at startup and merged with
live in-memory records (dedup by ts) when a client connects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 21:39:22 +02:00
jensandClaude Sonnet 4.6 d94df87443 Print running dataset count after each received record in client.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 21:25:34 +02:00
jensandClaude Sonnet 4.6 9d2d742368 Fall back to disk when in-memory store is empty on client connect
storage_helpers: load_today_records() scans logs/ for all files
matching today's UTC date and VIN, merges and sorts their records.

network: _accept_loop receives an optional load_today callable;
if store_ref["current"] has no records it calls load_today() to
replay the day's history from disk before adding the client.

collect: passes a load_today lambda (None when -o is used).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 21:21:17 +02:00
jensandClaude Sonnet 4.6 b38f8d052e Replace matplotlib with pyqtgraph in Plot tab
- pg.PlotWidget (2×2 grid) with DateAxisItem for proper time axis
- Built-in pan (left drag), zoom (right drag / scroll wheel), Reset zoom button
- pg.InfiniteLine cursor with pg.SignalProxy for smooth 30 fps tracking
- Cursor value label shows nearest-point values colour-coded per trace
- Legend auto-updated via pi.legend.clear() + named plot() calls
- matplotlib removed from requirements.txt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 21:08:58 +02:00
jensandClaude Sonnet 4.6 2bbfcc7084 Scale plot tab to fill available window space at fullscreen
Removed the QScrollArea wrapper and fixed figure-height calculation.
FigureCanvasQTAgg now fills the tab with an Expanding size policy so
all 8 subplots fit in the visible area at 1920x1200 without scrolling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:34:16 +02:00
jensandClaude Sonnet 4.6 1b0a8670e5 Fix spurious disconnect after first message in GUI client
create_connection(timeout=5) left a 5-second read timeout on the
socket. Any collection interval longer than 5 s caused recv() to
raise socket.timeout, which was caught as OSError and reported as
a lost connection. Switch to blocking mode with settimeout(None)
immediately after the connect handshake.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:25:33 +02:00
jensandClaude Sonnet 4.6 74a33cb74e Add PyQt5 and matplotlib to requirements; gitignore __pycache__
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:19:30 +02:00
jensandClaude Sonnet 4.6 7774f4842e Move server modules into server/ package
data_model, log_config, network, storage_helpers, we_connect are now
under server/. collect.py stays in the project root and imports via
the server package.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:16:46 +02:00
jensandClaude Sonnet 4.6 f0e0c9c6ae Add PyQt5 GUI client with Connector, Dashboard, and Plot tabs
- Connector tab: host/port fields, connect/disconnect button, status
- Dashboard tab: live QTreeWidget of the full snapshot hierarchy;
  physical values show value and unit in separate columns;
  expand state preserved across refreshes
- Plot tab: matplotlib time-series subplots (2 columns) for every
  physical {value, unit} field, scrollable, auto-layout on new keys
- TcpReader runs in a daemon thread and delivers messages via
  Qt signals so the UI never blocks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:06:50 +02:00
jensandClaude Sonnet 4.6 3ab7795248 Push last snapshot immediately to newly connected clients
The server keeps the most recent snapshot in memory (under the same
lock as the client list). When a client connects it receives that
snapshot right away, then continues to receive live updates on each
collection cycle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:57:36 +02:00
jensandClaude Sonnet 4.6 53dcaf537e Extract store helpers into storage_helpers.py
auto_out_path, load_store, save_store moved out of data_model.py into
their own module. data_model.py now contains only value helpers and
domain extractors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:50:00 +02:00
jensandClaude Sonnet 4.6 097bc5ad51 Move domain extractors and collect_snapshot into data_model.py
we_connect.py now only handles WeConnect login and vehicle selection.
All extract_*() functions, ALL_DOMAINS, and collect_snapshot() live
in data_model.py alongside the store and value helpers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:47:25 +02:00
jensandClaude Sonnet 4.6 bc016cd7cb Refactor collect.py into focused modules
- data_model.py  : _str, _phys, load_store, save_store, auto_out_path
- we_connect.py  : domain extractors, ALL_DOMAINS, connect,
                   select_vehicle, collect_snapshot
- network.py     : TCP push server (start_push_server, broadcast)
- log_config.py  : logging setup
- collect.py     : run() loop and CLI, wired to the above modules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:41:54 +02:00
jensandClaude Sonnet 4.6 3dae080520 Add client.py: test client for the TCP push server
Connects to collect.py's push server and prints each incoming
snapshot as pretty-printed JSON, separated by a dashed line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:27:57 +02:00
jensandClaude Sonnet 4.6 81ebcd8e0f Add TCP push server: broadcast new snapshots to connected clients
Each connected TCP client receives every snapshot as a newline-
delimited JSON line the moment it is collected (no polling needed).
The server binds on --host/--port (default 0.0.0.0:9999).
Client connections are accepted in a daemon thread; dead connections
are pruned on the next broadcast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:26:05 +02:00
jensandClaude Sonnet 4.6 eaec158f2f Add midnight UTC log rotation for auto-named output files
When -o is not set, the collector detects a UTC date change at the
top of each collection cycle and opens a fresh timestamped file under
logs/.  Explicit -o paths are never rotated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:11:44 +02:00
jensandClaude Sonnet 4.6 e56637f132 Fix default log file extension to .json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:08:45 +02:00
jensandClaude Sonnet 4.6 725b480cb4 Default output to logs/YYYY_MM_DD_HH_MM_SS_<VIN>.log
When -o is omitted, the output file is auto-named from the run's
start timestamp and the vehicle VIN and placed under ./logs/,
which is created on demand.  logs/ is gitignored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:07:31 +02:00
jensandClaude Sonnet 4.6 dcd5910b3d Refactor physical values to {value, unit} objects in JSON output
All fields with a physical unit are now emitted as
{"value": <number>, "unit": "<unit>"} instead of encoding the unit
in the field name.  Field names are renamed accordingly (e.g.
chargePower_kW → chargePower).  Battery temperature field names are
also fixed to reflect the already-applied K→°C conversion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:01:44 +02:00
jensandClaude Sonnet 4.6 02e8d71569 Initial commit: WeConnect data collector
Adds collect.py with CLI flags for credentials (-u/-p), credential
JSON file (-c), VIN, domains (-d), interval, output file, and
--list-domains.  Credentials file supports domains as array or string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 18:40:01 +02:00