diff --git a/firmware/apps/vest/prj.conf b/firmware/apps/vest/prj.conf index 153d4a0..bea73c3 100644 --- a/firmware/apps/vest/prj.conf +++ b/firmware/apps/vest/prj.conf @@ -42,4 +42,5 @@ CONFIG_BT_ATT_PREPARE_COUNT=5 # Enable Lasertag Shared Modules CONFIG_LASERTAG_UTILS=y CONFIG_THREAD_MGMT=y -CONFIG_BLE_MGMT=y \ No newline at end of file +CONFIG_BLE_MGMT=y +CONFIG_GAME_MGMT=y \ No newline at end of file diff --git a/firmware/libs/ble_mgmt/src/ble_mgmt.c b/firmware/libs/ble_mgmt/src/ble_mgmt.c index 5c074f1..a57f06f 100644 --- a/firmware/libs/ble_mgmt/src/ble_mgmt.c +++ b/firmware/libs/ble_mgmt/src/ble_mgmt.c @@ -204,6 +204,7 @@ static ssize_t write_leader_config(struct bt_conn *conn, const struct bt_gatt_at lasertag_set_thread_pan_id(payload->pan_id); lasertag_set_thread_channel(payload->channel); lasertag_set_thread_ext_pan_id(payload->ext_pan_id); + LOG_HEXDUMP_INF(payload->network_key, 16, "Setting new Thread network key:"); lasertag_set_thread_network_key(payload->network_key); if (payload->network_name[0] != '\0') // Only update if a name is provided { diff --git a/software/app/lib/providers/device_provider.dart b/software/app/lib/providers/device_provider.dart index 80c354e..22fe2d5 100644 --- a/software/app/lib/providers/device_provider.dart +++ b/software/app/lib/providers/device_provider.dart @@ -36,6 +36,23 @@ class DeviceProvider extends ChangeNotifier { await provisionDevice(device, configToUse); } + Future batchProvisionFoundDevices() async { + // 1. Scan für 10 Sekunden starten + startScan(); + await Future.delayed(const Duration(seconds: 10)); + await FlutterBluePlus.stopScan(); + + // 2. Alle gefundenen Peripherals provisionieren + for (var device in _discoveredPeripherals) { + try { + await provisionWithActiveConfig(device, keepName: true); + debugPrint("Provisioniert: ${device.name}"); + } catch (e) { + debugPrint("Fehler bei ${device.name}: $e"); + } + } + } + void startScan() async { _discoveredLeaders.clear(); _discoveredPeripherals.clear(); diff --git a/software/app/lib/ui/screens/leader_config_screen.dart b/software/app/lib/ui/screens/leader_config_screen.dart index 0e43040..c79315c 100644 --- a/software/app/lib/ui/screens/leader_config_screen.dart +++ b/software/app/lib/ui/screens/leader_config_screen.dart @@ -218,6 +218,7 @@ class _LeaderConfigScreenState extends State { networkKey: _netKeyCtrl.text.trim(), ); + context.read().setActiveConfig(newConfig); await context.read().provisionDevice( widget.device, newConfig, diff --git a/software/app/lib/ui/screens/lobby_screen.dart b/software/app/lib/ui/screens/lobby_screen.dart index 5ed73f2..cafc305 100644 --- a/software/app/lib/ui/screens/lobby_screen.dart +++ b/software/app/lib/ui/screens/lobby_screen.dart @@ -1,6 +1,9 @@ // lib/ui/screens/lobby_screen.dart import 'package:flutter/material.dart'; +import 'package:lasertag_app/constants.dart'; +import 'package:provider/provider.dart'; import '../../models/device_model.dart'; +import '../../providers/device_provider.dart'; import '../widgets/leader_info_dialog.dart'; class LobbyScreen extends StatelessWidget { @@ -61,20 +64,19 @@ class LobbyScreen extends StatelessWidget { ), // Keine Divider mehr hier für einen cleanen Übergang - Expanded( - child: Container( - width: double.infinity, - color: Theme.of(context).scaffoldBackgroundColor, - child: const Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircularProgressIndicator(), - SizedBox(height: 16), - Text("Suche Teilnehmer im Thread-Netzwerk..."), - ], - ), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.radar, + size: 64, + color: colorScheme.outlineVariant, + ), + const SizedBox(height: 16), + const Text("Suche Teilnehmer im Netzwerk..."), + ], ), ), ), @@ -93,9 +95,19 @@ class LobbyScreen extends StatelessWidget { children: [ Expanded( child: OutlinedButton( - onPressed: () => Navigator.pop(context), + onPressed: () async { + final provider = context.read(); + if (provider.activeConfig != null) { + // Leader zurück auf IDLE setzen + final idleConfig = provider.activeConfig!.copyWith( + systemState: LasertagConfig.sysStateIdle, + ); + await provider.provisionDevice(leaderDevice, idleConfig); + } + if (context.mounted) Navigator.pop(context); + }, child: const Text("Abbrechen"), - ), +), ), const SizedBox(width: 16), Expanded( diff --git a/software/app/lib/ui/widgets/leader_info_dialog.dart b/software/app/lib/ui/widgets/leader_info_dialog.dart index dcf964e..844c47f 100644 --- a/software/app/lib/ui/widgets/leader_info_dialog.dart +++ b/software/app/lib/ui/widgets/leader_info_dialog.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../models/device_model.dart'; -import '../../models/device_config_model.dart'; import '../../providers/device_provider.dart'; class LeaderInfoDialog extends StatelessWidget { @@ -10,40 +9,41 @@ class LeaderInfoDialog extends StatelessWidget { @override Widget build(BuildContext context) { - // Wir holen uns die Daten direkt aus dem Cache des Providers - final activeConfig = context.read().activeConfig; + // watch sorgt dafür, dass der Dialog reagiert, wenn sich der Cache füllt + final activeConfig = context.watch().activeConfig; return AlertDialog( - title: const Text("Leader Konfiguration"), + title: const Text("Leader Details"), content: activeConfig == null ? const Text("Keine Konfigurationsdaten im Cache gefunden.") - : SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - _buildInfoRow("Game ID", "0x${activeConfig.gameId.toRadixString(16).toUpperCase()}"), - _buildInfoRow("Status", _stateToString(activeConfig.systemState)), - const Divider(), - const Text("Thread Mesh", style: TextStyle(fontWeight: FontWeight.bold)), - _buildInfoRow("Netzwerk", activeConfig.networkName), - _buildInfoRow("Kanal", activeConfig.channel.toString()), - _buildInfoRow("PAN ID", "0x${activeConfig.panId.toRadixString(16).toUpperCase()}"), - _buildInfoRow("Ext PAN", activeConfig.extPanId), - const Divider(), - const Text("Security Key", style: TextStyle(fontSize: 10, color: Colors.grey)), - SelectableText( - activeConfig.networkKey, - style: const TextStyle(fontFamily: 'monospace', fontSize: 10) + : Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildInfoRow("Game ID", "0x${activeConfig.gameId.toRadixString(16).toUpperCase()}"), + _buildInfoRow("Status", _stateToString(activeConfig.systemState)), + const Divider(), + _buildInfoRow("Netzwerk", activeConfig.networkName), + _buildInfoRow("Kanal", activeConfig.channel.toString()), + _buildInfoRow("PAN ID", "0x${activeConfig.panId.toRadixString(16).toUpperCase()}"), + const SizedBox(height: 16), + + // Neuer Button für Batch-Provisioning + SizedBox( + width: double.infinity, + child: FilledButton.icon( + onPressed: () { + Navigator.pop(context); + context.read().batchProvisionFoundDevices(); + }, + icon: const Icon(Icons.bolt), + label: const Text("Alle Knoten provisionieren"), ), - ], - ), + ), + ], ), actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text("Schließen"), - ), + TextButton(onPressed: () => Navigator.pop(context), child: const Text("Schließen")), ], ); }