result static method
Selects known fields only, and validates all native/provider output.
Implementation
static Map<String, dynamic> result(
String action,
Object? raw, {
int deviceLimit = maxDevices,
int valueLimit = maxValueBytes,
}) {
if (raw is! Map) throw const FormatException('Invalid BLE result.');
bool flag(Map value, String key) {
if (value[key] is! bool) {
throw const FormatException('Invalid BLE status.');
}
return value[key] as bool;
}
Map<String, dynamic> characteristic(Map value) => {
'uuid': uuid(value['uuid']),
for (final key in [
'read',
'write',
'writeWithoutResponse',
'notify',
'indicate',
])
key: flag(value, key),
};
switch (action) {
case 'bluetooth.getState':
return {
for (final key in ['available', 'enabled', 'permissionGranted'])
key: flag(raw, key),
};
case 'bluetooth.scan':
final devices = raw['devices'];
if (devices is! List || devices.length > deviceLimit) {
throw const FormatException('Too many BLE devices.');
}
final seen = <String>{};
return {
'devices': devices
.map((value) {
if (value is! Map) {
throw const FormatException('Invalid BLE device.');
}
final id = deviceId(value['deviceId']);
final name = value['name'];
final rssi = value['rssi'];
if (!seen.add(id) ||
name is! String ||
name.length > 80 ||
RegExp(r'[\x00-\x1f\x7f-\x9f]').hasMatch(name) ||
rssi is! int ||
rssi < -127 ||
rssi > 20) {
throw const FormatException('Invalid BLE device metadata.');
}
return {
'deviceId': id,
'name': name,
'rssi': rssi,
'serviceUuids': uuids(value['serviceUuids'] ?? const []),
};
})
.toList(growable: false),
};
case 'bluetooth.connect':
return {
'deviceId': deviceId(raw['deviceId']),
'connected': flag(raw, 'connected'),
};
case 'bluetooth.discoverServices':
final services = raw['services'];
if (services is! List || services.length > 32) {
throw const FormatException('Too many BLE services.');
}
var count = 0;
return {
'services': services
.map((value) {
if (value is! Map) {
throw const FormatException('Invalid BLE service.');
}
final chars = value['characteristics'];
if (chars is! List ||
chars.length > 64 ||
(count += chars.length) > 256) {
throw const FormatException('Too many BLE characteristics.');
}
return {
'uuid': uuid(value['uuid']),
'characteristics': chars
.map((v) {
if (v is! Map) {
throw const FormatException(
'Invalid BLE characteristic.',
);
}
return characteristic(v);
})
.toList(growable: false),
};
})
.toList(growable: false),
};
case 'bluetooth.read':
case 'bluetooth.notification':
return {
'serviceUuid': uuid(raw['serviceUuid']),
'characteristicUuid': uuid(raw['characteristicUuid']),
'valueHex': hex(raw['valueHex'], limit: valueLimit),
};
case 'bluetooth.write':
final bytes = raw['bytesWritten'];
if (bytes is! int || bytes < 1 || bytes > maxWriteBytes) {
throw const FormatException('Invalid BLE write result.');
}
return {'bytesWritten': bytes};
case 'bluetooth.subscribe':
case 'bluetooth.unsubscribe':
return {'subscribed': flag(raw, 'subscribed')};
case 'bluetooth.stopScan':
case 'bluetooth.disconnect':
return {'changed': flag(raw, 'changed')};
default:
throw const FormatException('Unknown BLE result.');
}
}