fromPacket static method

List<BleAdvertisement> fromPacket(
  1. String raw
)

fromPacket creates a List<BleAdvertisement> from a raw message following this structure: MAC_ADDRESS;UNIX;LAT;LNG;ALT;MODEL;RSSI;MANUFACTURER+DATA;SERVICE+DATA;CRC16

Implementation

static List<BleAdvertisement> fromPacket(String raw) {
  final parts = raw.split(';');
  if (parts.isEmpty) return [];

  if (parts.length == 1) return [];
  // The parts should be divisible by 4, can be multiple groups

  if (parts.length % 12 != 0) {
    throw ParseException('Invalid advertisement definition');
  }

  // Separate each group
  final List<BleAdvertisement> advertisements = [];

  for (int i = 0; i < parts.length; i += 12) {
    final rawMacAddress = parts[i];
    final rawUnix = parts[i + 1];
    final rawLatitude = parts[i + 2];
    final rawLongitude = parts[i + 3];
    final rawAltitude = parts[i + 4];
    final model = parts[i + 5];
    final deviceName = parts[i + 6];
    final rawRssi = parts[i + 7];
    final rawTxPower = parts[i + 8];
    final rawManufacturerData = parts[i + 9];
    final rawServiceData = parts[i + 10];

    final receivedCrc = int.tryParse(parts[i + 11], radix: 16) ?? 0;

    final calculatedCrc = calculateCrc('${parts.sublist(i, i + 11).join(';')};'.codeUnits);
    if (receivedCrc != calculatedCrc) {
      throw CrcException(
        'Invalid CRC, expected ${receivedCrc.toRadixString(16)}, '
        'got ${calculatedCrc.toRadixString(16)}',
      );
    }

    final macParts = rawMacAddress.split('');
    if (macParts.length != 12) {
      throw MalformedException('Invalid MAC Address');
    }

    String macAddress = '';
    for (int i = 0; i < macParts.length; i += 2) {
      macAddress += '${macParts[i]}${macParts[i + 1]}';
      if (i + 2 < macParts.length) {
        macAddress += ':';
      }
    }

    DateTime timestamp;
    try {
      timestamp = DateTime.fromMillisecondsSinceEpoch(int.parse(rawUnix) * 1000, isUtc: true);
    } catch (e) {
      throw MalformedException('Invalid timestamp');
    }

    double? latitude;
    if (rawLatitude.isNotEmpty) {
      latitude = double.tryParse(rawLatitude);
    }

    double? longitude;
    if (rawLongitude.isNotEmpty) {
      longitude = double.tryParse(rawLongitude);
    }

    double? altitude;
    if (rawAltitude.isNotEmpty) {
      altitude = double.tryParse(rawAltitude);
    }

    int rssi = int.tryParse(rawRssi) ?? 0;
    int? txPower = int.tryParse(rawTxPower);

    List<BleManufacturerData> manufacturerData = [];
    for (String mfd in rawManufacturerData.split(',')) {
      if (mfd.isEmpty) continue;
      final subparts = mfd.split(':');
      if (subparts.isEmpty) continue;

      if (subparts.length != 2) {
        throw MalformedException('Invalid manufacturer data "$mfd"');
      }

      final companyId = int.tryParse(subparts[0], radix: 16) ?? 0;
      List<int> data = [];
      for (int i = 0; i < subparts[1].length; i += 2) {
        data.add(int.tryParse(subparts[1].substring(i, i + 2), radix: 16) ?? 0);
      }
      manufacturerData.add(BleManufacturerData(companyId: companyId, data: data));
    }

    List<BleServiceData> serviceData = [];

    for (String sfd in rawServiceData.split(',')) {
      if (sfd.isEmpty) continue;
      final subparts = sfd.split(':');
      if (subparts.isEmpty) continue;

      if (subparts.length != 2) {
        throw MalformedException('Invalid service data "$sfd"');
      }

      final uuid = int.tryParse(subparts[0], radix: 16) ?? 0;
      List<int> data = [];
      for (int i = 0; i < subparts[1].length; i += 2) {
        data.add(int.tryParse(subparts[1].substring(i, i + 2), radix: 16) ?? 0);
      }

      serviceData.add(BleServiceData(uuid: uuid, data: data));
    }

    advertisements.add(
      BleAdvertisement(
        deviceName: deviceName,
        macAddress: macAddress,
        timestamp: timestamp,
        rssi: rssi,
        model: model,
        latitude: latitude,
        longitude: longitude,
        altitude: altitude,
        txPower: txPower,
        manufacturerData: manufacturerData,
        serviceData: serviceData,
      ),
    );
  }

  return advertisements;
}