import 'dart:async'; import 'dart:convert'; import 'dart:io'; class Rpc { final WebSocket ws; int _id = 0; final _pending = >>{}; Rpc(this.ws) { ws.listen((raw) { final m = jsonDecode(raw as String) as Map; final rid = m['id']; if (rid is int && _pending.containsKey(rid)) _pending.remove(rid)!.complete(m); }); } Future> call(String m, [Map p = const {}]) { final id = ++_id; final c = Completer>(); _pending[id] = c; ws.add(jsonEncode({'id': id, 'method': m, 'params': p})); return c.future.timeout(const Duration(seconds: 20)); } } Future open(String host, String? locale) async { final ws = await WebSocket.connect('ws://$host:4444'); final rpc = Rpc(ws); final hello = await rpc.call('JSONRPC.Hello', locale == null ? {} : {'locale': locale}); final p = hello['params'] as Map; print('Hello(locale=${locale ?? ""}) -> locale renvoyée: "${p['locale']}" language: "${p['language']}"'); return rpc; } /// Renvoie quelques displayName de ThingClass + leurs states/actions. Future sample(Rpc rpc, String tag) async { final r = await rpc.call('Integrations.GetThingClasses', {}); final classes = (r['params']['thingClasses'] as List).cast>(); print(' [$tag] ${classes.length} thingClasses'); for (final name in ['Smart Meter', 'Wallbox', 'Relay', 'Light']) { final hit = classes.firstWhere( (c) => (c['displayName'] as String?)?.contains(name) ?? false, orElse: () => {}); if (hit.isNotEmpty) { print(' [$tag] ThingClass "${hit['displayName']}" (name=${hit['name']})'); final states = (hit['stateTypes'] as List?) ?? []; if (states.isNotEmpty) { print(' [$tag] state[0].displayName = "${states.first['displayName']}"'); } break; } } // Premiers displayName bruts, pour comparaison inter-locale final first = classes.take(5).map((c) => c['displayName']).toList(); print(' [$tag] 5 premiers: $first'); final v = await rpc.call('Integrations.GetVendors', {}); final vendors = (v['params']['vendors'] as List).cast>(); print(' [$tag] vendors: ${vendors.take(5).map((e) => e['displayName']).toList()}'); } Future main(List args) async { final host = args.isNotEmpty ? args[0] : '192.168.1.75'; print('=== 1. Deux connexions SIMULTANÉES, locales différentes ==='); final de = await open(host, 'de_DE'); final fr = await open(host, 'fr_FR'); await sample(de, 'de_DE'); await sample(fr, 'fr_FR'); print('\n=== 2. Locale omise (défaut du processus) ==='); final def = await open(host, null); await sample(def, 'défaut'); print('\n=== 3. Locale absurde ==='); final zz = await open(host, 'zz_ZZ'); await sample(zz, 'zz_ZZ'); print('\n=== 4. Re-Hello sur une connexion existante (de_DE -> fr_FR) ==='); final h = await de.call('JSONRPC.Hello', {'locale': 'fr_FR'}); print(' locale renvoyée après re-Hello: "${h['params']['locale']}"'); await sample(de, 'de->fr'); print('\n=== 5. Nos chaînes NymeaEnergy ==='); final lc = await fr.call('NymeaEnergy.GetLoadConfig', {}); print(' [fr_FR] label de charge: ' '${(lc['params']['loadConfigs'] as List).map((e) => e['label']).toList()}'); await de.ws.close(); await fr.ws.close(); await def.ws.close(); await zz.ws.close(); }