etm-powersync-app/lib/providers/tariff_provider.dart
Patrick Schurig 71d88418fd feat(tarif): l'écran Tarif lit et écrit le tarif du moteur (+etm69)
Consigne app §F, vérifiée sur le banc le 2026-10-15.
- Lecture GetTariff à l'ouverture : aucun tarif / fixe / HC-HP (deux
  zones, la moins chère sur UNE plage) / lecture seule pour tout le reste
  (HC en deux fenêtres, > 2 zones, prix égaux) — jamais réécrit.
- Écriture SetTariff en c€/kWh (€ × 100, 4 décimales) ; HC/HP : la plage
  HP est le complément exact de la plage HC ; revente facultative. Relue
  après succès (aucune notification).
- Refus TARIFF_REFUSED rendu en français depuis faultParams (cause, zone
  nommée par le libellé envoyé, minute en cause).
- Heures affichées comme celles de la box (timeZone).
- Retirés : prix par défaut codés en dur, stockage local, fournisseurs
  fictifs, prix « live » inventés. Menu « Tarifs & providers » → « Tarif ».
- Test : ce que l'app enverrait est identique, champ pour champ, au tarif
  réel du banc (aucune écriture faite sur la box).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:12:21 +02:00

81 lines
2.5 KiB
Dart

import 'package:flutter/foundation.dart';
import '../models/tarif.dart';
import '../services/nymea_service.dart';
/// **Le tarif de la box courante** — lu et écrit sur le moteur, rien en local.
///
/// Avant `+etm69` ce provider était un stub : prix par défaut codés en dur (0,096 / 0,146
/// €/kWh), fournisseurs fictifs (Linky, JSON…), prix « live » inventés, et rien n'allait à la
/// box. Tout cela est retiré : le moteur est désormais la source de vérité (consigne F).
class TariffProvider extends ChangeNotifier {
NymeaService? _service;
bool _loading = false;
bool _enregistrement = false;
TarifBox? _tarif;
String? _erreurLecture;
bool get loading => _loading;
bool get enregistrement => _enregistrement;
/// `null` tant que rien n'est lu, ou si la lecture a échoué ([erreurLecture]).
TarifBox? get tarif => _tarif;
/// Non nul = le tarif n'a pas été LU. Ce n'est pas « aucun tarif ».
String? get erreurLecture => _erreurLecture;
void attach(NymeaService s) {
if (identical(_service, s)) return;
_service = s;
}
Future<void> load() async {
final s = _service;
if (s == null) return;
_loading = true;
notifyListeners();
final r = await s.getTariff();
_tarif = r.tarif == null ? null : TarifBox.fromJson(r.tarif!);
_erreurLecture = r.erreur;
_loading = false;
notifyListeners();
}
/// Écrit le tarif, puis le RELIT (aucune notification n'existe). Rend `null` en cas de
/// succès, sinon un message en français.
Future<String?> enregistrer(Map<String, dynamic> payload) async {
final s = _service;
if (s == null) return 'Pas de box connectée.';
_enregistrement = true;
notifyListeners();
final r = await s.setTariff(payload);
_enregistrement = false;
String? message;
if (r.erreur == 'TARIFF_REFUSED') {
final libelles = [
for (final z in (payload['zones'] as List? ?? const []))
if (z is Map) z['label'] as String? ?? ''
];
message = messageRefus(r.faultParams, libelles);
} else if (r.erreur != null) {
message = 'La box n\'a pas enregistré le tarif (${r.erreur}).';
}
if (message == null) {
await load();
} else {
notifyListeners();
}
return message;
}
/// Remet à zéro au changement d'installation — le tarif appartient à la box quittée.
void clearForSwitch() {
_tarif = null;
_erreurLecture = null;
_loading = false;
_enregistrement = false;
notifyListeners();
}
}