tools(rpc): une sonde qui dit ce que l'app AFFICHERAIT de measurement
Les règles d'affichage de `measurement` sont figées par test sur des trames fabriquées. Cette sonde les éprouve sur les trames réelles de la box — en important le modèle de l'app (LoadTelemetryEntry.fromJson), pas en relisant le JSON à sa façon : une lecture parallèle ne prouverait rien sur ce que l'écran fera. Elle montre ce qu'un écran regardé de loin ne distingue pas : `none` (bloc masqué, « pas mesurable ») d'une charge simplement absente de loads[] (rien à conclure), et la portée d'un écart selon `meter` ou `device`. Relevé du 2026-08-28 sur .75 en 1.15.2+etm26 : measuredW encore publié ? false chauffe-eau source=meter → Mesuré 1500 W · mesure indépendante : concordance = preuve pac-terrain source=meter → Mesuré 800 W · idem V2C Trydan absente de loads[] — pas `none` Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQbZKrWqsMFP1Lh2jjjd9f
This commit is contained in:
parent
e362fb7430
commit
771345f1bc
@ -24,6 +24,7 @@ dart tools/rpc/<script>.dart [host] [args…] # host par défaut : 192.168.1
|
||||
| `loadconfig_roundtrip.dart` | Aller-retour neutre `Get` → `Set` verbatim → `Get`. | ⚠️ **OUI** |
|
||||
| `telemetry_watch.dart` | Instantané puis écoute de `LoadTelemetryChanged` : cadence, avancée du cycle, allocations. | non |
|
||||
| `evcharger_watch.dart` | Suit les **états d'une borne** (branchement, charge, énergie) — y compris quand elle est hors arbitrage. | non |
|
||||
| `measurement_check.dart` | Dit, charge par charge, **ce que l'app afficherait** de `measurement` — via le vrai modèle. | non |
|
||||
| `domain_assign.dart` | Classe des charges par `o:domain`, **par le code d'écriture de l'app**. | ⚠️ **OUI** |
|
||||
| `fault_probe.dart` | Provoque une charge en défaut, éprouve `ClearLoadFault`, restaure. | ⚠️ **OUI** |
|
||||
|
||||
@ -111,6 +112,22 @@ avant que le câble ne bouge ; et `powerL1/L2/L3` sont des **ampères mal nommé
|
||||
deux ne doit être affiché tant que le plugin n'a pas publié `sessionEnergy` et le renommage
|
||||
`currentL1/L2/L3`.
|
||||
|
||||
### `measurement_check.dart`
|
||||
|
||||
```bash
|
||||
dart run tools/rpc/measurement_check.dart 192.168.1.75
|
||||
```
|
||||
|
||||
⚠️ `dart run` : la sonde **importe le modèle de l'app** (`LoadTelemetryEntry.fromJson`)
|
||||
plutôt que de relire le JSON à sa façon — une lecture parallèle ne prouverait rien sur ce
|
||||
que l'écran fera. Elle rend, par charge, la source publiée et la phrase que l'app en tire.
|
||||
|
||||
Ce qu'elle sert à voir, et qu'un écran regardé de loin ne distingue pas : `none` (bloc
|
||||
masqué, « pas mesurable ») d'une charge simplement **absente de `loads[]`** (rien à
|
||||
conclure), et la portée d'un écart selon `meter` ou `device`. Relevé du 2026-08-28 sur
|
||||
`.75` en `1.15.2+etm26` : `measuredW` n'est plus publié, `chauffe-eau` et `pac-terrain`
|
||||
sont en `meter` (1 500 W et 800 W), la V2C est absente — pas `none`.
|
||||
|
||||
### `domain_assign.dart` — ⚠️ écrit
|
||||
|
||||
```bash
|
||||
|
||||
62
tools/rpc/measurement_check.dart
Normal file
62
tools/rpc/measurement_check.dart
Normal file
@ -0,0 +1,62 @@
|
||||
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<void> main(List<String> 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<Map<String, dynamic>>();
|
||||
|
||||
ws.listen((d) {
|
||||
final m = jsonDecode(d as String) as Map<String, dynamic>;
|
||||
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<String, dynamic>;
|
||||
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<Map>()) {
|
||||
final e = LoadTelemetryEntry.fromJson(Map<String, dynamic>.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.');
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user