/// Une installation HEMS enregistrée. /// /// **Identité stable = [uuid]** (Server UUID renvoyé par `JSONRPC.Hello`), jamais /// l'IP : en DHCP, [host] change. Le [token] est un **credential** persisté à /// part (Keychain/Keystore via `flutter_secure_storage`) — il ne figure donc /// **pas** dans [toJson] (métadonnées SharedPreferences uniquement). class Installation { /// Server UUID (clé d'identité, Hello `uuid`). final String uuid; /// Nom de la box (Hello `name`). final String name; /// Hôte courant (IP/hostname) — peut changer en DHCP, jamais une identité. final String host; /// Port JSON-RPC. Défaut **4444** (ws clair, LAN). final int port; /// Dernière fois où la box a répondu (connexion ou découverte). final DateTime? lastSeen; /// Token d'authentification — **transitoire en mémoire**, persisté séparément /// par UUID dans le secure storage. `null` tant que la box n'est pas appairée. final String? token; const Installation({ required this.uuid, required this.name, required this.host, this.port = 4444, this.lastSeen, this.token, }); bool get hasToken => token != null && token!.isNotEmpty; Installation copyWith({ String? name, String? host, int? port, DateTime? lastSeen, String? token, }) => Installation( uuid: uuid, name: name ?? this.name, host: host ?? this.host, port: port ?? this.port, lastSeen: lastSeen ?? this.lastSeen, token: token ?? this.token, ); /// Métadonnées persistées (SharedPreferences). **Sans token** (secure storage). Map toJson() => { 'uuid': uuid, 'name': name, 'host': host, 'port': port, if (lastSeen != null) 'lastSeen': lastSeen!.toIso8601String(), }; factory Installation.fromJson(Map j) => Installation( uuid: j['uuid'] as String, name: (j['name'] as String?) ?? 'nymea', host: j['host'] as String, port: (j['port'] as num?)?.toInt() ?? 4444, lastSeen: j['lastSeen'] != null ? DateTime.tryParse(j['lastSeen'] as String) : null, ); }