RaspberryIO_26/Unosquare.RaspberryIO/Computer/Bluetooth.cs
2019-12-06 23:12:34 +01:00

202 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Swan;
namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// Represents the Bluetooth information.
/// </summary>
public class Bluetooth : SingletonBase<Bluetooth> {
private const String BcCommand = "bluetoothctl";
/// <summary>
/// Turns on the Bluetooth adapter.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// Returns true or false depending if the controller was turned on.
/// </returns>
/// <exception cref="BluetoothErrorException">Failed to power on:.</exception>
public async Task<Boolean> 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}");
}
}
/// <summary>
/// Turns off the bluetooth adapter.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// Returns true or false depending if the controller was turned off.
/// </returns>
/// <exception cref="BluetoothErrorException">Failed to power off:.</exception>
public async Task<Boolean> 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}");
}
}
/// <summary>
/// Gets the list of detected devices.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// Returns the list of detected devices.
/// </returns>
/// <exception cref="BluetoothErrorException">Failed to retrieve devices:.</exception>
public async Task<IEnumerable<String>> 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}");
}
}
/// <summary>
/// Gets the list of bluetooth controllers.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// Returns the list of bluetooth controllers.
/// </returns>
/// <exception cref="BluetoothErrorException">Failed to retrieve controllers:.</exception>
public async Task<IEnumerable<String>> 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}");
}
}
/// <summary>
/// Pairs a specific device with a specific controller.
/// </summary>
/// <param name="controllerAddress">The mac address of the controller that will be used to pair.</param>
/// <param name="deviceAddress">The mac address of the device that will be paired.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// Returns true or false if the pair was successfully.
/// </returns>
/// <exception cref="BluetoothErrorException">Failed to Pair:.</exception>
public async Task<Boolean> 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}");
}
}
/// <summary>
/// Performs a connection of a given controller with a given device.
/// </summary>
/// <param name="controllerAddress">The mac address of the controller that will be used to make the connection.</param>
/// <param name="deviceAddress">The mac address of the device that will be connected.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// Returns true or false if the connection was successfully.
/// </returns>
/// <exception cref="BluetoothErrorException">Failed to connect:.</exception>
public async Task<Boolean> 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}");
}
}
/// <summary>
/// Sets the device to re-pair automatically when it is turned on, which eliminates the need to pair all over again.
/// </summary>
/// <param name="controllerAddress">The mac address of the controller will be used.</param>
/// <param name="deviceAddress">The mac address of the device will be added to the trust list devices.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// Returns true or false if the operation was successful.
/// </returns>
/// <exception cref="BluetoothErrorException">Failed to add to trust devices list:.</exception>
public async Task<Boolean> 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}");
}
}
/// <summary>
/// Displays information about a particular device.
/// </summary>
/// <param name="deviceAddress">The mac address of the device which info will be retrieved.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// Returns the device info.
/// </returns>
/// <exception cref="BluetoothErrorException">Failed to retrieve info for {deviceAddress}.</exception>
public async Task<String> 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}");
}
}
}