Sync
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 27s

This commit is contained in:
2026-01-13 16:46:12 +01:00
parent 55b5421671
commit 63f8f2aaac
6 changed files with 76 additions and 44 deletions

View File

@@ -36,6 +36,23 @@ class DeviceProvider extends ChangeNotifier {
await provisionDevice(device, configToUse);
}
Future<void> 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();

View File

@@ -218,6 +218,7 @@ class _LeaderConfigScreenState extends State<LeaderConfigScreen> {
networkKey: _netKeyCtrl.text.trim(),
);
context.read<DeviceProvider>().setActiveConfig(newConfig);
await context.read<DeviceProvider>().provisionDevice(
widget.device,
newConfig,

View File

@@ -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<DeviceProvider>();
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(

View File

@@ -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<DeviceProvider>().activeConfig;
// watch sorgt dafür, dass der Dialog reagiert, wenn sich der Cache füllt
final activeConfig = context.watch<DeviceProvider>().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<DeviceProvider>().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")),
],
);
}