diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index f3a63ee..f5ff68a 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -54,7 +54,13 @@ NSAllowsLocalNetworking + NSLocalNetworkUsageDescription - This app uses the local network for debugging and development. + Allow the app to find and connect to the Dart VM for debugging purposes. + + NSBonjourServices + + _dartvm._tcp + diff --git a/lib/BlindMasterResources/secure_transmissions.dart b/lib/BlindMasterResources/secure_transmissions.dart index d8596a1..8c5b27d 100644 --- a/lib/BlindMasterResources/secure_transmissions.dart +++ b/lib/BlindMasterResources/secure_transmissions.dart @@ -8,7 +8,7 @@ import 'package:socket_io_client/socket_io_client.dart' as IO; String local = Platform.isAndroid ? '10.0.2.2' : 'localhost'; String fromDevice = '192.168.1.190'; -String host = local; +String host = fromDevice; int port = 3000; String socketString = "$scheme://$host:$port"; String scheme = 'http'; diff --git a/lib/BlindMasterScreens/addingDevices/add_device.dart b/lib/BlindMasterScreens/addingDevices/add_device.dart index 6ed0efe..0a8b673 100644 --- a/lib/BlindMasterScreens/addingDevices/add_device.dart +++ b/lib/BlindMasterScreens/addingDevices/add_device.dart @@ -123,7 +123,8 @@ class _AddDeviceState extends State { } Widget _buildScanResultTiles() { - final res = _scanResults.where((r) => r.advertisementData.advName == "BlindMaster Device" && r.rssi > -55); + // final res = _scanResults.where((r) => r.advertisementData.advName == "BlindMaster Device" && r.rssi > -55); + final res = _scanResults.where((r) => r.advertisementData.advName == "BlindMaster-C6"); return (res.isNotEmpty) ? ListView( children: [ diff --git a/lib/BlindMasterScreens/addingDevices/device_setup.dart b/lib/BlindMasterScreens/addingDevices/device_setup.dart index 151ef09..a2a128b 100644 --- a/lib/BlindMasterScreens/addingDevices/device_setup.dart +++ b/lib/BlindMasterScreens/addingDevices/device_setup.dart @@ -44,6 +44,7 @@ class DeviceSetup extends StatefulWidget { } class _DeviceSetupState extends State { + bool refreshing = false; List _services = []; List> networks = []; @@ -82,7 +83,8 @@ class _DeviceSetupState extends State { try { final val = utf8.decode(rawData); - networks = json.decode(val) as List>; + final decoded = json.decode(val) as List; + networks = decoded.map((e) => e as Map).toList(); } catch (e) { if(!mounted)return; ScaffoldMessenger.of(context).showSnackBar(errorSnackbar(e)); @@ -118,6 +120,12 @@ class _DeviceSetupState extends State { ], ); }); + try { + await ssidRefreshChar.write(utf8.encode("Done"), withoutResponse: ssidRefreshChar.properties.writeWithoutResponse); + } catch (e) { + throw Exception ("Handshake Termination Error. Restart setup process."); + } + refreshing = false; } } catch (e) { if(!mounted)return; @@ -242,7 +250,9 @@ class _DeviceSetupState extends State { ), TextButton( onPressed: () { - Navigator.pop(dialogContext, passControl.text); + Navigator.pop(dialogContext, (ent ? + {"uname": unameControl.text, "password": passControl.text} + : (open ? {} : {"password": passControl.text}))); passControl.clear(); unameControl.clear(); }, @@ -286,7 +296,8 @@ class _DeviceSetupState extends State { } final connectConfirmChar = _services[0].characteristics.lastWhere((c) => c.uuid.str == "0005"); - final tokenEntryChar = _services[0].characteristics.lastWhere((c) => c.uuid.str == "0003"); + final tokenEntryChar = _services[0].characteristics.lastWhere((c) => c.uuid.str == "0002"); + final authConfirmChar = _services[0].characteristics.lastWhere((c) => c.uuid.str == "0003"); await connectConfirmChar.setNotifyValue(true); _confirmSub = connectConfirmChar.onValueReceived.listen((List connectVal) { try { @@ -297,7 +308,7 @@ class _DeviceSetupState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => SetDeviceName(tokenEntryChar: tokenEntryChar, device: widget.device), + builder: (context) => SetDeviceName(tokenEntryChar: tokenEntryChar, authConfirmChar: authConfirmChar, device: widget.device), ) ).then((_) { if (widget.device.isConnected) { @@ -318,6 +329,8 @@ class _DeviceSetupState extends State { } Future refreshWifiList() async{ + if (refreshing) return; + refreshing = true; final ssidRefreshChar = _services[0].characteristics.lastWhere((c) => c.uuid.str == "0004"); setState(() { wifiList = null; diff --git a/lib/BlindMasterScreens/addingDevices/set_device_name.dart b/lib/BlindMasterScreens/addingDevices/set_device_name.dart index 4272e92..f1dead1 100644 --- a/lib/BlindMasterScreens/addingDevices/set_device_name.dart +++ b/lib/BlindMasterScreens/addingDevices/set_device_name.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:blind_master/BlindMasterResources/error_snackbar.dart'; @@ -10,8 +11,9 @@ import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:google_fonts/google_fonts.dart'; class SetDeviceName extends StatefulWidget { - const SetDeviceName({super.key, required this.tokenEntryChar, required this.device}); + const SetDeviceName({super.key, required this.tokenEntryChar, required this.authConfirmChar, required this.device}); final BluetoothCharacteristic tokenEntryChar; + final BluetoothCharacteristic authConfirmChar; final BluetoothDevice device; @override @@ -21,6 +23,7 @@ class SetDeviceName extends StatefulWidget { class _SetDeviceNameState extends State { final deviceNameController = TextEditingController(); Widget? screen; + StreamSubscription>? _authSub; @override void initState() { @@ -30,6 +33,7 @@ class _SetDeviceNameState extends State { @override void dispose() { + _authSub?.cancel(); deviceNameController.dispose(); super.dispose(); } @@ -75,6 +79,48 @@ class _SetDeviceNameState extends State { return; } + // Set up authentication confirmation listener + try { + await widget.authConfirmChar.setNotifyValue(true); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(errorSnackbar(Exception("Failed to set up authentication listener"))); + return; + } + + _authSub = widget.authConfirmChar.onValueReceived.listen((List authVal) async { + try { + final authResponse = utf8.decode(authVal); + if (authResponse == "Authenticated") { + if (!mounted) return; + _authSub?.cancel(); + + await widget.device.disconnectAndUpdateStream().catchError((e) { + if(!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(errorSnackbar(e)); + }); + + if (!mounted) return; + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (context) => HomeScreen()), + (route) => false, + ); + } else if (authResponse == "Error") { + _authSub?.cancel(); + throw Exception("Authentication failed. Please try again."); + } + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(errorSnackbar(e)); + setState(() { + initScreen(); + }); + return; + } + }); + + // Write the token try { try { await widget.tokenEntryChar.write(utf8.encode(token), withoutResponse: widget.tokenEntryChar.properties.writeWithoutResponse); @@ -84,20 +130,12 @@ class _SetDeviceNameState extends State { } catch (e){ if(!mounted)return; ScaffoldMessenger.of(context).showSnackBar(errorSnackbar(e)); + _authSub?.cancel(); + setState(() { + initScreen(); + }); return; } - - await widget.device.disconnectAndUpdateStream().catchError((e) { - if(!mounted) return; - ScaffoldMessenger.of(context).showSnackBar(errorSnackbar(e)); - }); - - if (!mounted) return; - Navigator.pushAndRemoveUntil( - context, - MaterialPageRoute(builder: (context) => HomeScreen()), - (route) => false, - ); } Future onPressed() async { @@ -120,7 +158,17 @@ class _SetDeviceNameState extends State { body: SizedBox( height: MediaQuery.of(context).size.height * 0.6, child: Center( - child: screen ?? CircularProgressIndicator(color: Theme.of(context).primaryColorLight), + child: screen ?? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(color: Theme.of(context).primaryColorLight), + SizedBox(height: 10), + Text( + "Authenticating device...", + textAlign: TextAlign.center, + ), + ], + ), ) ) );