pile_nav_new/plugins/quick_blue/windows/quick_blue_plugin.cpp
2024-08-29 17:45:39 +08:00

624 lines
27 KiB
C++

#include "include/quick_blue/quick_blue_plugin.h"
// This must be included before many other Windows headers.
#include <windows.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Devices.Radios.h>
#include <winrt/Windows.Devices.Bluetooth.h>
#include <winrt/Windows.Devices.Bluetooth.Advertisement.h>
#include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
#include <flutter/method_channel.h>
#include <flutter/basic_message_channel.h>
#include <flutter/event_channel.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <flutter/standard_message_codec.h>
#include <map>
#include <memory>
#include <sstream>
#include <algorithm>
#include <iomanip>
#define GUID_FORMAT "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
#define GUID_ARG(guid) guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]
namespace {
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::Storage::Streams;
using namespace winrt::Windows::Devices::Radios;
using namespace winrt::Windows::Devices::Bluetooth;
using namespace winrt::Windows::Devices::Bluetooth::Advertisement;
using namespace winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;
using flutter::EncodableValue;
using flutter::EncodableMap;
using flutter::EncodableList;
union uint16_t_union {
uint16_t uint16;
byte bytes[sizeof(uint16_t)];
};
std::vector<uint8_t> to_bytevc(IBuffer buffer) {
auto reader = DataReader::FromBuffer(buffer);
auto result = std::vector<uint8_t>(reader.UnconsumedBufferLength());
reader.ReadBytes(result);
return result;
}
IBuffer from_bytevc(std::vector<uint8_t> bytes) {
auto writer = DataWriter();
writer.WriteBytes(bytes);
return writer.DetachBuffer();
}
std::string to_hexstring(std::vector<uint8_t> bytes) {
auto ss = std::stringstream();
for (auto b : bytes)
ss << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(b);
return ss.str();
}
std::string to_uuidstr(winrt::guid guid) {
char chars[36 + 1];
sprintf_s(chars, GUID_FORMAT, GUID_ARG(guid));
return std::string{ chars };
}
struct BluetoothDeviceAgent {
BluetoothLEDevice device;
winrt::event_token connnectionStatusChangedToken;
std::map<std::string, GattDeviceService> gattServices;
std::map<std::string, GattCharacteristic> gattCharacteristics;
std::map<std::string, winrt::event_token> valueChangedTokens;
BluetoothDeviceAgent(BluetoothLEDevice device, winrt::event_token connnectionStatusChangedToken)
: device(device),
connnectionStatusChangedToken(connnectionStatusChangedToken) {}
~BluetoothDeviceAgent() {
device = nullptr;
}
IAsyncOperation<GattDeviceService> GetServiceAsync(std::string service) {
if (gattServices.count(service) == 0) {
auto serviceResult = co_await device.GetGattServicesAsync();
if (serviceResult.Status() != GattCommunicationStatus::Success)
co_return nullptr;
for (auto s : serviceResult.Services())
if (to_uuidstr(s.Uuid()) == service)
gattServices.insert(std::make_pair(service, s));
}
co_return gattServices.at(service);
}
IAsyncOperation<GattCharacteristic> GetCharacteristicAsync(std::string service, std::string characteristic) {
if (gattCharacteristics.count(characteristic) == 0) {
auto gattService = co_await GetServiceAsync(service);
auto characteristicResult = co_await gattService.GetCharacteristicsAsync();
if (characteristicResult.Status() != GattCommunicationStatus::Success)
co_return nullptr;
for (auto c : characteristicResult.Characteristics())
if (to_uuidstr(c.Uuid()) == characteristic)
gattCharacteristics.insert(std::make_pair(characteristic, c));
}
co_return gattCharacteristics.at(characteristic);
}
};
class QuickBluePlugin : public flutter::Plugin, public flutter::StreamHandler<EncodableValue> {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
QuickBluePlugin();
virtual ~QuickBluePlugin();
private:
winrt::fire_and_forget InitializeAsync();
// Called when a method is called on this plugin's channel from Dart.
void HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
std::unique_ptr<flutter::StreamHandlerError<>> OnListenInternal(
const EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<>>&& events) override;
std::unique_ptr<flutter::StreamHandlerError<>> OnCancelInternal(
const EncodableValue* arguments) override;
std::unique_ptr<flutter::BasicMessageChannel<EncodableValue>> message_connector_;
std::unique_ptr<flutter::EventSink<EncodableValue>> availability_change_sink_;
std::unique_ptr<flutter::EventSink<EncodableValue>> scan_result_sink_;
Radio bluetoothRadio{ nullptr };
void Radio_StateChanged(Radio sender, IInspectable args);
RadioState oldRadioState = RadioState::Unknown;
BluetoothLEAdvertisementWatcher bluetoothLEWatcher{ nullptr };
winrt::event_token bluetoothLEWatcherReceivedToken;
winrt::fire_and_forget BluetoothLEWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args);
std::map<uint64_t, std::unique_ptr<BluetoothDeviceAgent>> connectedDevices{};
winrt::event_revoker<IRadio> radioStateChangedRevoker;
winrt::fire_and_forget ConnectAsync(uint64_t bluetoothAddress);
void BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, IInspectable args);
void CleanConnection(uint64_t bluetoothAddress);
winrt::fire_and_forget DiscoverServicesAsync(BluetoothDeviceAgent &bluetoothDeviceAgent);
winrt::fire_and_forget SetNotifiableAsync(BluetoothDeviceAgent& bluetoothDeviceAgent, GattCharacteristic& gattCharacteristic, std::string bleInputProperty);
winrt::fire_and_forget RequestMtuAsync(BluetoothDeviceAgent& bluetoothDeviceAgent, uint64_t expectedMtu);
winrt::fire_and_forget ReadValueAsync(GattCharacteristic& gattCharacteristic);
winrt::fire_and_forget WriteValueAsync(GattCharacteristic& gattCharacteristic, std::vector<uint8_t> value, std::string bleOutputProperty);
void QuickBluePlugin::GattCharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args);
};
// static
void QuickBluePlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows *registrar) {
auto method =
std::make_unique<flutter::MethodChannel<EncodableValue>>(
registrar->messenger(), "quick_blue/method",
&flutter::StandardMethodCodec::GetInstance());
auto event_availability_change =
std::make_unique<flutter::EventChannel<EncodableValue>>(
registrar->messenger(), "quick_blue/event.availabilityChange",
&flutter::StandardMethodCodec::GetInstance());
auto event_scan_result =
std::make_unique<flutter::EventChannel<EncodableValue>>(
registrar->messenger(), "quick_blue/event.scanResult",
&flutter::StandardMethodCodec::GetInstance());
auto message_connector_ =
std::make_unique<flutter::BasicMessageChannel<EncodableValue>>(
registrar->messenger(), "quick_blue/message.connector",
&flutter::StandardMessageCodec::GetInstance());
auto plugin = std::make_unique<QuickBluePlugin>();
method->SetMethodCallHandler(
[plugin_pointer = plugin.get()](const auto &call, auto result) {
plugin_pointer->HandleMethodCall(call, std::move(result));
});
auto availability_handler = std::make_unique<
flutter::StreamHandlerFunctions<>>(
[plugin_pointer = plugin.get()](
const EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<>>&& events)
-> std::unique_ptr<flutter::StreamHandlerError<>> {
return plugin_pointer->OnListen(arguments, std::move(events));
},
[plugin_pointer = plugin.get()](const EncodableValue* arguments)
-> std::unique_ptr<flutter::StreamHandlerError<>> {
return plugin_pointer->OnCancel(arguments);
});
auto scan_result_handler = std::make_unique<
flutter::StreamHandlerFunctions<>>(
[plugin_pointer = plugin.get()](
const EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<>>&& events)
-> std::unique_ptr<flutter::StreamHandlerError<>> {
return plugin_pointer->OnListen(arguments, std::move(events));
},
[plugin_pointer = plugin.get()](const EncodableValue* arguments)
-> std::unique_ptr<flutter::StreamHandlerError<>> {
return plugin_pointer->OnCancel(arguments);
});
event_availability_change->SetStreamHandler(std::move(availability_handler));
event_scan_result->SetStreamHandler(std::move(scan_result_handler));
plugin->message_connector_ = std::move(message_connector_);
registrar->AddPlugin(std::move(plugin));
}
QuickBluePlugin::QuickBluePlugin() {
InitializeAsync();
}
QuickBluePlugin::~QuickBluePlugin() {}
winrt::fire_and_forget QuickBluePlugin::InitializeAsync() {
auto bluetoothAdapter = co_await BluetoothAdapter::GetDefaultAsync();
bluetoothRadio = co_await bluetoothAdapter.GetRadioAsync();
if (bluetoothRadio) {
radioStateChangedRevoker = bluetoothRadio.StateChanged(winrt::auto_revoke, { this, &QuickBluePlugin::Radio_StateChanged });
}
}
void QuickBluePlugin::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
auto method_name = method_call.method_name();
OutputDebugString((L"HandleMethodCall " + winrt::to_hstring(method_name) + L"\n").c_str());
if (method_name.compare("isBluetoothAvailable") == 0) {
result->Success(EncodableValue(bluetoothRadio && bluetoothRadio.State() == RadioState::On));
} else if (method_name.compare("startScan") == 0) {
if (bluetoothRadio && bluetoothRadio.State() == RadioState::On) {
if (!bluetoothLEWatcher) {
bluetoothLEWatcher = BluetoothLEAdvertisementWatcher();
bluetoothLEWatcherReceivedToken = bluetoothLEWatcher.Received({ this, &QuickBluePlugin::BluetoothLEWatcher_Received });
}
bluetoothLEWatcher.Start();
result->Success(nullptr);
} else {
result->Error("IllegalState", "Bluetooth unavailable");
}
} else if (method_name.compare("stopScan") == 0) {
if (bluetoothRadio && bluetoothRadio.State() == RadioState::On) {
if (bluetoothLEWatcher) {
bluetoothLEWatcher.Stop();
bluetoothLEWatcher.Received(bluetoothLEWatcherReceivedToken);
}
bluetoothLEWatcher = nullptr;
result->Success(nullptr);
} else {
result->Error("IllegalState", "Bluetooth unavailable");
}
} else if (method_name.compare("connect") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
ConnectAsync(std::stoull(deviceId));
result->Success(nullptr);
} else if (method_name.compare("disconnect") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
CleanConnection(std::stoull(deviceId));
// TODO send `disconnected` message
result->Success(nullptr);
} else if (method_name.compare("discoverServices") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
auto it = connectedDevices.find(std::stoull(deviceId));
if (it == connectedDevices.end()) {
result->Error("IllegalArgument", "Unknown devicesId:" + deviceId);
return;
}
DiscoverServicesAsync(*it->second);
result->Success(nullptr);
} else if (method_name.compare("setNotifiable") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
auto service = std::get<std::string>(args[EncodableValue("service")]);
auto characteristic = std::get<std::string>(args[EncodableValue("characteristic")]);
auto bleInputProperty = std::get<std::string>(args[EncodableValue("bleInputProperty")]);
auto it = connectedDevices.find(std::stoull(deviceId));
if (it == connectedDevices.end()) {
result->Error("IllegalArgument", "Unknown devicesId:" + deviceId);
return;
}
auto bluetoothAgent = *it->second;
auto async_c = bluetoothAgent.GetCharacteristicAsync(service, characteristic);
async_c.Completed([&, result_pointer = result.get()]
(IAsyncOperation<GattCharacteristic> const& sender, AsyncStatus const args) {
// FIXME https://github.com/woodemi/quick.flutter/pull/31#issuecomment-1159213902
auto c = sender.GetResults();
if (c == nullptr) {
result_pointer->Error("IllegalArgument", "Unknown characteristic:" + characteristic);
return;
}
SetNotifiableAsync(bluetoothAgent, c, bleInputProperty);
result_pointer->Success(nullptr);
});
} else if (method_name.compare("readValue") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
auto service = std::get<std::string>(args[EncodableValue("service")]);
auto characteristic = std::get<std::string>(args[EncodableValue("characteristic")]);
auto it = connectedDevices.find(std::stoull(deviceId));
if (it == connectedDevices.end()) {
result->Error("IllegalArgument", "Unknown devicesId:" + deviceId);
return;
}
auto bluetoothAgent = *it->second;
auto async_c = bluetoothAgent.GetCharacteristicAsync(service, characteristic);
async_c.Completed([&, result_pointer = result.get()]
(IAsyncOperation<GattCharacteristic> const& sender, AsyncStatus const args) {
// FIXME https://github.com/woodemi/quick.flutter/pull/31#issuecomment-1159213902
auto c = sender.GetResults();
if (c == nullptr) {
result_pointer->Error("IllegalArgument", "Unknown characteristic:" + characteristic);
return;
}
ReadValueAsync(c);
result_pointer->Success(nullptr);
});
} else if (method_name.compare("writeValue") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
auto service = std::get<std::string>(args[EncodableValue("service")]);
auto characteristic = std::get<std::string>(args[EncodableValue("characteristic")]);
auto value = std::get<std::vector<uint8_t>>(args[EncodableValue("value")]);
auto bleOutputProperty = std::get<std::string>(args[EncodableValue("bleOutputProperty")]);
auto it = connectedDevices.find(std::stoull(deviceId));
if (it == connectedDevices.end()) {
result->Error("IllegalArgument", "Unknown devicesId:" + deviceId);
return;
}
auto bluetoothAgent = *it->second;
auto async_c = bluetoothAgent.GetCharacteristicAsync(service, characteristic);
async_c.Completed([&, result_pointer = result.get()]
(IAsyncOperation<GattCharacteristic> const& sender, AsyncStatus const args) {
// FIXME https://github.com/woodemi/quick.flutter/pull/31#issuecomment-1159213902
auto c = sender.GetResults();
if (c == nullptr) {
result_pointer->Error("IllegalArgument", "Unknown characteristic:" + characteristic);
return;
}
WriteValueAsync(c, value, bleOutputProperty);
result_pointer->Success(nullptr);
});
} else if (method_name.compare("requestMtu") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
auto expectedMtu = std::get<int32_t>(args[EncodableValue("expectedMtu")]);
auto it = connectedDevices.find(std::stoull(deviceId));
if (it == connectedDevices.end()) {
result->Error("IllegalArgument", "Unknown devicesId:" + deviceId);
return;
}
RequestMtuAsync(*it->second, expectedMtu);
result->Success(nullptr);
} else {
result->NotImplemented();
}
}
std::vector<uint8_t> parseManufacturerDataHead(BluetoothLEAdvertisement advertisement)
{
if (advertisement.ManufacturerData().Size() == 0)
return std::vector<uint8_t>();
auto manufacturerData = advertisement.ManufacturerData().GetAt(0);
// FIXME Compat with REG_DWORD_BIG_ENDIAN
uint8_t* prefix = uint16_t_union{ manufacturerData.CompanyId() }.bytes;
auto result = std::vector<uint8_t>{ prefix, prefix + sizeof(uint16_t_union) };
auto data = to_bytevc(manufacturerData.Data());
result.insert(result.end(), data.begin(), data.end());
return result;
}
enum class AvailabilityState : int {
unknown = 0,
resetting = 1,
unsupported = 2,
unauthorized = 3,
poweredOff = 4,
poweredOn = 5,
};
void QuickBluePlugin::Radio_StateChanged(Radio radio, IInspectable args) {
auto radioState = !radio ? RadioState::Disabled : radio.State();
// FIXME https://stackoverflow.com/questions/66099947/bluetooth-radio-statechanged-event-fires-twice/67723902#67723902
if (oldRadioState == radioState) {
return;
}
oldRadioState = radioState;
auto state = [=]() -> AvailabilityState {
if (radioState == RadioState::Unknown) {
return AvailabilityState::unknown;
} else if (radioState == RadioState::Off) {
return AvailabilityState::poweredOff;
} else if (radioState == RadioState::On) {
return AvailabilityState::poweredOn;
} else if (radioState == RadioState::Disabled) {
return AvailabilityState::unsupported;
} else {
return AvailabilityState::unknown;
}
}();
if (availability_change_sink_) {
availability_change_sink_->Success(static_cast<int>(state));
}
}
winrt::fire_and_forget QuickBluePlugin::BluetoothLEWatcher_Received(
BluetoothLEAdvertisementWatcher sender,
BluetoothLEAdvertisementReceivedEventArgs args) {
auto device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(args.BluetoothAddress());
auto name = device ? device.Name() : args.Advertisement().LocalName();
OutputDebugString((L"Received BluetoothAddress:" + winrt::to_hstring(args.BluetoothAddress())
+ L", Name:" + name + L", LocalName:" + args.Advertisement().LocalName() + L"\n").c_str());
if (scan_result_sink_) {
scan_result_sink_->Success(EncodableMap{
{"name", winrt::to_string(name)},
{"deviceId", std::to_string(args.BluetoothAddress())},
{"manufacturerDataHead", parseManufacturerDataHead(args.Advertisement())},
{"rssi", args.RawSignalStrengthInDBm()},
});
}
}
std::unique_ptr<flutter::StreamHandlerError<EncodableValue>> QuickBluePlugin::OnListenInternal(
const EncodableValue* arguments, std::unique_ptr<flutter::EventSink<EncodableValue>>&& events)
{
if (arguments == nullptr) {
return nullptr;
}
auto args = std::get<EncodableMap>(*arguments);
auto name = std::get<std::string>(args[EncodableValue("name")]);
if (name.compare("availabilityChange") == 0) {
availability_change_sink_ = std::move(events);
Radio_StateChanged(bluetoothRadio, nullptr);
} else if (name.compare("scanResult") == 0) {
scan_result_sink_ = std::move(events);
}
return nullptr;
}
std::unique_ptr<flutter::StreamHandlerError<EncodableValue>> QuickBluePlugin::OnCancelInternal(
const EncodableValue* arguments)
{
if (arguments == nullptr) {
return nullptr;
}
auto args = std::get<EncodableMap>(*arguments);
auto name = std::get<std::string>(args[EncodableValue("name")]);
if (name.compare("availabilityChange") == 0) {
availability_change_sink_ = nullptr;
} else if (name.compare("scanResult") == 0) {
scan_result_sink_ = nullptr;
}
return nullptr;
}
winrt::fire_and_forget QuickBluePlugin::ConnectAsync(uint64_t bluetoothAddress) {
auto device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(bluetoothAddress);
auto servicesResult = co_await device.GetGattServicesAsync();
if (servicesResult.Status() != GattCommunicationStatus::Success) {
OutputDebugString((L"GetGattServicesAsync error: " + winrt::to_hstring((int32_t)servicesResult.Status()) + L"\n").c_str());
message_connector_->Send(EncodableMap{
{"deviceId", std::to_string(bluetoothAddress)},
{"ConnectionState", "disconnected"},
});
co_return;
}
auto connnectionStatusChangedToken = device.ConnectionStatusChanged({ this, &QuickBluePlugin::BluetoothLEDevice_ConnectionStatusChanged });
auto deviceAgent = std::make_unique<BluetoothDeviceAgent>(device, connnectionStatusChangedToken);
auto pair = std::make_pair(bluetoothAddress, std::move(deviceAgent));
connectedDevices.insert(std::move(pair));
message_connector_->Send(EncodableMap{
{"deviceId", std::to_string(bluetoothAddress)},
{"ConnectionState", "connected"},
});
}
void QuickBluePlugin::BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, IInspectable args) {
OutputDebugString((L"ConnectionStatusChanged " + winrt::to_hstring((int32_t)sender.ConnectionStatus()) + L"\n").c_str());
if (sender.ConnectionStatus() == BluetoothConnectionStatus::Disconnected) {
CleanConnection(sender.BluetoothAddress());
message_connector_->Send(EncodableMap{
{"deviceId", std::to_string(sender.BluetoothAddress())},
{"ConnectionState", "disconnected"},
});
}
}
void QuickBluePlugin::CleanConnection(uint64_t bluetoothAddress) {
auto node = connectedDevices.extract(bluetoothAddress);
if (!node.empty()) {
auto deviceAgent = std::move(node.mapped());
deviceAgent->device.ConnectionStatusChanged(deviceAgent->connnectionStatusChangedToken);
for (auto& tokenPair : deviceAgent->valueChangedTokens) {
deviceAgent->gattCharacteristics.at(tokenPair.first).ValueChanged(tokenPair.second);
}
}
}
winrt::fire_and_forget QuickBluePlugin::DiscoverServicesAsync(BluetoothDeviceAgent &bluetoothDeviceAgent) {
auto serviceResult = co_await bluetoothDeviceAgent.device.GetGattServicesAsync();
if (serviceResult.Status() != GattCommunicationStatus::Success) {
message_connector_->Send(
EncodableMap{
{"deviceId", std::to_string(bluetoothDeviceAgent.device.BluetoothAddress())},
{"ServiceState", "discovered"}
}
);
co_return;
}
for (auto s : serviceResult.Services()) {
auto characteristicResult = co_await s.GetCharacteristicsAsync();
auto msg = EncodableMap{
{"deviceId", std::to_string(bluetoothDeviceAgent.device.BluetoothAddress())},
{"ServiceState", "discovered"},
{"service", to_uuidstr(s.Uuid())}
};
if (characteristicResult.Status() == GattCommunicationStatus::Success) {
EncodableList characteristics;
for (auto c : characteristicResult.Characteristics()) {
characteristics.push_back(to_uuidstr(c.Uuid()));
}
msg.insert({"characteristics", characteristics});
}
message_connector_->Send(msg);
}
}
winrt::fire_and_forget QuickBluePlugin::RequestMtuAsync(BluetoothDeviceAgent& bluetoothDeviceAgent, uint64_t expectedMtu) {
OutputDebugString(L"RequestMtuAsync expectedMtu");
auto gattSession = co_await GattSession::FromDeviceIdAsync(bluetoothDeviceAgent.device.BluetoothDeviceId());
message_connector_->Send(EncodableMap{
{"mtuConfig", (int64_t)gattSession.MaxPduSize()},
});
}
winrt::fire_and_forget QuickBluePlugin::SetNotifiableAsync(BluetoothDeviceAgent& bluetoothDeviceAgent, GattCharacteristic& gattCharacteristic, std::string bleInputProperty) {
auto descriptorValue = bleInputProperty == "notification" ? GattClientCharacteristicConfigurationDescriptorValue::Notify
: bleInputProperty == "indication" ? GattClientCharacteristicConfigurationDescriptorValue::Indicate
: GattClientCharacteristicConfigurationDescriptorValue::None;
auto writeDescriptorStatus = co_await gattCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(descriptorValue);
if (writeDescriptorStatus != GattCommunicationStatus::Success)
OutputDebugString((L"WriteClientCharacteristicConfigurationDescriptorAsync " + winrt::to_hstring((int32_t)writeDescriptorStatus) + L"\n").c_str());
auto uuid = to_uuidstr(gattCharacteristic.Uuid());
if (bleInputProperty != "disabled") {
bluetoothDeviceAgent.valueChangedTokens[uuid] = gattCharacteristic.ValueChanged({ this, &QuickBluePlugin::GattCharacteristic_ValueChanged });
} else {
gattCharacteristic.ValueChanged(std::exchange(bluetoothDeviceAgent.valueChangedTokens[uuid], {}));
}
}
winrt::fire_and_forget QuickBluePlugin::ReadValueAsync(GattCharacteristic& gattCharacteristic) {
auto readValueResult = co_await gattCharacteristic.ReadValueAsync();
auto uuid = to_uuidstr(gattCharacteristic.Uuid());
auto bytes = to_bytevc(readValueResult.Value());
OutputDebugString((L"ReadValueAsync " + winrt::to_hstring(uuid) + L", " + winrt::to_hstring(to_hexstring(bytes)) + L"\n").c_str());
message_connector_->Send(EncodableMap{
{"deviceId", std::to_string(gattCharacteristic.Service().Device().BluetoothAddress())},
{"characteristicValue", EncodableMap{
{"characteristic", uuid},
{"value", bytes},
}},
});
}
winrt::fire_and_forget QuickBluePlugin::WriteValueAsync(GattCharacteristic& gattCharacteristic, std::vector<uint8_t> value, std::string bleOutputProperty) {
auto writeOption = bleOutputProperty.compare("withoutResponse") == 0 ? GattWriteOption::WriteWithoutResponse : GattWriteOption::WriteWithResponse;
auto writeValueStatus = co_await gattCharacteristic.WriteValueAsync(from_bytevc(value), writeOption);
auto uuid = to_uuidstr(gattCharacteristic.Uuid());
OutputDebugString((L"WriteValueAsync " + winrt::to_hstring(uuid) + L", " + winrt::to_hstring(to_hexstring(value)) + L", " + winrt::to_hstring((int32_t)writeValueStatus) + L"\n").c_str());
}
void QuickBluePlugin::GattCharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args) {
auto uuid = to_uuidstr(sender.Uuid());
auto bytes = to_bytevc(args.CharacteristicValue());
OutputDebugString((L"GattCharacteristic_ValueChanged " + winrt::to_hstring(uuid) + L", " + winrt::to_hstring(to_hexstring(bytes)) + L"\n").c_str());
message_connector_->Send(EncodableMap{
{"deviceId", std::to_string(sender.Service().Device().BluetoothAddress())},
{"characteristicValue", EncodableMap{
{"characteristic", uuid},
{"value", bytes},
}},
});
}
} // namespace
void QuickBluePluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
QuickBluePlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
}