Commit Graph
20 Commits
Author SHA1 Message Date
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