using System.Collections.ObjectModel; using System.Diagnostics; using System.Windows.Input; using Microsoft.Maui.Controls; using RobobinApp.Views; using Plugin.BLE; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Abstractions.Exceptions; using Plugin.BLE.Abstractions.EventArgs; using System.Threading.Tasks; using Plugin.BLE.Abstractions; using RobobinApp.Models; using System.Net.NetworkInformation; using System.Collections.Generic; using Windows.Networking.Connectivity; using Windows.Devices.WiFi; namespace RobobinApp.ViewModels { public class ConnectionPageViewModel : BaseViewModel { private BluetoothDevice _selectedDevice; private IDevice _connectedDevice; public ICommand GoHomeCommand { get; } public ObservableCollection<BluetoothDevice> BluetoothDevices { get; } public ObservableCollection<WifiNetwork> WifiNetworks { get; } public ICommand ConnectCommand { get; } public ICommand SendWifiInfoCommand { get; } public ICommand DisconnectCommand { get; } private bool _isConnected; private string ssid; private string password; public bool IsBluetoothDeviceSelectionVisible => !IsWifiNetworkSelectionVisible; public bool IsConnected { get => _isConnected; set { _isConnected = value; OnPropertyChanged(); OnPropertyChanged(nameof(ConnectButtonText)); // Update button text when connection state changes } } public string SSID { get => ssid; set { ssid = value; OnPropertyChanged(nameof(SSID)); } } public string Password { get => password; set { password = value; OnPropertyChanged(nameof(Password)); } } private readonly IAdapter _adapter; private readonly IBluetoothLE _bluetoothLE; public string ConnectButtonText => IsConnected ? "Disconnect" : "Connect"; private bool _isWifiNetworkSelectionVisible; private WifiNetwork _selectedWifiNetwork; public bool IsWifiNetworkSelectionVisible { get => _isWifiNetworkSelectionVisible; set { _isWifiNetworkSelectionVisible = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsBluetoothDeviceSelectionVisible)); } } public ConnectionPageViewModel() { BluetoothDevices = new ObservableCollection<BluetoothDevice>(); WifiNetworks = new ObservableCollection<WifiNetwork>(); ConnectCommand = new Command(OnConnect); DisconnectCommand = new Command(OnDisconnect); SendWifiInfoCommand = new Command(OnSendWifiInfo); GoHomeCommand = new Command(async () => await OnGoHome()); ConnectCommand = new Command(OnToggleConnection); _bluetoothLE = CrossBluetoothLE.Current; _adapter = _bluetoothLE.Adapter; _adapter.DeviceDiscovered += OnDeviceDiscovered; IsWifiNetworkSelectionVisible = false; Debug.WriteLine("Checking and requesting Bluetooth permissions."); CheckAndRequestBluetoothPermissions(); ScanForWifiNetworks(); } private async void ScanForWifiNetworks() { WifiNetworks.Clear(); Debug.WriteLine("Scanning for Wi-Fi networks..."); var wifiAdapter = await GetWifiAdapterAsync(); if (wifiAdapter == null) { Debug.WriteLine("Wi-Fi adapter not found."); return; } await wifiAdapter.ScanAsync(); Debug.Write("Scanning for networks"); var networks = wifiAdapter.NetworkReport.AvailableNetworks; var addedSsids = new HashSet<string>(); // To keep track of added SSIDs foreach (var network in networks) { Debug.Write("This network exists: " + network.Ssid); if (addedSsids.Add(network.Ssid)) { var wifiNetwork = new WifiNetwork { SSID = network.Ssid, SignalStrength = network.NetworkRssiInDecibelMilliwatts }; Device.BeginInvokeOnMainThread(() => WifiNetworks.Add(wifiNetwork)); Debug.WriteLine($"Found Wi-Fi network: {wifiNetwork.SSID}, Signal Strength: {wifiNetwork.SignalStrength} dBm"); } } } private async Task<WiFiAdapter> GetWifiAdapterAsync() { var result = await WiFiAdapter.FindAllAdaptersAsync(); return result.FirstOrDefault(); // Get the first available Wi-Fi adapter } private async void OnSendWifiInfo(object obj) { if (SelectedWifiNetwork != null) { Debug.WriteLine("Wifi been selected"); } if (!_isConnected || _connectedDevice == null) { Debug.WriteLine("Cannot send WiFi information: No device is connected."); await Application.Current.MainPage.DisplayAlert("Error", "No device is connected.", "OK"); return; } try { Debug.WriteLine($"Sending WiFi information to {_connectedDevice.Name}..."); var services = await _connectedDevice.GetServicesAsync(); var uartService = services.FirstOrDefault(s => s.Id == Guid.Parse("6e400001-b5a3-f393-e0a9-e50e24dcca9e")); if (uartService == null) { Debug.WriteLine("UART service not found."); await Application.Current.MainPage.DisplayAlert("Error", "UART service not found on the device.", "OK"); return; } var characteristics = await uartService.GetCharacteristicsAsync(); var rxCharacteristic = characteristics.FirstOrDefault(c => c.Id == Guid.Parse("6e400002-b5a3-f393-e0a9-e50e24dcca9e")); if (rxCharacteristic == null) { Debug.WriteLine("RX characteristic not found."); await Application.Current.MainPage.DisplayAlert("Error", "RX characteristic not found on the device.", "OK"); return; } string wifiInfo = $"{SSID},{Password}"; byte[] data = System.Text.Encoding.UTF8.GetBytes(wifiInfo); await rxCharacteristic.WriteAsync(data); Debug.WriteLine("WiFi information sent successfully."); await Application.Current.MainPage.DisplayAlert("Success", "WiFi information sent successfully.", "OK"); } catch (Exception ex) { Debug.WriteLine($"Error sending WiFi information: {ex.Message}"); await Application.Current.MainPage.DisplayAlert("Error", $"Failed to send WiFi information: {ex.Message}", "OK"); } } public BluetoothDevice SelectedDevice { get => _selectedDevice; set { _selectedDevice = value; OnPropertyChanged(); } } public WifiNetwork SelectedWifiNetwork { get => _selectedWifiNetwork; set { _selectedWifiNetwork = value; OnPropertyChanged(); } } private async void OnToggleConnection() { if (IsConnected) { OnDisconnect(); } else { OnConnect(); } } private async void CheckAndRequestBluetoothPermissions() { Debug.WriteLine("Checking Bluetooth permissions."); var status = await Permissions.CheckStatusAsync<Permissions.Bluetooth>(); if (status != PermissionStatus.Granted) { Debug.WriteLine("Bluetooth permission not granted, requesting permission."); status = await Permissions.RequestAsync<Permissions.Bluetooth>(); } if (status == PermissionStatus.Granted) { Debug.WriteLine("Bluetooth permission granted, proceeding to scan devices."); ScanDevices(); } else { Debug.WriteLine("Bluetooth permission denied."); await Application.Current.MainPage.DisplayAlert("Permissions", "Bluetooth scan permission is required to discover devices.", "OK"); } } public async Task OnGoHome() { Debug.WriteLine("Navigating to home page."); await Application.Current.MainPage.Navigation.PushAsync(new MainPage()); } private async void ScanDevices() { BluetoothDevices.Clear(); Debug.WriteLine("Cleared existing Bluetooth devices. Starting scan..."); try { if (!_bluetoothLE.IsAvailable) { Debug.WriteLine("Bluetooth is not available."); await Application.Current.MainPage.DisplayAlert("Bluetooth", "Bluetooth is not available.", "OK"); return; } if (!_bluetoothLE.IsOn) { Debug.WriteLine("Bluetooth is turned off."); await Application.Current.MainPage.DisplayAlert("Bluetooth", "Please turn on Bluetooth.", "OK"); return; } Debug.WriteLine("Starting scanning for devices..."); await _adapter.StartScanningForDevicesAsync(); } catch (Exception ex) { Debug.WriteLine($"Error during scanning: {ex.Message}"); await Application.Current.MainPage.DisplayAlert("Error", $"Failed to scan for devices: {ex.Message}", "OK"); } } private void OnDeviceDiscovered(object sender, DeviceEventArgs e) { Debug.WriteLine($"Discovered device: {e.Device.Name ?? e.Device.Id.ToString()}"); var bluetoothDevice = new BluetoothDevice( string.IsNullOrWhiteSpace(e.Device.Name) ? e.Device.Id.ToString() : e.Device.Name, e.Device.Id.ToString() ); if (!BluetoothDevices.Any(d => d.MacAddress == bluetoothDevice.MacAddress)) { Device.BeginInvokeOnMainThread(() => BluetoothDevices.Add(bluetoothDevice)); } } private async Task<IDevice> TryConnectAsync(Guid deviceId, int retryCount = 3) { IDevice device = null; for (int attempt = 1; attempt <= retryCount; attempt++) { try { Debug.WriteLine($"Attempt {attempt}: Connecting to device {deviceId}"); var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); device = await _adapter.ConnectToKnownDeviceAsync(deviceId, new ConnectParameters(false, true), cancellationTokenSource.Token); return device; } catch (Exception ex) { Debug.WriteLine($"Attempt {attempt} failed: {ex.Message}"); if (attempt == retryCount) { throw; } } } return device; } private async void OnConnect() { if (SelectedDevice == null) { Debug.WriteLine("No device selected for connection."); return; } Debug.WriteLine($"Attempting to connect to {SelectedDevice.Name}, MAC Address {SelectedDevice.MacAddress}"); await Application.Current.MainPage.DisplayAlert("Connection", $"Connecting to {SelectedDevice.Name}...", "OK"); try { await _adapter.StopScanningForDevicesAsync(); Debug.WriteLine("Stopped scanning for devices."); IsConnected = true; // Connect to the selected device _connectedDevice = await TryConnectAsync(Guid.Parse(SelectedDevice.MacAddress)); Debug.WriteLine($"Successfully connected to {SelectedDevice.Name}."); // Try to get services var services = await _connectedDevice.GetServicesAsync(); if (services != null && services.Count > 0) { foreach (var service in services) { Debug.WriteLine($"Service: ID={service.Id}, Name={service.Name}"); } } else { Debug.WriteLine("No services found."); } } catch (DeviceConnectionException ex) { Debug.WriteLine($"Connection error: {ex.Message}"); await Application.Current.MainPage.DisplayAlert("Connection Error", $"Failed to connect: {ex.Message}", "OK"); } catch (TaskCanceledException ex) { Debug.WriteLine($"TaskCanceledException: {ex.Message}"); await Application.Current.MainPage.DisplayAlert("Connection Error", "Connection timed out.", "OK"); _connectedDevice = null; IsConnected = false; } catch (Exception ex) { Debug.WriteLine($"General connection error: {ex.Message}"); await Application.Current.MainPage.DisplayAlert("Connection Error", $"An error occurred: {ex.Message}", "OK"); } } private async void OnDisconnect() { if (_connectedDevice != null) { try { Debug.WriteLine($"Attempting to disconnect from {_connectedDevice.Name}"); await _adapter.DisconnectDeviceAsync(_connectedDevice); Debug.WriteLine("Disconnected successfully."); IsWifiNetworkSelectionVisible = false; // Dispose of the device reference if (_connectedDevice != null) _connectedDevice.Dispose(); _connectedDevice = null; IsConnected = false; await Application.Current.MainPage.DisplayAlert("Disconnected", "Device disconnected successfully.", "OK"); } catch (Exception ex) { Debug.WriteLine($"Error during disconnection: {ex.Message}"); await Application.Current.MainPage.DisplayAlert("Disconnection Error", $"Failed to disconnect: {ex.Message}", "OK"); } } else { Debug.WriteLine("No device to disconnect."); } } } }