collect: migrate from weconnect to CarConnectivity + VW connector

Replace the weconnect library with carconnectivity and
carconnectivity-connector-volkswagen.  The connector is configured via
a dict passed to CarConnectivity(); startup()/fetch_all() replace
login()/update().  All domain extractors in data_model.py are rewritten
for the new object model (vehicle.charging.*, vehicle.get_electric_drive(),
vehicle.climatization.*, etc.).  Remaining time fields are now derived
from estimated_date_reached DateAttributes.  Battery temperatures are
read directly in °C (no Kelvin conversion).  Auth error handling drops
the manual re-login since the connector manages token refresh internally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 08:02:34 +02:00
co-authored by Claude Sonnet 4.6
parent ef7b99e7e8
commit f050de7001
4 changed files with 122 additions and 125 deletions
+13 -17
View File
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
"""
WeConnect vehicle data collector.
CarConnectivity vehicle data collector.
Periodically fetches selected domains from a VW WeConnect vehicle and
appends timestamped records to a JSON file. The JSON structure mirrors
the WeConnect domain hierarchy: domain → status-object → field.
Periodically fetches selected domains from a VW vehicle via CarConnectivity
and appends timestamped records to a JSON file. The JSON structure mirrors
the domain hierarchy: domain → status-object → field.
Usage examples
--------------
@@ -53,7 +53,7 @@ from datetime import datetime, timezone
from pathlib import Path
from server import log_config, network, we_connect
from weconnect.errors import AuthentificationError
from carconnectivity.errors import AuthenticationError, TemporaryAuthenticationError
from server.data_model import ALL_DOMAINS, collect_snapshot, apply_procedural
from server.storage_helpers import auto_out_path, load_last_24h_records, load_store, save_store
from utils.jay_diff import jay_diff_full
@@ -74,12 +74,12 @@ def run(args: argparse.Namespace) -> None:
sys.exit(1)
if args.dry:
log.info("Dry-run mode — skipping WeConnect login")
log.info("Dry-run mode — skipping CarConnectivity login")
wc = None
vehicle = None
vin = args.vin or "UNKNOWN"
else:
log.info("Connecting to WeConnect…")
log.info("Connecting via CarConnectivity")
wc = we_connect.connect(args.username, args.password)
vehicle, err = we_connect.select_vehicle(wc, args.vin)
if err:
@@ -132,7 +132,7 @@ def run(args: argparse.Namespace) -> None:
log.info("Midnight UTC rotation → %s", out)
try:
wc.update()
we_connect.update(wc)
snapshot = collect_snapshot(vehicle, domains)
apply_procedural(snapshot)
store["records"].append(snapshot)
@@ -146,12 +146,8 @@ def run(args: argparse.Namespace) -> None:
log.info("Record #%d saved at %s", len(store["records"]), snapshot["ts"])
except KeyboardInterrupt:
raise
except AuthentificationError:
log.warning("Authentication error — re-logging in before next interval")
try:
wc.login()
except Exception:
log.exception("Re-login failed")
except (AuthenticationError, TemporaryAuthenticationError):
log.warning("Authentication error — will retry at next interval")
except Exception:
log.exception("Collection failed — will retry at next interval")
@@ -180,12 +176,12 @@ def main() -> None:
creds.add_argument(
"-u", "--username",
default=os.environ.get("WC_USER"),
help="WeConnect / MyVolkswagen email (overrides credentials file)",
help="MyVolkswagen email (overrides credentials file)",
)
creds.add_argument(
"-p", "--password",
default=os.environ.get("WC_PASS"),
help="WeConnect password (overrides credentials file)",
help="MyVolkswagen password (overrides credentials file)",
)
p.add_argument(
@@ -239,7 +235,7 @@ def main() -> None:
p.add_argument(
"--dry",
action="store_true",
help="Skip WeConnect login and data collection; run push server only (for GUI testing)",
help="Skip CarConnectivity login and data collection; run push server only (for GUI testing)",
)
p.add_argument(
"--list-domains",