BLE Handling
This commit is contained in:
@@ -105,11 +105,8 @@ static ssize_t write_lasertag_val(struct bt_conn *conn, const struct bt_gatt_att
|
||||
if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0)
|
||||
{
|
||||
rc = lasertag_set_device_name(buf, len);
|
||||
if (rc == 0 && adv_enabled) {
|
||||
LOG_INF("Stopping advertising to update device name");
|
||||
ble_mgmt_adv_stop();
|
||||
LOG_INF("Restarting advertising with new device name");
|
||||
ble_mgmt_adv_start();
|
||||
if (rc == 0 ) {
|
||||
bt_set_name(lasertag_get_device_name());
|
||||
}
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_PANID_CHAR) == 0)
|
||||
|
||||
@@ -7,6 +7,7 @@ class LasertagDevice {
|
||||
final int type;
|
||||
final bool isConnected;
|
||||
final BluetoothDevice btDevice;
|
||||
final bool isNameVerified;
|
||||
|
||||
LasertagDevice({
|
||||
required this.id,
|
||||
@@ -14,8 +15,19 @@ class LasertagDevice {
|
||||
required this.type,
|
||||
required this.btDevice,
|
||||
this.isConnected = false,
|
||||
this.isNameVerified = false,
|
||||
});
|
||||
|
||||
// Hilfsmethode: Ist es ein Leader?
|
||||
bool get isLeader => type == LasertagUUIDs.typeLeader;
|
||||
|
||||
LasertagDevice copyWith({String? name, bool? isNameVerified}) {
|
||||
return LasertagDevice(
|
||||
id: id,
|
||||
type: type,
|
||||
btDevice: btDevice,
|
||||
name: name ?? this.name,
|
||||
isNameVerified: isNameVerified ?? this.isNameVerified,
|
||||
isConnected: isConnected,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ class DeviceProvider extends ChangeNotifier {
|
||||
List<LasertagDevice> get leaders => _discoveredLeaders;
|
||||
List<LasertagDevice> get peripherals => _discoveredPeripherals;
|
||||
|
||||
// 1. HARDWARE LESEN
|
||||
Future<String> readDeviceNameFromHardware(LasertagDevice ltDevice) async {
|
||||
try {
|
||||
if (!ltDevice.btDevice.isConnected) {
|
||||
@@ -22,21 +23,15 @@ class DeviceProvider extends ChangeNotifier {
|
||||
);
|
||||
}
|
||||
|
||||
List<BluetoothService> services = await ltDevice.btDevice
|
||||
.discoverServices();
|
||||
var service = services.firstWhere(
|
||||
(s) => s.uuid == Guid(LasertagUUIDs.provService),
|
||||
);
|
||||
var characteristic = service.characteristics.firstWhere(
|
||||
(c) => c.uuid == Guid(LasertagUUIDs.provNameChar),
|
||||
);
|
||||
List<BluetoothService> services = await ltDevice.btDevice.discoverServices();
|
||||
var service = services.firstWhere((s) => s.uuid == Guid(LasertagUUIDs.provService));
|
||||
var characteristic = service.characteristics.firstWhere((c) => c.uuid == Guid(LasertagUUIDs.provNameChar));
|
||||
|
||||
// Namen von der Hardware lesen
|
||||
List<int> value = await characteristic.read();
|
||||
String hardwareName = utf8.decode(value);
|
||||
|
||||
// Lokalen Cache in der App ebenfalls aktualisieren, um Caching zu umgehen
|
||||
updateDeviceName(ltDevice.id, hardwareName);
|
||||
// Den Namen lokal speichern und als verifiziert markieren (für das Styling)
|
||||
updateDeviceName(ltDevice.id, hardwareName, verified: true);
|
||||
|
||||
return hardwareName;
|
||||
} catch (e) {
|
||||
@@ -45,10 +40,8 @@ class DeviceProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateDeviceNameOnHardware(
|
||||
LasertagDevice ltDevice,
|
||||
String newName,
|
||||
) async {
|
||||
// 2. HARDWARE SCHREIBEN
|
||||
Future<void> updateDeviceNameOnHardware(LasertagDevice ltDevice, String newName) async {
|
||||
try {
|
||||
await ltDevice.btDevice.connect(
|
||||
license: License.free,
|
||||
@@ -58,21 +51,14 @@ class DeviceProvider extends ChangeNotifier {
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
List<BluetoothService> services = await ltDevice.btDevice
|
||||
.discoverServices();
|
||||
List<BluetoothService> services = await ltDevice.btDevice.discoverServices();
|
||||
var service = services.firstWhere((s) => s.uuid == Guid(LasertagUUIDs.provService));
|
||||
var characteristic = service.characteristics.firstWhere((c) => c.uuid == Guid(LasertagUUIDs.provNameChar));
|
||||
|
||||
var service = services.firstWhere(
|
||||
(s) => s.uuid == Guid(LasertagUUIDs.provService),
|
||||
);
|
||||
var characteristic = service.characteristics.firstWhere(
|
||||
(c) => c.uuid == Guid(LasertagUUIDs.provNameChar),
|
||||
);
|
||||
|
||||
// 1. Auf Hardware schreiben (UTF-8)
|
||||
await characteristic.write(utf8.encode(newName));
|
||||
|
||||
// 2. Lokal in der App-Liste aktualisieren
|
||||
updateDeviceName(ltDevice.id, newName);
|
||||
// Lokal aktualisieren und als verifiziert markieren
|
||||
updateDeviceName(ltDevice.id, newName, verified: true);
|
||||
|
||||
await ltDevice.btDevice.disconnect();
|
||||
} catch (e) {
|
||||
@@ -81,71 +67,42 @@ class DeviceProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void updateDeviceName(String id, String newName) {
|
||||
// In den Leadern suchen
|
||||
int leaderIndex = _discoveredLeaders.indexWhere((d) => d.id == id);
|
||||
if (leaderIndex != -1) {
|
||||
final old = _discoveredLeaders[leaderIndex];
|
||||
_discoveredLeaders[leaderIndex] = LasertagDevice(
|
||||
id: old.id,
|
||||
name: newName,
|
||||
type: old.type,
|
||||
btDevice: old.btDevice,
|
||||
isConnected: old.isConnected,
|
||||
);
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
// In der Ausrüstung suchen
|
||||
int peripheralIndex = _discoveredPeripherals.indexWhere((d) => d.id == id);
|
||||
if (peripheralIndex != -1) {
|
||||
final old = _discoveredPeripherals[peripheralIndex];
|
||||
_discoveredPeripherals[peripheralIndex] = LasertagDevice(
|
||||
id: old.id,
|
||||
name: newName,
|
||||
type: old.type,
|
||||
btDevice: old.btDevice,
|
||||
isConnected: old.isConnected,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
// 3. LOKALE LISTE AKTUALISIEREN (Hilfsmethode konsolidiert)
|
||||
void updateDeviceName(String id, String newName, {bool verified = false}) {
|
||||
_updateDeviceInLists(
|
||||
id,
|
||||
(old) => old.copyWith(name: newName, isNameVerified: verified)
|
||||
);
|
||||
}
|
||||
|
||||
// 4. BLUETOOTH SCAN
|
||||
void startScan() async {
|
||||
// Listen leeren für neuen Scan
|
||||
_discoveredLeaders.clear();
|
||||
_discoveredPeripherals.clear();
|
||||
notifyListeners();
|
||||
|
||||
// 1. Bluetooth-Status prüfen
|
||||
await FlutterBluePlus.stopScan(); // Scan sauber stoppen vor Neustart
|
||||
|
||||
await FlutterBluePlus.adapterState
|
||||
.where((s) => s == BluetoothAdapterState.on)
|
||||
.first;
|
||||
|
||||
// 2. Scan-Ergebnisse verarbeiten
|
||||
_scanSubscription?.cancel();
|
||||
_scanSubscription = FlutterBluePlus.scanResults.listen((results) {
|
||||
for (ScanResult r in results) {
|
||||
bool hasService = r.advertisementData.serviceUuids.contains(
|
||||
Guid(LasertagUUIDs.provService),
|
||||
);
|
||||
bool hasService = r.advertisementData.serviceUuids.contains(Guid(LasertagUUIDs.provService));
|
||||
if (!hasService) continue;
|
||||
|
||||
final mfgData =
|
||||
r.advertisementData.manufacturerData[65535]; // Unsere ID 0xFFFF
|
||||
final mfgData = r.advertisementData.manufacturerData[65535];
|
||||
|
||||
if (mfgData != null && mfgData.isNotEmpty) {
|
||||
int type = mfgData[0]; // Typ-Byte vom nRF52
|
||||
|
||||
final device = LasertagDevice(
|
||||
id: r.device.remoteId.toString(),
|
||||
name: r.device.platformName.isEmpty
|
||||
? "Unbekannt"
|
||||
: r.device.platformName,
|
||||
type: type,
|
||||
name: r.device.platformName.isEmpty ? "Unbekannt" : r.device.platformName,
|
||||
type: mfgData[0],
|
||||
btDevice: r.device,
|
||||
isConnected: false,
|
||||
isNameVerified: false, // Initial immer unversichert (Kursiv)
|
||||
);
|
||||
|
||||
_addDeviceToLists(device);
|
||||
@@ -153,7 +110,6 @@ class DeviceProvider extends ChangeNotifier {
|
||||
}
|
||||
});
|
||||
|
||||
// 3. Bluetooth-Scan starten
|
||||
try {
|
||||
await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15));
|
||||
} catch (e) {
|
||||
@@ -162,17 +118,45 @@ class DeviceProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void _addDeviceToLists(LasertagDevice device) {
|
||||
if (device.isLeader) {
|
||||
if (!_discoveredLeaders.any((d) => d.id == device.id)) {
|
||||
_discoveredLeaders.add(device);
|
||||
_sortList(_discoveredLeaders);
|
||||
notifyListeners();
|
||||
}
|
||||
} else {
|
||||
if (!_discoveredPeripherals.any((d) => d.id == device.id)) {
|
||||
_discoveredPeripherals.add(device);
|
||||
_sortList(_discoveredPeripherals);
|
||||
List<LasertagDevice> list = device.isLeader ? _discoveredLeaders : _discoveredPeripherals;
|
||||
|
||||
if (!list.any((d) => d.id == device.id)) {
|
||||
list.add(device);
|
||||
_sortList(list);
|
||||
notifyListeners();
|
||||
_verifyNameInBackground(device); // Hintergrund-Check anstoßen
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _verifyNameInBackground(LasertagDevice device) async {
|
||||
if (device.isNameVerified) return;
|
||||
|
||||
try {
|
||||
await device.btDevice.connect(license: License.free, timeout: const Duration(seconds: 5));
|
||||
|
||||
List<BluetoothService> services = await device.btDevice.discoverServices();
|
||||
var service = services.firstWhere((s) => s.uuid == Guid(LasertagUUIDs.provService));
|
||||
var characteristic = service.characteristics.firstWhere((c) => c.uuid == Guid(LasertagUUIDs.provNameChar));
|
||||
|
||||
List<int> value = await characteristic.read();
|
||||
String realName = utf8.decode(value);
|
||||
|
||||
updateDeviceName(device.id, realName, verified: true);
|
||||
|
||||
await device.btDevice.disconnect();
|
||||
} catch (e) {
|
||||
debugPrint("Background Sync fehlgeschlagen für ${device.id}: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void _updateDeviceInLists(String id, LasertagDevice Function(LasertagDevice) updateFn) {
|
||||
for (var list in [_discoveredLeaders, _discoveredPeripherals]) {
|
||||
int index = list.indexWhere((d) => d.id == id);
|
||||
if (index != -1) {
|
||||
list[index] = updateFn(list[index]);
|
||||
_sortList(list);
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,4 +174,4 @@ class DeviceProvider extends ChangeNotifier {
|
||||
_scanSubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,15 @@ class _DeviceSelectionScreenState extends State<DeviceSelectionScreen> {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.appTitle)),
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.appTitle),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => context.read<DeviceProvider>().startScan(),
|
||||
icon: Icon(Icons.refresh),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
_buildHeader(context, l10n.listTypeLeader),
|
||||
@@ -74,16 +82,24 @@ class _DeviceSelectionScreenState extends State<DeviceSelectionScreen> {
|
||||
),
|
||||
title: Text(
|
||||
device.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: device.isNameVerified
|
||||
? FontStyle.normal
|
||||
: FontStyle.italic, // Kursiv, wenn nicht verifiziert
|
||||
color: device.isNameVerified
|
||||
? Theme.of(context).colorScheme.onSurface
|
||||
: Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
|
||||
),
|
||||
),
|
||||
subtitle: Text("BTLE-ID: ${device.id}"),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => RenameDialog(device: device),
|
||||
);
|
||||
},
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => RenameDialog(device: device),
|
||||
);
|
||||
},
|
||||
);
|
||||
}, childCount: devices.length),
|
||||
);
|
||||
|
||||
@@ -20,7 +20,12 @@ class _RenameDialogState extends State<RenameDialog> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController(text: widget.device.name);
|
||||
_fetchHardwareName(); // Namen beim Öffnen abfragen
|
||||
if (!widget.device.isNameVerified) {
|
||||
// Wenn der Name bereits verifiziert ist, nicht von der Hardware lesen
|
||||
_isReading = false;
|
||||
} else {
|
||||
_fetchHardwareName(); // Namen beim Öffnen abfragen
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchHardwareName() async {
|
||||
|
||||
Reference in New Issue
Block a user