fix(accueil): « En ce moment » n'affiche que ce qui consomme ou travaille
Patrick, 2026-10-16 : « un chauffe-eau qui ne consomme pas, pas la peine de l'afficher — uniquement des choses actives ». - Une charge agit si elle consomme (mesure > 50 W) ou si son mécanisme est réellement commandé (palier de relais > 0, consigne > 0, borne autorisée avec du courant, SG-Ready en état 3 ou 4) — plus sur la puissance seulement DÉCIDÉE. Sur le banc, le chauffe-eau avait 83 W décidés en éco mais son relais restait au palier 0 : il ne chauffait pas. Alertes (défaut, dérogation, objectif manqué) toujours montrées. - Ventilation « 83 W éco + 0 W confort » retirée : éco / confort se lisent comme des réglages de température. Question posée au moteur (consignes/QUESTION-moteur-vocabulaire-niveaux.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
250c0fcb6b
commit
727f309d75
@ -155,16 +155,28 @@ class EnCeMomentView extends StatelessWidget {
|
||||
'EV_IDLE',
|
||||
};
|
||||
|
||||
/// Une charge **agit** quand un de ses niveaux porte de la puissance sous un motif qui
|
||||
/// n'est pas une attente, qu'elle tire réellement de la puissance, ou qu'il y a quelque
|
||||
/// chose à dire : dérogation, défaut, objectif manqué retenu.
|
||||
/// Une charge **agit** quand elle consomme ou travaille VRAIMENT — pas quand le moteur
|
||||
/// lui a seulement décidé de la puissance (Patrick, 2026-10-16 : « uniquement des choses
|
||||
/// actives »). Sur le banc, le chauffe-eau avait 83 W décidés en éco, mais son relais
|
||||
/// restait au palier 0 : un relais de 3 500 W ne délivre pas 83 W, rien ne chauffait.
|
||||
///
|
||||
/// La puissance seule ne suffit pas : sur le banc, la PAC SG-Ready porte 1 200 W en état
|
||||
/// « normal » maintenu sous `BELOW_MIN_POWER` (« le budget passe à la charge suivante ») —
|
||||
/// rien n'est piloté, elle attend (exemple de Patrick, 2026-10-16).
|
||||
/// Dans l'ordre : une alerte (défaut, dérogation, objectif manqué) se montre toujours ;
|
||||
/// une MESURE au-delà de 50 W tranche ; sinon le mécanisme APPLIQUÉ ; et seulement sans
|
||||
/// mécanisme publié, la puissance décidée sous un motif qui n'est pas une attente.
|
||||
static bool agit(LoadTelemetryEntry e, {bool manque = false}) {
|
||||
if (manque || !e.available || e.enDerogation) return true;
|
||||
if (e.measurement.mesuree && e.measurement.powerW! > 50) return true;
|
||||
final m = e.measurement;
|
||||
if (m.mesuree && m.powerW! > 50) return true;
|
||||
final meca = e.mechanism;
|
||||
if (meca != null) {
|
||||
if (meca.isRelay) return (meca.stageW ?? 0) > 0;
|
||||
if (meca.isVariable) return (meca.setpointW ?? 0) > 0 || (meca.percent ?? 0) > 0;
|
||||
if (meca.isEvCharger) {
|
||||
return meca.chargingEnabled == true && (meca.currentA ?? 0) > 0;
|
||||
}
|
||||
// SG-Ready : 3 = recommandé, 4 = forcé. 2 est l'état normal — rien de piloté.
|
||||
if (meca.isSgReady) return meca.state == 3 || meca.state == 4;
|
||||
}
|
||||
if (e.levels.isEmpty) {
|
||||
return e.allocatedW > 0 && !motifsDAttente.contains(e.decision.code);
|
||||
}
|
||||
@ -197,20 +209,11 @@ class EnCeMomentView extends StatelessWidget {
|
||||
final couleur = EtmTokens.domaineOf(context, dom) ?? muted;
|
||||
final m = e.measurement;
|
||||
|
||||
// Ventilation éco / confort quand plusieurs passes se cumulent (R7, etm70) : le motif
|
||||
// publié est celui de la part majoritaire, les deux parts se lisent ici.
|
||||
String? ventilation;
|
||||
if (e.levels.length > 1) {
|
||||
ventilation = e.levels
|
||||
.map((l) => t.ventilationNiveau(
|
||||
l.targetW.round(),
|
||||
switch (l.level) {
|
||||
'eco' => t.ventilationEco,
|
||||
'comfort' => t.ventilationConfort,
|
||||
final autre => autre,
|
||||
}))
|
||||
.join(' + ');
|
||||
}
|
||||
// La ventilation « 83 W éco + 0 W confort » est RETIRÉE (Patrick, 2026-10-16) : pour un
|
||||
// client, éco / confort sont des réglages de température, pas des besoins. Elle
|
||||
// reviendra avec le vocabulaire tranché avec le moteur
|
||||
// (`consignes/QUESTION-moteur-vocabulaire-niveaux.md`) — sans doute par source,
|
||||
// « réseau / surplus », depuis `counts`.
|
||||
final manque = derniersManques[e.loadId.replaceAll(RegExp(r'[{}]'), '').toLowerCase()];
|
||||
|
||||
return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
@ -231,9 +234,6 @@ class EnCeMomentView extends StatelessWidget {
|
||||
const SizedBox(height: 2),
|
||||
Text(decisionText(t, motifDeLAction(e)),
|
||||
style: EtmTokens.sans(size: 11.5, color: muted).copyWith(height: 1.35)),
|
||||
if (ventilation != null)
|
||||
Text(ventilation,
|
||||
style: EtmTokens.mono(size: 10.5, weight: FontWeight.w400, color: muted)),
|
||||
if (manque != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
_manque(context, t, manque),
|
||||
|
||||
@ -51,28 +51,45 @@ LoadDecision _d(String code, Map<String, dynamic> params) =>
|
||||
void main() {
|
||||
final fr = lookupL10n(const Locale('fr'));
|
||||
|
||||
testWidgets('trame réelle : une ligne par charge qui AGIT, les autres « en attente »',
|
||||
testWidgets('trame réelle : rien ne consomme ni ne travaille → tout « en attente »',
|
||||
(t) async {
|
||||
await t.pumpWidget(_app(_vue(_banc())));
|
||||
expect(find.text('En ce moment'), findsOneWidget);
|
||||
// Le chauffe-eau achète 83 W en éco : il agit, et son motif dit l'ACHAT — pas
|
||||
// « rien n'est commandé », que la racine etm69 publiait (dernier niveau parcouru).
|
||||
expect(find.text('Chauffe-eau'), findsOneWidget);
|
||||
expect(find.text('Plancher servi — 83 W achetés au réseau'), findsOneWidget);
|
||||
expect(find.textContaining('rien n\'est commandé'), findsNothing);
|
||||
// La PAC SG-Ready porte 1 200 W en état normal maintenu sous BELOW_MIN_POWER : rien de
|
||||
// piloté — une ligne discrète, pas une action (Patrick, 2026-10-16).
|
||||
expect(find.text('En attente : PAC banc'), findsOneWidget);
|
||||
expect(find.text('PAC banc'), findsNothing);
|
||||
// Le chauffe-eau a 83 W DÉCIDÉS en éco, mais son relais est au palier 0 : il ne chauffe
|
||||
// pas. La PAC SG-Ready est en état 2 (normal), mesurée à 0 W. Aucune n'agit (Patrick,
|
||||
// 2026-10-16 : « uniquement des choses actives »).
|
||||
expect(find.text('En attente : Chauffe-eau, PAC banc'), findsOneWidget);
|
||||
expect(find.text('Chauffe-eau'), findsNothing);
|
||||
expect(find.textContaining('Plancher servi'), findsNothing);
|
||||
// Les décisions inventées de l'ancienne démo ne peuvent plus sortir.
|
||||
expect(find.textContaining('Batterie en charge'), findsNothing);
|
||||
// Le nom de marque, écrit en échappement pour que le grep de la consigne reste vide.
|
||||
expect(find.textContaining('Héos'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('la ventilation des niveaux se lit (83 W éco + 0 W confort)', (t) async {
|
||||
testWidgets('relais commandé : la ligne apparaît, avec le motif de l\'action', (t) async {
|
||||
final b = _banc();
|
||||
final loads = [
|
||||
for (final l in (b['loads'] as List).cast<Map<String, dynamic>>())
|
||||
if (l['loadId'] == 'chauffe-eau')
|
||||
{
|
||||
...l,
|
||||
'mechanism': {...(l['mechanism'] as Map), 'stageW': 500},
|
||||
}
|
||||
else
|
||||
l,
|
||||
];
|
||||
await t.pumpWidget(_app(_vue({...b, 'loads': loads})));
|
||||
expect(find.text('Chauffe-eau'), findsOneWidget);
|
||||
expect(find.text('Plancher servi — 83 W achetés au réseau'), findsOneWidget);
|
||||
expect(find.text('En attente : PAC banc'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('pas de ventilation « éco / confort » : des mots de thermostat (Patrick)',
|
||||
(t) async {
|
||||
await t.pumpWidget(_app(_vue(_banc())));
|
||||
expect(find.text('83 W éco + 0 W confort'), findsOneWidget);
|
||||
expect(find.textContaining('confort'), findsNothing);
|
||||
expect(find.textContaining(' éco'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('puissance MESURÉE seulement — jamais l\'allocation à sa place', (t) async {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user