etm-powersync-app/test/fixtures/schema_hems75_v3.json
Patrick Schurig dee20896c5 feat(télémétrie): lot B app — consommation de la télémétrie d'arbitrage
Le lot plugin B-bis (1.15.2+etm15) est sur .75 et GetLoadConfig/GetLoadTelemetry
concordent : les critères 4 à 8 du brief deviennent atteignables.

- models/load_telemetry.dart : vue en lecture. timestamp/budget/mechanism/lock/
  faultCode nullables — omis porte un sens que zéro ne porterait pas.
- services/telemetry_text.dart : rendu français des CODES (19 motifs, 3 défauts,
  3 verrous, 4 mécanismes). Repli lisible sur un code inconnu ET sur un code connu
  privé d'un paramètre : une phrase trouée a l'air d'une donnée, le code brut a
  l'air de ce qu'il est.
- providers/load_telemetry_provider.dart : instantané + LoadTelemetryChanged,
  fraîcheur à trois signaux (âge du timestamp, silence des trames, timestamp qui
  cesse d'avancer), machine à états de ClearLoadFault qui ne conclut JAMAIS sur
  l'acquittement du RPC.
- load_config_provider : assignDomain() en brouillon, énumération fermée.
- load_order_screen : budget, fraîcheur, mode dégradé, bloc par charge, encart
  défaut, menu de domaine.
- nymea_service : getLoadTelemetry(), clearLoadFault(), aiguillage de la
  notification. Une box sans la méthode s'annonce comme telle, pas comme en panne.

Seuil de fraîcheur à 180 s : le battement de cœur du plugin est de 60 s et le
timestamp avance d'un cycle par minute. Un seuil plus court crierait « données
anciennes » sur une installation vivante.

Fixtures v3 prises sur .75 après déploiement, dont un cycle porteur d'un verrou —
« lock » étant optionnel, un dump pris au mauvais moment ne l'éprouverait pas.

57 tests (30 avant), flutter analyze inchangé à 25 issues.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
2026-08-25 19:20:30 +02:00

153 lines
4.9 KiB
JSON

{
"_source": "JSONRPC.Introspect sur 192.168.1.75, 2026-08-25 — plugin 1.15.2+etm15 (194 méthodes / 101 notifications au total, dont 20/14 pour NymeaEnergy.*).",
"_note": "nymea rejette toute clé inconnue AVANT le handler (jsonvalidator.cpp:143). Ce que la box déclare fait donc autorité sur ce que l'app a le droit d'envoyer. mechanism, lock, faultCode, budget et timestamp sont OPTIONNELS (o:) — omis, jamais nuls.",
"GetLoadTelemetry": {
"description": "Get a snapshot of the arbitration runtime state: surplus budget, per-load allocation, availability, fault code, active lock (seconds remaining), mechanism payload and decision reason. Reason and fault are CODES with numeric params - never human-readable sentences: the client renders them in the user's language. Identical payload to the LoadTelemetryChanged notification. 'timestamp' is the end of the last arbitration cycle and is omitted while no cycle has run yet.",
"params": {},
"permissionScope": "PermissionScopeControlThings",
"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": {
"description": "Emitted whenever the arbitration runtime state changed significantly, and periodically (heartbeat) even when unchanged, so that a frozen arbitrator is detectable by a timestamp that stops advancing. Same payload as GetLoadTelemetry.",
"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"
}
},
"ClearLoadFault": {
"description": "Clear the fault latch of a controllable load (ECS-410). Deliberate operator action: the fault never clears by itself.",
"params": {
"loadId": "String"
},
"permissionScope": "PermissionScopeControlThings",
"returns": {
"energyError": "$ref:EnergyError"
}
},
"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"
}
]
},
"LoadConfigChanged": {
"description": "Emitted whenever the controllable load configuration changed.",
"params": {
"loadConfigs": [
"$ref:LoadConfig"
]
}
}
}