import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:etm_powersync_app/models/load_telemetry.dart'; // Lit `NymeaEnergy.GetLoadTelemetry` et dit, charge par charge, **ce que l'app afficherait** // — en passant par le vrai modèle de l'app, pas par une lecture parallèle du JSON. // // ## Pourquoi cette sonde plutôt qu'un coup d'œil à l'écran // // Les règles d'affichage de `measurement` sont figées par test sur des trames fabriquées. // Ceci vérifie la même chose sur les trames RÉELLES de la box, et c'est la seule façon de // voir que le contrat déployé correspond à ce que l'app attend. Un écran regardé de loin ne // distingue pas « pas mesurable » d'un bloc simplement absent. // // LECTURE SEULE. Usage : dart run tools/rpc/measurement_check.dart [host] Future main(List args) async { final host = args.isNotEmpty ? args[0] : '192.168.1.75'; final ws = await WebSocket.connect('ws://$host:4444') .timeout(const Duration(seconds: 8)); final done = Completer>(); ws.listen((d) { final m = jsonDecode(d as String) as Map; if (m['id'] == 2) done.complete(m); }); ws.add(jsonEncode({'id': 1, 'method': 'JSONRPC.Hello', 'params': {}})); ws.add(jsonEncode( {'id': 2, 'method': 'NymeaEnergy.GetLoadTelemetry', 'params': {}})); final r = await done.future.timeout(const Duration(seconds: 10)); await ws.close(); final p = r['params'] as Map; final loads = (p['loads'] as List? ?? const []); final brut = jsonEncode(p); print('measuredW encore publié ? ${brut.contains('"measuredW"')} ' '(doit être false depuis 1.15.2+etm26)'); print('${loads.length} charge(s) dans loads[] — et `measurement` est présent pour ' 'chacune, pour elles seules.\n'); for (final l in loads.whereType()) { final e = LoadTelemetryEntry.fromJson(Map.from(l)); final m = e.measurement; final ligne = switch (m.source) { MeasurementSource.none => 'bloc de mesure MASQUÉ — « pas mesurable » est un état, pas un vide', MeasurementSource.meter => 'Mesuré ${m.powerW!.round()} W · mesure indépendante : une concordance est une preuve', MeasurementSource.device => 'Mesuré ${m.powerW!.round()} W · publiée par l\'appareil : une concordance ne prouve rien', MeasurementSource.inconnue => 'Mesuré ${m.powerW?.round()} W · source inconnue de cette app, aucune portée affichée', }; print(' ${e.loadId.padRight(38)} alloué ${e.allocatedW.round()} W'); print(' ${' '.padRight(38)} source=${m.source.name} → $ligne'); } print('\nToute charge de GetLoadConfig ABSENTE de cette liste n\'est pas `none` :'); print('elle est hors de loads[], et l\'app n\'en conclut rien.'); }