From 4ade3e415199625747e1fcaa5ae6bd64a5049ce4 Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Tue, 25 Aug 2026 14:40:03 +0200 Subject: [PATCH] =?UTF-8?q?tools(rpc):=20sondes=20JSON-RPC=20+=20fixtures?= =?UTF-8?q?=20du=20lot=20t=C3=A9l=C3=A9m=C3=A9trie=20d=C3=A9ploy=C3=A9=20s?= =?UTF-8?q?ur=20.75?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Range dans tools/rpc/ les sondes qui vivaient dans un scratchpad volatil : probe (appel RPC quelconque), notifications (abonnement + comptage), locale et locale_diff (comportement o:locale et son effet réel), loadconfig_roundtrip (aller-retour neutre §7-1). README avec usages. loadconfig_roundtrip est le SEUL qui écrit sur la box : garde --yes ajouté pour qu'un SetLoadConfig ne parte jamais par réflexe de flèche haut. analysis_options : tools/** et les classes l10n générées sortent de l'analyse — flutter analyze revient à 25 issues (ligne de base). Fixtures : le lot plugin télémétrie EST déployé sur .75 depuis ce matin (20 méthodes / 14 notifications). Capturé avant que ça ne se reperde : - loadtelemetry_hems75.json : dump réel de GetLoadTelemetry - loadconfig_hems75_v2.json : GetLoadConfig avec la clé 'domain' - schema_hems75_v2.json : schémas SET + télémétrie introspectés Co-Authored-By: Claude Opus 5 (1M context) --- analysis_options.yaml | 10 ++ test/fixtures/loadconfig_hems75_v2.json | 38 +++++++ test/fixtures/loadtelemetry_hems75.json | 57 +++++++++++ test/fixtures/schema_hems75_v2.json | 127 ++++++++++++++++++++++++ tools/rpc/README.md | 78 +++++++++++++++ tools/rpc/loadconfig_roundtrip.dart | 77 ++++++++++++++ tools/rpc/locale.dart | 93 +++++++++++++++++ tools/rpc/locale_diff.dart | 70 +++++++++++++ tools/rpc/notifications.dart | 55 ++++++++++ tools/rpc/probe.dart | 45 +++++++++ 10 files changed, 650 insertions(+) create mode 100644 test/fixtures/loadconfig_hems75_v2.json create mode 100644 test/fixtures/loadtelemetry_hems75.json create mode 100644 test/fixtures/schema_hems75_v2.json create mode 100644 tools/rpc/README.md create mode 100644 tools/rpc/loadconfig_roundtrip.dart create mode 100644 tools/rpc/locale.dart create mode 100644 tools/rpc/locale_diff.dart create mode 100644 tools/rpc/notifications.dart create mode 100644 tools/rpc/probe.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..8f627a0 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -9,6 +9,16 @@ # packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml +analyzer: + exclude: + # Sondes JSON-RPC autonomes (dart:io), exécutées à la main contre une box. + # Ce sont des outils de diagnostic, pas du code d'application : leur sortie + # console EST leur raison d'être, donc `avoid_print` n'y a pas de sens. + # Les exclure garde `flutter analyze` lisible pour ce qui part en production. + - tools/** + # Classes de localisation générées par `flutter gen-l10n` — jamais éditées à la main. + - lib/l10n/app_localizations*.dart + linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` diff --git a/test/fixtures/loadconfig_hems75_v2.json b/test/fixtures/loadconfig_hems75_v2.json new file mode 100644 index 0000000..c7881f0 --- /dev/null +++ b/test/fixtures/loadconfig_hems75_v2.json @@ -0,0 +1,38 @@ +{ + "_source": "dump réel HEMS 192.168.1.75, NymeaEnergy.GetLoadConfig, 2026-08-25 (APRÈS déploiement du lot télémétrie).", + "_note": "Diffère de loadconfig_hems75.json : la clé 'domain' existe désormais (vide = non classé) et une 2e charge sg-ready 'pac-terrain' est déclarée. Conserve les charges utiles VIDES des mécanismes inutilisés — sérialisation par méta-objet. Ne PAS nettoyer.", + "loadConfigs": [ + { + "adapter": "relay-router", + "domain": "", + "enabled": true, + "id": "chauffe-eau", + "label": "chauffe-eau", + "maxPowerW": 0, + "minOffS": 60, + "minOnS": 60, + "mode": "fixed", + "needs": { + "dailyDeadline": "", + "minEnergyWhPerDay": 0 + }, + "powerLevels": [], + "priority": 1, + "relays": [ + { + "powerW": 500, + "thingId": "{8538782f-2c8c-4a30-bfce-c8140f791c9b}" + }, + { + "powerW": 1000, + "thingId": "{2ebe6bef-829b-4695-9d11-ddb4c16c5448}" + }, + { + "powerW": 2000, + "thingId": "{b033b212-1adb-4df0-ba2b-8fa477de52a2}" + } + ], + "sgReady": {} + } + ] +} \ No newline at end of file diff --git a/test/fixtures/loadtelemetry_hems75.json b/test/fixtures/loadtelemetry_hems75.json new file mode 100644 index 0000000..0fc36c5 --- /dev/null +++ b/test/fixtures/loadtelemetry_hems75.json @@ -0,0 +1,57 @@ +{ + "_source": "dump réel HEMS 192.168.1.75, NymeaEnergy.GetLoadTelemetry, 2026-08-25 — lot plugin télémétrie déployé (20 méthodes / 14 notifications).", + "_note": "Charge utile IDENTIQUE à la notification LoadTelemetryChanged. decision.code et faultCode sont des CODES avec params numériques, jamais des phrases : le rendu en français est à la charge de l'app (clés ARB). 'timestamp' = fin du dernier cycle d'arbitrage, omis tant qu'aucun cycle n'a tourné — c'est lui qui rend un arbitre figé détectable.", + "telemetry": { + "budget": { + "allocatedW": 0, + "evReservedW": 0, + "recreditedW": 500, + "remainingW": 229.29998779296875, + "surplusW": -270.70001220703125 + }, + "degradedMode": false, + "loads": [ + { + "allocatedW": 0, + "available": true, + "decision": { + "code": "SURPLUS_INSUFFICIENT", + "params": { + "budgetW": 229 + } + }, + "loadId": "chauffe-eau", + "lock": { + "kind": "minOff", + "remainingS": 60 + }, + "mechanism": { + "kind": "relay", + "maxStageW": 3500, + "stageW": 0 + } + }, + { + "allocatedW": 0, + "available": true, + "decision": { + "code": "SG_NORMAL", + "params": { + "budgetW": 229 + } + }, + "loadId": "pac-terrain", + "lock": { + "kind": "minStateHold", + "remainingS": 240 + }, + "mechanism": { + "estimatedPowerW": 0, + "kind": "sgReady", + "state": 2 + } + } + ], + "timestamp": "2026-08-25T12:37:00Z" + } +} \ No newline at end of file diff --git a/test/fixtures/schema_hems75_v2.json b/test/fixtures/schema_hems75_v2.json new file mode 100644 index 0000000..edba10d --- /dev/null +++ b/test/fixtures/schema_hems75_v2.json @@ -0,0 +1,127 @@ +{ + "_source": "JSONRPC.Introspect sur 192.168.1.75, 2026-08-25, APRÈS déploiement du lot télémétrie.", + "_note": "nymea rejette toute clé inconnue (jsonvalidator.cpp:143). Liste exhaustive de ce que la box accepte et renvoie.", + "SetLoadConfig_params": { + "loadConfigs": [ + { + "adapter": "String", + "enabled": "Bool", + "id": "String", + "label": "String", + "mode": "String", + "o:domain": "String", + "o:maxPowerW": "Uint", + "o:minOffS": "Uint", + "o:minOnS": "Uint", + "o:needs": { + "o:dailyDeadline": "String", + "o:minEnergyWhPerDay": "Uint" + }, + "o:powerLevels": [ + "Uint" + ], + "o:relays": [ + { + "powerW": "Uint", + "thingId": "String" + } + ], + "o:sgReady": { + "o:minStateHoldS": "Uint", + "o:states": [ + { + "estimatedPowerW": "Double", + "relays": [ + "String" + ], + "state": "Uint" + } + ] + }, + "priority": "Uint" + } + ] + }, + "GetLoadTelemetry_returns": { + "degradedMode": "Bool", + "loads": [ + { + "allocatedW": "Double", + "available": "Bool", + "decision": { + "code": "String", + "params": "Variant" + }, + "loadId": "String", + "o:faultCode": "String", + "o:lock": { + "kind": "String", + "remainingS": "Int" + }, + "o:mechanism": { + "kind": "String", + "o:chargingEnabled": "Bool", + "o:currentA": "Double", + "o:estimatedPowerW": "Double", + "o:maxPowerW": "Double", + "o:maxStageW": "Double", + "o:percent": "Double", + "o:phaseCount": "Uint", + "o:pluggedIn": "Bool", + "o:setpointW": "Double", + "o:stageW": "Double", + "o:state": "Int" + } + } + ], + "o:budget": { + "allocatedW": "Double", + "evReservedW": "Double", + "recreditedW": "Double", + "remainingW": "Double", + "surplusW": "Double" + }, + "o:timestamp": "String" + }, + "LoadTelemetryChanged_params": { + "degradedMode": "Bool", + "loads": [ + { + "allocatedW": "Double", + "available": "Bool", + "decision": { + "code": "String", + "params": "Variant" + }, + "loadId": "String", + "o:faultCode": "String", + "o:lock": { + "kind": "String", + "remainingS": "Int" + }, + "o:mechanism": { + "kind": "String", + "o:chargingEnabled": "Bool", + "o:currentA": "Double", + "o:estimatedPowerW": "Double", + "o:maxPowerW": "Double", + "o:maxStageW": "Double", + "o:percent": "Double", + "o:phaseCount": "Uint", + "o:pluggedIn": "Bool", + "o:setpointW": "Double", + "o:stageW": "Double", + "o:state": "Int" + } + } + ], + "o:budget": { + "allocatedW": "Double", + "evReservedW": "Double", + "recreditedW": "Double", + "remainingW": "Double", + "surplusW": "Double" + }, + "o:timestamp": "String" + } +} \ No newline at end of file diff --git a/tools/rpc/README.md b/tools/rpc/README.md new file mode 100644 index 0000000..7503650 --- /dev/null +++ b/tools/rpc/README.md @@ -0,0 +1,78 @@ +# Sondes JSON-RPC — `tools/rpc/` + +Scripts `dart:io` autonomes pour interroger une box nymea **sans lancer l'app**. + +Pourquoi ils existent : sur ce poste, Flutter Linux natif est bloqué (snap sans linker +`ld.lld`) et le web ne peut pas joindre le LAN en `ws://` (CORS). Ces sondes sont donc le +moyen le plus court de vérifier un comportement réel de la box — et elles font plus autorité +qu'une lecture de code ou qu'un `Introspect` recopié de mémoire. + +Aucune dépendance hors SDK : `dart:io` + `dart:convert`. Rien à installer. + +```bash +dart tools/rpc/