using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Swan;
namespace Unosquare.RaspberryIO.Computer {
///
/// Represents the Bluetooth information.
///
public class Bluetooth : SingletonBase {
private const String BcCommand = "bluetoothctl";
///
/// Turns on the Bluetooth adapter.
///
/// The cancellation token.
///
/// Returns true or false depending if the controller was turned on.
///
/// Failed to power on:.
public async Task PowerOn(CancellationToken cancellationToken = default) {
try {
String output = await ProcessRunner.GetProcessOutputAsync(BcCommand, "power on", null, cancellationToken).ConfigureAwait(false);
return output.Contains("succeeded");
} catch(Exception ex) {
throw new BluetoothErrorException($"Failed to power on: {ex.Message}");
}
}
///
/// Turns off the bluetooth adapter.
///
/// The cancellation token.
///
/// Returns true or false depending if the controller was turned off.
///
/// Failed to power off:.
public async Task PowerOff(CancellationToken cancellationToken = default) {
try {
String output = await ProcessRunner.GetProcessOutputAsync(BcCommand, "power off", null, cancellationToken).ConfigureAwait(false);
return output.Contains("succeeded");
} catch(Exception ex) {
throw new BluetoothErrorException($"Failed to power off: {ex.Message}");
}
}
///
/// Gets the list of detected devices.
///
/// The cancellation token.
///
/// Returns the list of detected devices.
///
/// Failed to retrieve devices:.
public async Task> ListDevices(CancellationToken cancellationToken = default) {
try {
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(3000);
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "scan on", null, cancellationTokenSource.Token).ConfigureAwait(false);
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "scan off", null, cancellationToken).ConfigureAwait(false);
String devices = await ProcessRunner.GetProcessOutputAsync(BcCommand, "devices", null, cancellationToken).ConfigureAwait(false);
return devices.Trim().Split('\n').Select(x => x.Trim());
} catch(Exception ex) {
throw new BluetoothErrorException($"Failed to retrieve devices: {ex.Message}");
}
}
///
/// Gets the list of bluetooth controllers.
///
/// The cancellation token.
///
/// Returns the list of bluetooth controllers.
///
/// Failed to retrieve controllers:.
public async Task> ListControllers(CancellationToken cancellationToken = default) {
try {
String controllers = await ProcessRunner.GetProcessOutputAsync(BcCommand, "list", null, cancellationToken).ConfigureAwait(false);
return controllers.Trim().Split('\n').Select(x => x.Trim());
} catch(Exception ex) {
throw new BluetoothErrorException($"Failed to retrieve controllers: {ex.Message}");
}
}
///
/// Pairs a specific device with a specific controller.
///
/// The mac address of the controller that will be used to pair.
/// The mac address of the device that will be paired.
/// The cancellation token.
///
/// Returns true or false if the pair was successfully.
///
/// Failed to Pair:.
public async Task Pair(String controllerAddress, String deviceAddress, CancellationToken cancellationToken = default) {
try {
// Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken).ConfigureAwait(false);
// Makes the controller visible to other devices.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken).ConfigureAwait(false);
// Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken).ConfigureAwait(false);
// Pairs the device with the controller.
String result = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"pair {deviceAddress}", null, cancellationToken).ConfigureAwait(false);
// Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken).ConfigureAwait(false);
return result.Contains("Paired: yes");
} catch(Exception ex) {
throw new BluetoothErrorException($"Failed to Pair: {ex.Message}");
}
}
///
/// Performs a connection of a given controller with a given device.
///
/// The mac address of the controller that will be used to make the connection.
/// The mac address of the device that will be connected.
/// The cancellation token.
///
/// Returns true or false if the connection was successfully.
///
/// Failed to connect:.
public async Task Connect(String controllerAddress, String deviceAddress, CancellationToken cancellationToken = default) {
try {
// Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken).ConfigureAwait(false);
// Makes the controller visible to other devices.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken).ConfigureAwait(false);
// Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken).ConfigureAwait(false);
// Readies the device for pairing.
String result = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"connect {deviceAddress}", null, cancellationToken).ConfigureAwait(false);
// Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken).ConfigureAwait(false);
return result.Contains("Connected: yes");
} catch(Exception ex) {
throw new BluetoothErrorException($"Failed to connect: {ex.Message}");
}
}
///
/// Sets the device to re-pair automatically when it is turned on, which eliminates the need to pair all over again.
///
/// The mac address of the controller will be used.
/// The mac address of the device will be added to the trust list devices.
/// The cancellation token.
///
/// Returns true or false if the operation was successful.
///
/// Failed to add to trust devices list:.
public async Task Trust(String controllerAddress, String deviceAddress, CancellationToken cancellationToken = default) {
try {
// Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken).ConfigureAwait(false);
// Makes the controller visible to other devices.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken).ConfigureAwait(false);
// Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken).ConfigureAwait(false);
// Sets the device to re-pair automatically when it is turned on, which eliminates the need to pair all over again.
String result = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"trust {deviceAddress}", null, cancellationToken).ConfigureAwait(false);
// Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
_ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken).ConfigureAwait(false);
return result.Contains("Trusted: yes");
} catch(Exception ex) {
throw new BluetoothErrorException($"Failed to add to trust devices list: {ex.Message}");
}
}
///
/// Displays information about a particular device.
///
/// The mac address of the device which info will be retrieved.
/// The cancellation token.
///
/// Returns the device info.
///
/// Failed to retrieve info for {deviceAddress}.
public async Task DeviceInfo(String deviceAddress, CancellationToken cancellationToken = default) {
String info = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"info {deviceAddress}", null, cancellationToken).ConfigureAwait(false);
return !String.IsNullOrEmpty(info) ? info : throw new BluetoothErrorException($"Failed to retrieve info for {deviceAddress}");
}
}
}