Demande de Patrick du 2026-10-16 (inspiré du sélecteur Victron, réduit) : ‹ jour › avec calendrier, raccourcis Hier / Aujourd'hui / Demain, et un lien « Plusieurs jours » vers l'écran Énergie — pas de plage de plusieurs jours dans une carte à colonnes horaires. - Jour passé : mesures du jour et prévision ENREGISTRÉE telle qu'elle était connue à son début (GetPvForecast + knownAt) — sinon la table rendrait la dernière révision, faite après coup. - Demain : prévision seule ; au-delà, flèche désactivée (horizon). - Météo de maintenant sur aujourd'hui seulement ; rangée horaire si la série couvre le jour (aujourd'hui, demain). - Une réponse tardive d'un jour quitté est jetée. - Correctif trouvé par le test de navigation : la carte ne relisait ses mesures qu'après une prévision lue avec succès ; sur une box sans prévision, elle restait sur le sablier. Elle suit désormais le compteur de lectures du provider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.2 KiB
Dart
101 lines
3.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import '../models/pv_forecast.dart';
|
|
import '../services/nymea_service.dart';
|
|
|
|
/// Intervalle de relecture. La prévision ne notifie RIEN (décision moteur) : sans
|
|
/// relecture, l'écran laissé ouvert montrerait la courbe du matin le soir.
|
|
const Duration kRelecturePrevision = Duration(minutes: 12);
|
|
|
|
/// **La prévision PV de la box courante**, partagée par l'accueil et l'écran Énergie.
|
|
///
|
|
/// Une seule lecture et un seul minuteur pour deux écrans : deux cartes qui liraient
|
|
/// chacune de leur côté pourraient montrer deux courbes différentes à quelques minutes
|
|
/// d'écart, et rien ne dirait laquelle est la plus récente.
|
|
///
|
|
/// La lecture n'est lancée qu'à la demande ([assurer]) — l'écran qui s'ouvre la déclenche,
|
|
/// puis le minuteur prend le relais.
|
|
class PvForecastProvider extends ChangeNotifier {
|
|
NymeaService? _service;
|
|
Timer? _timer;
|
|
|
|
bool _chargement = false;
|
|
bool _luUneFois = false;
|
|
PvForecastSnapshot? _snapshot;
|
|
String? _erreur;
|
|
PvForecastSite? _site;
|
|
String? _refusDesignation;
|
|
int _lectures = 0;
|
|
|
|
bool get chargement => _chargement || !_luUneFois;
|
|
PvForecastSnapshot? get snapshot => _snapshot;
|
|
|
|
/// Non nul = la prévision n'a pas été LUE. Ce n'est pas « rien de publié ».
|
|
String? get erreur => _erreur;
|
|
PvForecastSite? get site => _site;
|
|
String? get refusDesignation => _refusDesignation;
|
|
|
|
/// Nombre de lectures TERMINÉES, réussies ou non. Un écran qui relit ses propres données
|
|
/// au rythme de la prévision s'accroche à ce compteur, pas à `capturedAt` : sans
|
|
/// prévision lisible, `capturedAt` ne bouge jamais, et l'écran attendrait pour rien.
|
|
int get lectures => _lectures;
|
|
|
|
void attach(NymeaService s) {
|
|
if (identical(_service, s)) return;
|
|
_service = s;
|
|
}
|
|
|
|
/// À appeler à l'ouverture d'un écran qui montre la prévision : relit tout de suite, et
|
|
/// arme la relecture périodique si elle ne l'est pas déjà.
|
|
Future<void> assurer() async {
|
|
_timer ??= Timer.periodic(kRelecturePrevision, (_) => relire());
|
|
await relire();
|
|
}
|
|
|
|
Future<void> relire() async {
|
|
final s = _service;
|
|
if (s == null || _chargement) return;
|
|
_chargement = true;
|
|
notifyListeners();
|
|
try {
|
|
final r = await s.fetchForecastSnapshot();
|
|
_snapshot = r.snapshot;
|
|
_erreur = r.erreur;
|
|
_site = r.snapshot?.etat == PvForecastEtat.siteManquant
|
|
? await s.fetchForecastSite()
|
|
: null;
|
|
} finally {
|
|
_chargement = false;
|
|
_luUneFois = true;
|
|
_lectures++;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
/// Désigne le site — geste explicite de l'utilisateur, jamais automatique.
|
|
Future<void> designer(String siteId) async {
|
|
final s = _service;
|
|
if (s == null) return;
|
|
_refusDesignation = await s.setForecastSite(siteId);
|
|
await relire();
|
|
}
|
|
|
|
/// Remet à zéro au changement d'installation — la prévision appartient à la box quittée.
|
|
void clearForSwitch() {
|
|
_snapshot = null;
|
|
_erreur = null;
|
|
_site = null;
|
|
_refusDesignation = null;
|
|
_luUneFois = false;
|
|
notifyListeners();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_timer?.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|