2019-12-06 23:12:34 +01:00
|
|
|
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";
|
|
|
|
|
2019-12-04 18:57:18 +01:00
|
|
|
/// <summary>
|
2019-12-06 23:12:34 +01:00
|
|
|
/// Turns on the Bluetooth adapter.
|
2019-12-04 18:57:18 +01:00
|
|
|
/// </summary>
|
2019-12-06 23:12:34 +01:00
|
|
|
/// <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}");
|
|
|
|
}
|
|
|
|
}
|
2019-12-04 18:57:18 +01:00
|
|
|
}
|