mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-28 02:31:25 +01:00
Bluetooth: adds option showType
This commit is contained in:
@@ -10,6 +10,7 @@ Changes:
|
||||
|
||||
Features:
|
||||
* Added Bluetooth Low Energy detection on Windows and macOS, through WinRT and Core Bluetooth. (Bluetooth, Windows / macOS)
|
||||
* Added the `showType` option to the `Bluetooth` module, which selects which stacks are looked for. (Bluetooth)
|
||||
* Added `{device-type}`, `{signal-quality}` and `{signal-quality-bar}` to the `Bluetooth` module, and `deviceType` and `signalQuality` to its JSON output. (Bluetooth)
|
||||
* `deviceType` names the stacks a device answers on, `Classic` and/or `Low Energy`, and `signalQuality` is the signal strength of the LE link, converted from dBm on the same scale the `Wifi` module uses.
|
||||
* Linux fills both from the BlueZ `Class`, `Appearance` and `RSSI` properties, and macOS from Core Bluetooth. The remaining platforms report `Classic` and no signal quality, which is what their backends can tell.
|
||||
|
||||
@@ -1749,6 +1749,24 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"devType": {
|
||||
"description": "Which Bluetooth stacks to look for",
|
||||
"oneOf": [
|
||||
{
|
||||
"const": "classic",
|
||||
"description": "Look for BR/EDR devices only"
|
||||
},
|
||||
{
|
||||
"const": "le",
|
||||
"description": "Look for Bluetooth Low Energy devices only"
|
||||
},
|
||||
{
|
||||
"const": "both",
|
||||
"description": "Look for devices on either stack"
|
||||
}
|
||||
],
|
||||
"default": "both"
|
||||
},
|
||||
"percent": {
|
||||
"$ref": "#/$defs/percent"
|
||||
},
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
#include "fastfetch.h"
|
||||
#include "modules/bluetooth/option.h"
|
||||
|
||||
// Which radio a device was seen over. A dual-mode device answers on both, and a platform that only
|
||||
// walks one stack can only report the bit it knows about, so this is a bitfield rather than a value.
|
||||
typedef enum FFBluetoothDeviceType: uint8_t {
|
||||
FF_BLUETOOTH_DEVICE_TYPE_NONE = 0,
|
||||
FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT = 1 << 0, // BR/EDR, the BTHENUM enumerator
|
||||
FF_BLUETOOTH_DEVICE_TYPE_LE_BIT = 1 << 1, // Low Energy, the BTHLE enumerator
|
||||
} FFBluetoothDeviceType;
|
||||
|
||||
typedef struct FFBluetoothResult {
|
||||
FFstrbuf name;
|
||||
FFstrbuf address;
|
||||
|
||||
@@ -100,6 +100,11 @@
|
||||
// link is up, and the callbacks are waited for as a group, so a machine with no LE link pays
|
||||
// nothing.
|
||||
//
|
||||
// * `showType` is honoured here rather than in the module, because the two stacks are two separate
|
||||
// walks with two separate permission surfaces: a stack the user switched off is not asked about
|
||||
// at all. The LE pass keeps working without the classic one, it only loses the address join and
|
||||
// falls back to matching by name.
|
||||
//
|
||||
// Note on the permission model, which is what the first bullet above cannot show: any process that
|
||||
// touches Bluetooth is subject to the `kTCCServiceBluetoothAlways` service, and the request is
|
||||
// attributed to the *responsible* process. A host application that does not declare
|
||||
@@ -148,7 +153,7 @@
|
||||
// The assigned 16-bit UUIDs, plus the 0xFEE0-0xFEFF block, which is where the vendor-specific ones
|
||||
// live. `retrieveConnectedPeripheralsWithServices:` filters on this list, so anything exposing only
|
||||
// a 128-bit custom service is out of reach -- that is a limit of the API, not of this list.
|
||||
static NSArray<CBUUID*>* ffBluetoothLeServiceUuids(void) {
|
||||
static NSArray<CBUUID*>* leServiceUuids(void) {
|
||||
static NSArray<CBUUID*>* uuids;
|
||||
static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{
|
||||
@@ -166,7 +171,7 @@ static NSArray<CBUUID*>* ffBluetoothLeServiceUuids(void) {
|
||||
|
||||
// See the note on `CBPeripheral.isConnectedToSystem` at the top of the file. The fallback keeps the
|
||||
// module working on a system that does not answer the private property.
|
||||
static bool ffBluetoothLeIsConnected(CBPeripheral* peripheral) {
|
||||
static bool leIsConnected(CBPeripheral* peripheral) {
|
||||
if ([peripheral respondsToSelector: @selector(isConnectedToSystem)]) {
|
||||
return peripheral.isConnectedToSystem;
|
||||
}
|
||||
@@ -175,7 +180,7 @@ static bool ffBluetoothLeIsConnected(CBPeripheral* peripheral) {
|
||||
|
||||
// The address is what a device is looked up by, both while the classic list is walked and by the LE
|
||||
// pass, and it is the only field the two copies of a dual-mode device are guaranteed to agree on.
|
||||
static FFBluetoothResult* ffBluetoothFindByAddress(FFlist* devices, const char* address) {
|
||||
static FFBluetoothResult* findByAddress(FFlist* devices, const char* address) {
|
||||
FF_LIST_FOR_EACH (FFBluetoothResult, device, *devices) {
|
||||
if (ffStrbufEqualS(&device->address, address)) {
|
||||
return device;
|
||||
@@ -187,7 +192,7 @@ static FFBluetoothResult* ffBluetoothFindByAddress(FFlist* devices, const char*
|
||||
// The class of device, as the classic stack reports it. It is built into a scratch buffer rather than
|
||||
// straight into the entry because the entry may already exist: see the merge in `ffDetectBluetooth`,
|
||||
// where the second copy of a dual-mode device only fills what the first copy left empty.
|
||||
static void ffBluetoothSetClassOfDevice(FFstrbuf* type, IOBluetoothDevice* ioDevice) {
|
||||
static void setClassOfDevice(FFstrbuf* type, IOBluetoothDevice* ioDevice) {
|
||||
if(ioDevice.serviceClassMajor & kBluetoothServiceClassMajorLimitedDiscoverableMode)
|
||||
ffStrbufAppendS(type, "Limited Discoverable Mode, ");
|
||||
if(ioDevice.serviceClassMajor & kBluetoothServiceClassMajorReserved1)
|
||||
@@ -263,7 +268,7 @@ static void ffBluetoothSetClassOfDevice(FFstrbuf* type, IOBluetoothDevice* ioDev
|
||||
// The fallback for a system whose `IOBluetoothDevice` does not answer `identifier`. A name that is
|
||||
// carried by more than one classic entry cannot identify anything, so an ambiguous name joins
|
||||
// nothing and the peripheral is reported as its own device instead of being merged.
|
||||
static FFBluetoothResult* ffBluetoothFindByName(FFlist* devices, const char* name) {
|
||||
static FFBluetoothResult* findByName(FFlist* devices, const char* name) {
|
||||
FFBluetoothResult* found = nullptr;
|
||||
FF_LIST_FOR_EACH (FFBluetoothResult, device, *devices) {
|
||||
if (!ffStrEqualsIgnCase(device->name.chars, name)) {
|
||||
@@ -277,7 +282,7 @@ static FFBluetoothResult* ffBluetoothFindByName(FFlist* devices, const char* nam
|
||||
return found;
|
||||
}
|
||||
|
||||
static const char* ffBluetoothDetectLe(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */, NSDictionary<NSString*, NSString*>* classicAddressByIdentifier) {
|
||||
static const char* detectLe(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */, NSDictionary<NSString*, NSString*>* classicAddressByIdentifier) {
|
||||
CBManagerAuthorization authorization = CBCentralManager.authorization;
|
||||
if (authorization == CBManagerAuthorizationDenied || authorization == CBManagerAuthorizationRestricted) {
|
||||
return nullptr; // The user said no. `NotDetermined` is left to the manager below, which asks.
|
||||
@@ -297,13 +302,13 @@ static const char* ffBluetoothDetectLe(FFBluetoothOptions* options, FFlist* devi
|
||||
return nullptr; // Powered off, unauthorized or no radio: the classic list stands on its own
|
||||
}
|
||||
|
||||
NSArray<CBPeripheral*>* peripherals = [delegate.central retrieveConnectedPeripheralsWithServices: ffBluetoothLeServiceUuids()];
|
||||
NSArray<CBPeripheral*>* peripherals = [delegate.central retrieveConnectedPeripheralsWithServices: leServiceUuids()];
|
||||
if (peripherals.count == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (CBPeripheral* peripheral in peripherals) {
|
||||
if (ffBluetoothLeIsConnected(peripheral)) {
|
||||
if (leIsConnected(peripheral)) {
|
||||
peripheral.delegate = delegate;
|
||||
++delegate.signalStrengthAsked;
|
||||
[peripheral readRSSI];
|
||||
@@ -319,7 +324,7 @@ static const char* ffBluetoothDetectLe(FFBluetoothOptions* options, FFlist* devi
|
||||
|
||||
for (CBPeripheral* peripheral in peripherals) {
|
||||
const char* name = peripheral.name.length > 0 ? peripheral.name.UTF8String : nullptr;
|
||||
bool connected = ffBluetoothLeIsConnected(peripheral);
|
||||
bool connected = leIsConnected(peripheral);
|
||||
|
||||
// The classic half wins wherever it has an answer: its name, address and type come from the
|
||||
// class of device rather than from a name the two stacks may spell differently. The
|
||||
@@ -327,10 +332,10 @@ static const char* ffBluetoothDetectLe(FFBluetoothOptions* options, FFlist* devi
|
||||
FFBluetoothResult* target = nullptr;
|
||||
NSString* address = classicAddressByIdentifier[peripheral.identifier.UUIDString];
|
||||
if (address) {
|
||||
target = ffBluetoothFindByAddress(devices, address.UTF8String);
|
||||
target = findByAddress(devices, address.UTF8String);
|
||||
}
|
||||
if (!target && name) {
|
||||
target = ffBluetoothFindByName(devices, name);
|
||||
target = findByName(devices, name);
|
||||
}
|
||||
|
||||
if (!target && !connected && !options->showDisconnected) {
|
||||
@@ -370,16 +375,15 @@ static const char* ffBluetoothDetectLe(FFBluetoothOptions* options, FFlist* devi
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */)
|
||||
// The classic (BR/EDR) half. `classicAddressByIdentifier` is an output rather than an input: the
|
||||
// classic list is the only place a device address can be had from, and the LE pass needs those
|
||||
// addresses to recognise a peripheral that has already been listed.
|
||||
static const char* detectClassic(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */, NSMutableDictionary<NSString*, NSString*>* classicAddressByIdentifier)
|
||||
{
|
||||
NSArray<IOBluetoothDevice*>* ioDevices = IOBluetoothDevice.pairedDevices;
|
||||
if(!ioDevices)
|
||||
return "IOBluetoothDevice.pairedDevices failed";
|
||||
|
||||
// The identifier the LE pass matches on, mapped to the address rather than to a position in
|
||||
// `devices`: the list is the caller's, and a position in it is not this module's to promise.
|
||||
NSMutableDictionary<NSString*, NSString*>* classicAddressByIdentifier = [NSMutableDictionary dictionary];
|
||||
|
||||
for(IOBluetoothDevice* ioDevice in ioDevices)
|
||||
{
|
||||
if (!options->showDisconnected && !ioDevice.isConnected)
|
||||
@@ -404,7 +408,7 @@ const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FF
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY type = ffStrbufCreate();
|
||||
ffBluetoothSetClassOfDevice(&type, ioDevice);
|
||||
setClassOfDevice(&type, ioDevice);
|
||||
|
||||
uint8_t battery = 0;
|
||||
if (ioDevice.batteryPercentSingle)
|
||||
@@ -415,7 +419,7 @@ const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FF
|
||||
battery = ioDevice.batteryPercentCase;
|
||||
|
||||
// A device with no address cannot be recognised as a repeat, so it is always its own entry.
|
||||
FFBluetoothResult* device = address.length > 0 ? ffBluetoothFindByAddress(devices, address.chars) : nullptr;
|
||||
FFBluetoothResult* device = address.length > 0 ? findByAddress(devices, address.chars) : nullptr;
|
||||
if (!device)
|
||||
{
|
||||
device = FF_LIST_ADD(FFBluetoothResult, *devices);
|
||||
@@ -442,9 +446,31 @@ const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FF
|
||||
device->connected |= !!ioDevice.isConnected;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */)
|
||||
{
|
||||
// `showType` selects which stacks are walked, not merely which entries are printed: a stack the
|
||||
// user switched off is not asked about at all. The classic pass is the one that produces the
|
||||
// identifier map the LE pass joins on, so it is nil when that pass does not run -- the LE pass
|
||||
// then has nothing to match against and reports every peripheral as a device of its own.
|
||||
NSMutableDictionary<NSString*, NSString*>* classicAddressByIdentifier = nil;
|
||||
|
||||
if (options->showType & FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT) {
|
||||
classicAddressByIdentifier = [NSMutableDictionary dictionary];
|
||||
|
||||
const char* error = detectClassic(options, devices, classicAddressByIdentifier);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
// Last, so that a peripheral answering on both stacks is matched against the classic list and
|
||||
// ends up as one entry rather than two.
|
||||
ffBluetoothDetectLe(options, devices, classicAddressByIdentifier);
|
||||
if (options->showType & FF_BLUETOOTH_DEVICE_TYPE_LE_BIT) {
|
||||
detectLe(options, devices, classicAddressByIdentifier);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,13 @@ static int enumDev([[maybe_unused]] int sockfd, struct bt_devinfo const* dev, FF
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* ffDetectBluetooth([[maybe_unused]] FFBluetoothOptions* options, [[maybe_unused]] FFlist* devices /* FFBluetoothResult */) {
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
|
||||
// Netgraph carries the BR/EDR stack and nothing else, so there is no second function to dispatch
|
||||
// to here: a Low Energy-only configuration simply has nothing to walk.
|
||||
if (!(options->showType & FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// struct hostent* ent = bt_gethostent();
|
||||
if (bt_devenum((void*) enumDev, devices) < 0) {
|
||||
return "bt_devenum() failed";
|
||||
|
||||
@@ -5,7 +5,13 @@ extern "C" {
|
||||
|
||||
#include <bluetooth/LocalDevice.h>
|
||||
|
||||
const char* ffDetectBluetooth([[maybe_unused]] FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
|
||||
// The local adapter's device class is a BR/EDR concept and the adapter is the only device this
|
||||
// backend has, so a Low Energy-only configuration leaves the list empty.
|
||||
if (!(options->showType & FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
using namespace Bluetooth;
|
||||
FF_SUPPRESS_IO();
|
||||
|
||||
|
||||
@@ -196,11 +196,20 @@ static void detectBluetoothRoot(FFBluetoothOptions* options, FFlist* devices, FF
|
||||
FFBluetoothResult* device = detectBluetoothObject(devices, dbus, &arrayIter);
|
||||
|
||||
if (device) {
|
||||
if (!options->showDisconnected && !device->connected) {
|
||||
// `showType` selects which stacks are reported. Unlike the other backends there is no
|
||||
// per-stack call to skip here: BlueZ publishes the class of device and the GATT appearance
|
||||
// as two properties of the same `org.bluez.Device1` object, so both stacks arrive on this
|
||||
// one walk and the filter lands on the result instead. A device whose stack could not be
|
||||
// determined is kept -- BlueZ does not promise that a Device1 object carries either
|
||||
// property, and dropping those would lose devices the default configuration reports.
|
||||
bool deviceTypeWanted = device->deviceType == FF_BLUETOOTH_DEVICE_TYPE_NONE || (device->deviceType & options->showType);
|
||||
|
||||
if (!deviceTypeWanted || (!options->showDisconnected && !device->connected)) {
|
||||
ffStrbufDestroy(&device->name);
|
||||
ffStrbufDestroy(&device->address);
|
||||
ffStrbufDestroy(&device->type);
|
||||
--devices->length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (device->name.length == 0) {
|
||||
|
||||
@@ -40,7 +40,7 @@ const char* ffBluetoothDetectLe(FFBluetoothOptions* options, FFlist* devices /*
|
||||
// The device tree spells an address without separators, while the list holds the printed form. The
|
||||
// case has to be folded because it depends on the enumerator: the BTHENUM nodes are upper case and
|
||||
// the BTHLE / BTHLEDevice ones lower case, and the list is always upper case.
|
||||
static bool ffBluetoothAddressEquals(const wchar_t* plain, const FFstrbuf* address) {
|
||||
static bool addressEquals(const wchar_t* plain, const FFstrbuf* address) {
|
||||
if (address->length != 17) {
|
||||
return false;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ static bool ffBluetoothAddressEquals(const wchar_t* plain, const FFstrbuf* addre
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* ffBluetoothDetectBattery(FFlist* devices) {
|
||||
static const char* detectBattery(FFlist* devices) {
|
||||
ULONG idListLength = 0;
|
||||
// The class filter is a no-op here, and deliberately so: CM_Get_Device_ID_ListW() only honours the
|
||||
// filter string together with CM_GETIDLIST_FILTER_CLASS, so this walks every present node of the
|
||||
@@ -107,7 +107,7 @@ static const char* ffBluetoothDetectBattery(FFlist* devices) {
|
||||
}
|
||||
|
||||
FF_LIST_FOR_EACH (FFBluetoothResult, bt, *devices) {
|
||||
if (ffBluetoothAddressEquals(deviceAddress, &bt->address)) {
|
||||
if (addressEquals(deviceAddress, &bt->address)) {
|
||||
bt->battery = battery;
|
||||
break;
|
||||
}
|
||||
@@ -117,7 +117,10 @@ static const char* ffBluetoothDetectBattery(FFlist* devices) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
|
||||
// The classic (BR/EDR) half. `BluetoothFindFirstDevice` only ever answers with BR/EDR devices; the
|
||||
// Low Energy stack is a separate walk, in bluetooth_windows.cpp, which folds what it finds into the
|
||||
// list this function builds.
|
||||
static const char* detectClassic(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bluetoothapis.dll", 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstDevice)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextDevice)
|
||||
@@ -239,10 +242,28 @@ const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FF
|
||||
ffBluetoothFindDeviceClose(hFind);
|
||||
}
|
||||
|
||||
ffBluetoothDetectLe(options, devices);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
|
||||
// `showType` selects which stacks are walked, not merely which entries are printed. The classic
|
||||
// and the Low Energy stack are two different APIs here, so a stack the user switched off is not
|
||||
// called at all.
|
||||
if (options->showType & FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT) {
|
||||
const char* error = detectClassic(options, devices);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (options->showType & FF_BLUETOOTH_DEVICE_TYPE_LE_BIT) {
|
||||
// A failure here is not fatal: the LE half only adds to the classic list, which stands on
|
||||
// its own, and "no LE link on this machine" is the ordinary case rather than an error.
|
||||
ffBluetoothDetectLe(options, devices);
|
||||
}
|
||||
|
||||
if (devices->length > 0) {
|
||||
ffBluetoothDetectBattery(devices);
|
||||
detectBattery(devices);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "common/percent.h"
|
||||
#include "common/printing.h"
|
||||
#include "common/jsonconfig.h"
|
||||
#include "common/strutil.h"
|
||||
#include "detection/bluetooth/bluetooth.h"
|
||||
#include "modules/bluetooth/bluetooth.h"
|
||||
|
||||
@@ -120,6 +119,22 @@ void ffParseBluetoothJsonObject(FFBluetoothOptions* options, yyjson_val* module)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unsafe_yyjson_equals_str(key, "showType")) {
|
||||
int value;
|
||||
const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
|
||||
{ "classic", FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT },
|
||||
{ "le", FF_BLUETOOTH_DEVICE_TYPE_LE_BIT },
|
||||
{ "both", FF_BLUETOOTH_DEVICE_TYPE_BOTH },
|
||||
{},
|
||||
});
|
||||
if (error) {
|
||||
ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bluetooth), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
|
||||
} else {
|
||||
options->showType = (FFBluetoothDeviceType) value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ffPercentParseJsonObject(key, val, &options->percent)) {
|
||||
continue;
|
||||
}
|
||||
@@ -133,6 +148,21 @@ void ffGenerateBluetoothJsonConfig(FFBluetoothOptions* options, yyjson_mut_doc*
|
||||
|
||||
yyjson_mut_obj_add_bool(doc, module, "showDisconnected", options->showDisconnected);
|
||||
|
||||
switch (options->showType) {
|
||||
case FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT:
|
||||
yyjson_mut_obj_add_str(doc, module, "showType", "classic");
|
||||
break;
|
||||
case FF_BLUETOOTH_DEVICE_TYPE_LE_BIT:
|
||||
yyjson_mut_obj_add_str(doc, module, "showType", "le");
|
||||
break;
|
||||
case FF_BLUETOOTH_DEVICE_TYPE_BOTH:
|
||||
yyjson_mut_obj_add_str(doc, module, "showType", "both");
|
||||
break;
|
||||
case FF_BLUETOOTH_DEVICE_TYPE_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ffPercentGenerateJsonConfig(doc, module, options->percent);
|
||||
}
|
||||
|
||||
@@ -181,6 +211,8 @@ bool ffGenerateBluetoothJsonResult(FFBluetoothOptions* options, yyjson_mut_doc*
|
||||
void ffInitBluetoothOptions(FFBluetoothOptions* options) {
|
||||
ffOptionInitModuleArg(&options->moduleArgs, "");
|
||||
options->showDisconnected = false;
|
||||
// Both stacks are walked by default; `showType` exists to switch one off, not to switch one on.
|
||||
options->showType = FF_BLUETOOTH_DEVICE_TYPE_BOTH;
|
||||
options->percent = (FFPercentageModuleConfig) { 50, 20, 0 };
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,22 @@
|
||||
#include "common/option.h"
|
||||
#include "common/percent.h"
|
||||
|
||||
// Which radio a device answers on. A dual-mode device answers on both, and a backend that walks only
|
||||
// one stack can only report the bit it knows about, so this is a bitfield rather than a value. It
|
||||
// carries both meanings the module needs: which stacks a result was seen on, and which stacks
|
||||
// `options->showType` asks the detection to walk at all.
|
||||
typedef enum FFBluetoothDeviceType: uint8_t {
|
||||
FF_BLUETOOTH_DEVICE_TYPE_NONE = 0,
|
||||
FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT = 1 << 0, // BR/EDR, the BTHENUM enumerator
|
||||
FF_BLUETOOTH_DEVICE_TYPE_LE_BIT = 1 << 1, // Low Energy, the BTHLE enumerator
|
||||
FF_BLUETOOTH_DEVICE_TYPE_BOTH = FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT | FF_BLUETOOTH_DEVICE_TYPE_LE_BIT,
|
||||
} FFBluetoothDeviceType;
|
||||
|
||||
typedef struct FFBluetoothOptions {
|
||||
FFModuleArgs moduleArgs;
|
||||
|
||||
bool showDisconnected;
|
||||
FFBluetoothDeviceType showType;
|
||||
FFPercentageModuleConfig percent;
|
||||
} FFBluetoothOptions;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user