feat: type remote device models
This commit is contained in:
203
lib/models/ignis_group.dart
Normal file
203
lib/models/ignis_group.dart
Normal file
@@ -0,0 +1,203 @@
|
||||
class IgnisGroup {
|
||||
final String id;
|
||||
final String name;
|
||||
final List<String> macs;
|
||||
final IgnisGroupState state;
|
||||
|
||||
const IgnisGroup({
|
||||
required this.id,
|
||||
required this.name,
|
||||
this.macs = const [],
|
||||
this.state = const IgnisGroupState(),
|
||||
});
|
||||
|
||||
IgnisGroup copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
List<String>? macs,
|
||||
IgnisGroupState? state,
|
||||
}) {
|
||||
return IgnisGroup(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
macs: macs ?? this.macs,
|
||||
state: state ?? this.state,
|
||||
);
|
||||
}
|
||||
|
||||
static IgnisGroup fromApi(Object? value, {String? fallbackId}) {
|
||||
if (value is! Map) {
|
||||
final id = value?.toString() ?? fallbackId;
|
||||
if (id == null || id.isEmpty) {
|
||||
throw const FormatException('group должен быть объектом или id');
|
||||
}
|
||||
return IgnisGroup(id: id, name: id);
|
||||
}
|
||||
|
||||
final map = Map<String, dynamic>.from(value);
|
||||
final id = _stringValue(map, const ['id', 'group_id']) ?? fallbackId;
|
||||
if (id == null || id.isEmpty) {
|
||||
throw const FormatException('group не содержит id');
|
||||
}
|
||||
|
||||
final name = _stringValue(map, const ['name', 'label']) ?? id;
|
||||
final macs = _stringList(
|
||||
map['macs'] ?? map['devices'] ?? map['device_ids'],
|
||||
);
|
||||
final state = IgnisGroupState.fromApi(
|
||||
map['last_state'] ?? map['state'] ?? map['status'],
|
||||
);
|
||||
|
||||
return IgnisGroup(id: id, name: name, macs: macs, state: state);
|
||||
}
|
||||
|
||||
static List<IgnisGroup> listFromApi(Object? data) {
|
||||
final values = _collectionValues(data, const ['data', 'groups']);
|
||||
return values.map((value) {
|
||||
if (value.entryKey == null) return IgnisGroup.fromApi(value.value);
|
||||
return IgnisGroup.fromApi(value.value, fallbackId: value.entryKey);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
class IgnisGroupState {
|
||||
final bool isOn;
|
||||
final int brightness;
|
||||
final int temp;
|
||||
final int r;
|
||||
final int g;
|
||||
final int b;
|
||||
final String? sceneId;
|
||||
|
||||
const IgnisGroupState({
|
||||
this.isOn = false,
|
||||
this.brightness = 100,
|
||||
this.temp = 4000,
|
||||
this.r = 255,
|
||||
this.g = 200,
|
||||
this.b = 100,
|
||||
this.sceneId,
|
||||
});
|
||||
|
||||
IgnisGroupState copyWith({
|
||||
bool? isOn,
|
||||
int? brightness,
|
||||
int? temp,
|
||||
int? r,
|
||||
int? g,
|
||||
int? b,
|
||||
String? sceneId,
|
||||
}) {
|
||||
return IgnisGroupState(
|
||||
isOn: isOn ?? this.isOn,
|
||||
brightness: brightness ?? this.brightness,
|
||||
temp: temp ?? this.temp,
|
||||
r: r ?? this.r,
|
||||
g: g ?? this.g,
|
||||
b: b ?? this.b,
|
||||
sceneId: sceneId ?? this.sceneId,
|
||||
);
|
||||
}
|
||||
|
||||
IgnisGroupState applyPatch(Map<String, dynamic> patch) {
|
||||
return copyWith(
|
||||
isOn: patch.containsKey('state') ? patch['state'] == true : null,
|
||||
brightness: _intValue(patch['brightness'] ?? patch['dimming']),
|
||||
temp: _intValue(patch['temp']),
|
||||
r: _intValue(patch['r']),
|
||||
g: _intValue(patch['g']),
|
||||
b: _intValue(patch['b']),
|
||||
sceneId: patch['scene']?.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
static IgnisGroupState fromApi(Object? value, {IgnisGroupState? fallback}) {
|
||||
final base = fallback ?? const IgnisGroupState();
|
||||
if (value is! Map) return base;
|
||||
|
||||
final map = Map<String, dynamic>.from(value);
|
||||
return base.applyPatch({
|
||||
'state': map['state'],
|
||||
'brightness': map['brightness'] ?? map['dimming'],
|
||||
'temp': map['temp'],
|
||||
'r': map['r'],
|
||||
'g': map['g'],
|
||||
'b': map['b'],
|
||||
'scene': map['scene'],
|
||||
});
|
||||
}
|
||||
|
||||
static IgnisGroupState? firstFromStatusResponse(
|
||||
Object? data, {
|
||||
IgnisGroupState? fallback,
|
||||
}) {
|
||||
if (data is! Map) return fallback;
|
||||
final map = Map<String, dynamic>.from(data);
|
||||
final results = map['results'];
|
||||
if (results is! List || results.isEmpty) return fallback;
|
||||
|
||||
Object? validResult;
|
||||
for (final result in results) {
|
||||
if (result is Map &&
|
||||
result['status'] != null &&
|
||||
result['error'] == null) {
|
||||
validResult = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
validResult ??= results.first;
|
||||
|
||||
if (validResult is! Map) return fallback;
|
||||
return IgnisGroupState.fromApi(validResult['status'], fallback: fallback);
|
||||
}
|
||||
}
|
||||
|
||||
String? _stringValue(Map<String, dynamic> map, List<String> keys) {
|
||||
for (final key in keys) {
|
||||
final value = map[key];
|
||||
if (value != null && value.toString().isNotEmpty) {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int? _intValue(Object? value) {
|
||||
if (value is int) return value;
|
||||
if (value is num) return value.toInt();
|
||||
return int.tryParse(value?.toString() ?? '');
|
||||
}
|
||||
|
||||
List<String> _stringList(Object? value) {
|
||||
if (value is! List) return const [];
|
||||
return value.map((item) => item.toString()).toList();
|
||||
}
|
||||
|
||||
List<_CollectionValue> _collectionValues(Object? data, List<String> wrappers) {
|
||||
if (data is List) {
|
||||
return data.map((value) => _CollectionValue(value)).toList();
|
||||
}
|
||||
|
||||
if (data is Map) {
|
||||
final map = Map<String, dynamic>.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});
|
||||
}
|
||||
Reference in New Issue
Block a user