import 'dart:math' as math; enum WeatherKind { sun, partlyCloudy, cloudy, moon } class HourlyFlow { final int hour; final double solToHouse; final double solToEcs; final double solToVe; final double solToBatt; final double solToGrid; final double battToHouse; final double gridToHouse; const HourlyFlow({ required this.hour, this.solToHouse = 0, this.solToEcs = 0, this.solToVe = 0, this.solToBatt = 0, this.solToGrid = 0, this.battToHouse = 0, this.gridToHouse = 0, }); double get totalPositive => solToHouse + solToEcs + solToVe + solToBatt + solToGrid; double get totalNegative => battToHouse + gridToHouse; } class WeatherPoint { final int hour; final WeatherKind kind; final int tempC; const WeatherPoint({ required this.hour, required this.kind, required this.tempC, }); } class DayPlan { final List hours; final List weather; final double nowHour; const DayPlan({ required this.hours, required this.weather, required this.nowHour, }); static DayPlan demo() { final now = DateTime.now(); final nowHour = now.hour + now.minute / 60.0; final hours = List.generate(24, (h) { final solar = h >= 6 && h <= 20 ? 3.8 * math.exp(-math.pow(h - 13, 2) / 18.0) : 0.0; final baseHouse = 1.2; final peakHouse = (h >= 7 && h <= 9) || (h >= 18 && h <= 21) ? 0.7 : 0.0; final house = baseHouse + peakHouse; final excess = math.max(0.0, solar - house); final deficit = math.max(0.0, house - solar); final solToHouse = math.min(solar, house); final solToEcs = excess > 0.5 ? math.min(excess * 0.35, 0.7) : 0.0; final solToVe = excess > 1.2 ? math.min(excess * 0.3, 1.1) : 0.0; final solToBatt = excess > 0.3 ? math.min(excess * 0.15, 0.4) : 0.0; final solToGrid = math.max(0.0, excess - solToEcs - solToVe - solToBatt); final battToHouse = h >= 17 ? math.min(deficit * 0.55, 0.8) : 0.0; final gridToHouse = math.max(0.0, deficit - battToHouse); return HourlyFlow( hour: h, solToHouse: solToHouse, solToEcs: solToEcs, solToVe: solToVe, solToBatt: solToBatt, solToGrid: solToGrid, battToHouse: battToHouse, gridToHouse: gridToHouse, ); }); final weather = [ const WeatherPoint(hour: 0, kind: WeatherKind.moon, tempC: 17), const WeatherPoint(hour: 4, kind: WeatherKind.moon, tempC: 15), const WeatherPoint(hour: 8, kind: WeatherKind.partlyCloudy, tempC: 19), const WeatherPoint(hour: 12, kind: WeatherKind.sun, tempC: 25), const WeatherPoint(hour: 16, kind: WeatherKind.sun, tempC: 27), const WeatherPoint(hour: 20, kind: WeatherKind.partlyCloudy, tempC: 22), ]; return DayPlan(hours: hours, weather: weather, nowHour: nowHour); } } class HeosAction { final String iconKey; final String title; final String why; final double powerKw; const HeosAction({ required this.iconKey, required this.title, required this.why, required this.powerKw, }); } class HeosDecision { final String summary; final String highlight; final List actions; final DateTime updatedAt; const HeosDecision({ required this.summary, required this.highlight, required this.actions, required this.updatedAt, }); static HeosDecision demo() => HeosDecision( summary: 'Le surplus solaire part dans l\'eau chaude et la voiture ' 'plutôt que d\'être revendu à bas prix.', highlight: 'plutôt que d\'être revendu à bas prix', actions: [ const HeosAction( iconKey: 'water', title: 'Chauffe-eau', why: 'Surplus PV disponible · coût réseau ce soir 0,22 €/kWh', powerKw: 2.0, ), const HeosAction( iconKey: 'ev', title: 'Voiture — mode Éco', why: 'Charge à partir du surplus · objectif 80 % pour 18h', powerKw: 3.6, ), const HeosAction( iconKey: 'battery', title: 'Batterie en charge', why: 'Stockage pour couvrir le pic 18h–21h', powerKw: 0.8, ), ], updatedAt: DateTime.now(), ); }