class IgnisDevice { final String id; final String? mac; final String name; final String? model; final String? ip; const IgnisDevice({ required this.id, required this.name, this.mac, this.model, this.ip, }); String get groupMemberId => mac ?? id; String? get subtitle { final parts = [?mac, ?ip]; return parts.isEmpty ? null : parts.join(' - '); } static IgnisDevice fromApi(Object? value, {String? fallbackId}) { if (value is Map) { final map = Map.from(value); final id = _stringValue(map, const ['id', 'mac', 'mac_address', 'device_id']) ?? fallbackId; if (id == null || id.isEmpty) { throw const FormatException('device не содержит id/mac'); } final mac = _stringValue(map, const ['mac', 'mac_address']); final model = _stringValue(map, const ['model']); final name = _stringValue(map, const ['name', 'label', 'model']) ?? mac ?? id; final ip = _stringValue(map, const ['ip', 'address']); return IgnisDevice(id: id, mac: mac, name: name, model: model, ip: ip); } final id = value?.toString() ?? fallbackId; if (id == null || id.isEmpty) { throw const FormatException('device должен быть объектом или id'); } return IgnisDevice(id: id, mac: id, name: id); } static List listFromApi(Object? data) { final values = _collectionValues(data, const ['data', 'devices']); return values .map( (value) => value.entryKey == null ? IgnisDevice.fromApi(value.value) : IgnisDevice.fromApi(value.value, fallbackId: value.entryKey), ) .toList(); } } String? _stringValue(Map map, List keys) { for (final key in keys) { final value = map[key]; if (value != null && value.toString().isNotEmpty) { return value.toString(); } } return null; } List<_CollectionValue> _collectionValues(Object? data, List wrappers) { if (data is List) { return data.map((value) => _CollectionValue(value)).toList(); } if (data is Map) { final map = Map.from(data); for (final wrapper in wrappers) { final value = map[wrapper]; if (value is List) { return value.map((item) => _CollectionValue(item)).toList(); } } return map.entries .map((entry) => _CollectionValue(entry.value, entryKey: entry.key)) .toList(); } throw const FormatException('ожидался список или объект'); } class _CollectionValue { final Object? value; final String? entryKey; const _CollectionValue(this.value, {this.entryKey}); }