Lot Connexions multi-HEMS : - modèle Installation + InstallationStore (persistance par UUID ; métadonnées SharedPreferences, token via flutter_secure_storage) - ConnectionManager au-dessus de nymea_service : mono-connexion, switchTo, connectNew, enterDemo (démo = active factice), contrôle d'identité DHCP - nymea_service : connect() réveillé (ws://4444, token par UUID), capture Hello (uuid/name/initialSetupRequired), authenticate/createUser, resetState - écran Installations (3 états) + déverrouillage installateur depuis la racine - écran Connexion : auth 2 branches (JSONRPC.Authenticate / CreateUser), détection auto via initialSetupRequired, segmenteur de repli - gate de routage (redirect + refreshListenable merge[cm,svc]) ; hasActiveConnection = isSimulation || (connected && authenticated) - en-tête drawer cliquable → Installations - invalidation des caches au switch (service.resetState + clearForSwitch rôles/scheduler/tariff), sans persist() Lot Rôles & appareils : - EnergySetupProvider refondu en tri-état (present/absent/non-configuré) - écran roles_devices_screen (3 zones, drag-priorité, inférence solaire/batterie) - LoadDescriptor (etmvariableload, rév.2 : PAC exclue → SG-Ready) ; Energy.SetRootMeter réel, Get/SetLoadConfig en stub loggé Correctifs : - bug signe énergie : production +, consommation NÉGATIVE sur nymea 1.15.2 → consumptionW/home en .abs() (autoconso n'est plus clouée à 0) - UUID Hello brace-wrapped normalisé Tests : flutter analyze 0 erreur ; gate + zéro-fuite au switch (5/5 verts). Validé en vrai : login auth .120, .75 ouvert, switch, déverrouillage installateur. Note : embarque aussi le WIP dashboard/thème déjà présent dans l'arbre. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
98 lines
2.9 KiB
Dart
98 lines
2.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../../../theme/etm_tokens.dart';
|
|
import '../models/dashboard_models.dart';
|
|
import '../painters/day_plan_painter.dart';
|
|
|
|
class DayPlanCard extends StatelessWidget {
|
|
const DayPlanCard({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final plan = DayPlan.demo();
|
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: EtmTokens.surfaceOf(context),
|
|
borderRadius: BorderRadius.circular(EtmTokens.radiusCard),
|
|
boxShadow: EtmTokens.cardShadowOf(context),
|
|
),
|
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 12),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text('Plan de la journée',
|
|
style: EtmTokens.sans(
|
|
size: 15,
|
|
weight: FontWeight.w600,
|
|
color: EtmTokens.inkOf(context))),
|
|
Text("aujourd'hui",
|
|
style: EtmTokens.sans(
|
|
size: 12, color: EtmTokens.mutedOf(context))),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
SizedBox(
|
|
height: 180,
|
|
child: CustomPaint(
|
|
painter: DayPlanPainter(
|
|
plan: plan,
|
|
surfaceColor: EtmTokens.surfaceOf(context),
|
|
lineColor: EtmTokens.lineOf(context),
|
|
inkColor: EtmTokens.inkOf(context),
|
|
mutedColor: EtmTokens.mutedOf(context),
|
|
isDark: isDark,
|
|
),
|
|
size: const Size(double.infinity, 180),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
_Legend(context: context),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _Legend extends StatelessWidget {
|
|
final BuildContext context;
|
|
const _Legend({required this.context});
|
|
|
|
@override
|
|
Widget build(BuildContext _) {
|
|
final items = [
|
|
('Maison', EtmTokens.solToHouse),
|
|
('ECS', EtmTokens.solToEcs),
|
|
('VE', EtmTokens.solToVe),
|
|
('Batterie', EtmTokens.solToBatt),
|
|
('Réseau', EtmTokens.solToGrid),
|
|
('Batt.→maison', EtmTokens.battToHouse),
|
|
];
|
|
return Wrap(
|
|
spacing: 12,
|
|
runSpacing: 6,
|
|
children: items.map((e) {
|
|
return Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
width: 10, height: 10,
|
|
decoration: BoxDecoration(
|
|
color: e.$2,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text(e.$1,
|
|
style: EtmTokens.sans(
|
|
size: 10, color: EtmTokens.mutedOf(context))),
|
|
],
|
|
);
|
|
}).toList(),
|
|
);
|
|
}
|
|
}
|