Bluetooth.Maui 4.0.2

dotnet add package Bluetooth.Maui --version 4.0.2
                    
NuGet\Install-Package Bluetooth.Maui -Version 4.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Bluetooth.Maui" Version="4.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bluetooth.Maui" Version="4.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Bluetooth.Maui" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Bluetooth.Maui --version 4.0.2
                    
#r "nuget: Bluetooth.Maui, 4.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Bluetooth.Maui@4.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Bluetooth.Maui&version=4.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Bluetooth.Maui&version=4.0.2
                    
Install as a Cake Tool

Bluetooth.Core & Bluetooth.Maui

<div style="max-width: 256px; margin-left: auto; margin-right: auto;">

Icon

</div>

CI .NET NuGet NuGet Downloads GitHub Release License

A cross-platform .NET MAUI Bluetooth Low Energy (BLE) library providing a clean, unified API for Android, iOS/MacCatalyst, and Windows platforms.

Features

  • 🔍 BLE Scanning - Discover nearby devices with customizable filtering
  • 🔗 Connection Management - Robust connect/disconnect with auto-reconnect support
  • 📡 GATT Operations - Full support for services, characteristics, and descriptors
  • 📊 Read/Write/Notify - Read values, write data, and subscribe to notifications
  • 🎯 Cross-Platform - Consistent API across all major platforms
  • 🔄 Modern Async - Async/await throughout with cancellation token support
  • 💉 DI-First - Built for MAUI dependency injection
  • 🧩 Options Pattern - Flexible configuration via options objects
  • 📝 Well-Documented - Comprehensive XML docs and examples

Platform Support

Platform Scanning Connection GATT Operations Broadcasting
Android
iOS
MacCatalyst
Windows

Note: Broadcasting (peripheral mode) is currently Android-only. iOS/MacCatalyst/Windows throw NotImplementedException.

Installation

dotnet add package Bluetooth.Maui

Or via NuGet Package Manager:

<PackageReference Include="Bluetooth.Maui" Version="1.0.0" />

Quick Start

1. Register Services in MauiProgram.cs

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();

        builder
            .UseMauiApp<App>()
            .UseMauiCommunityToolkit();

        // Register Bluetooth services
        builder.Services.AddBluetoothServices();

        return builder.Build();
    }
}

2. Inject and Use IBluetoothScanner

public class ScannerViewModel
{
    private readonly IBluetoothScanner _scanner;

    public ScannerViewModel(IBluetoothScanner scanner)
    {
        _scanner = scanner;

        // Subscribe to device discovery
        _scanner.DeviceListChanged += OnDeviceListChanged;
    }

    public async Task StartScanningAsync()
    {
        var options = new ScanningOptions
        {
            // Optional: Configure scanning behavior
        };

        await _scanner.StartScanningAsync(options);
    }

    private void OnDeviceListChanged(object? sender, DeviceListChangedEventArgs e)
    {
        foreach (var device in _scanner.Devices)
        {
            Console.WriteLine($"Found: {device.Name} ({device.Id})");
            Console.WriteLine($"  RSSI: {device.SignalStrengthDbm} dBm");
        }
    }
}

Usage Guide

Scanning for Devices

// Start scanning
await _scanner.StartScanningAsync();

// Access discovered devices
var devices = _scanner.Devices;

// Stop scanning when done
await _scanner.StopScanningAsync();

Connecting to a Device

// Get a device from the scanner
var device = _scanner.Devices.FirstOrDefault(d => d.Name == "MyDevice");

if (device != null)
{
    // Connect with options
    var connectionOptions = new ConnectionOptions
    {
        // Platform-specific connection parameters
    };

    await device.ConnectAsync(connectionOptions);

    // Check connection status
    Console.WriteLine($"Connected: {device.IsConnected}");
}

Service Discovery - Simplified API

The new exploration APIs use a single, flexible method with optional configuration:

// Simple exploration (defaults: services only, caching enabled)
await device.ExploreServicesAsync();

// Explore services AND characteristics
await device.ExploreServicesAsync(ServiceExplorationOptions.WithCharacteristics);

// Full exploration (services + characteristics + descriptors)
await device.ExploreServicesAsync(ServiceExplorationOptions.Full);

// Force re-exploration (ignore cache)
await device.ExploreServicesAsync(new ServiceExplorationOptions
{
    UseCache = false
});

// Filter by service UUID
await device.ExploreServicesAsync(new ServiceExplorationOptions
{
    ServiceUuidFilter = uuid => uuid == myServiceUuid
});

Getting Services and Characteristics

// Get a specific service by UUID
var service = device.GetService(serviceGuid);

// Or use a filter
var service = device.GetService(s => s.Id == serviceGuid);

// Explore characteristics (simple)
await service.ExploreCharacteristicsAsync();

// Explore characteristics AND descriptors
await service.ExploreCharacteristicsAsync(CharacteristicExplorationOptions.Full);

// Get a characteristic
var characteristic = service.GetCharacteristic(characteristicGuid);

Reading and Writing

// Read a characteristic value
var value = await characteristic.ReadValueAsync();
Console.WriteLine($"Value: {BitConverter.ToString(value.ToArray())}");

// Write a value
byte[] data = new byte[] { 0x01, 0x02, 0x03 };
await characteristic.WriteValueAsync(data);

// Check capabilities
if (characteristic.CanRead)
{
    // Safe to read
}

if (characteristic.CanWrite)
{
    // Safe to write
}

Subscribing to Notifications

// Subscribe to value changes
characteristic.ValueUpdated += (sender, args) =>
{
    Console.WriteLine($"New value: {BitConverter.ToString(args.NewValue.ToArray())}");
    Console.WriteLine($"Old value: {BitConverter.ToString(args.OldValue.ToArray())}");
};

// Start listening
await characteristic.StartListeningAsync();

// Check listening state
Console.WriteLine($"Listening: {characteristic.IsListening}");

// Stop listening when done
await characteristic.StopListeningAsync();

Working with Descriptors

// Explore descriptors
await characteristic.ExploreDescriptorsAsync();

// Get a specific descriptor
var descriptor = characteristic.GetDescriptor(descriptorGuid);

// Read descriptor value
var value = await descriptor.ReadValueAsync();

// Write descriptor value
await descriptor.WriteValueAsync(new byte[] { 0x01, 0x00 });

Advanced Features

Connection Priority (Android)

Optimize connection parameters for your use case:

// High priority: Fast data transfer, low latency (11.25-15ms)
await device.RequestConnectionPriorityAsync(BluetoothConnectionPriority.High);

// Balanced: Moderate performance and power (30-50ms)
await device.RequestConnectionPriorityAsync(BluetoothConnectionPriority.Balanced);

// Low power: Battery optimization, higher latency (100-125ms)
await device.RequestConnectionPriorityAsync(BluetoothConnectionPriority.LowPower);

Note: iOS, macOS, and Windows manage connection parameters automatically. This API is a no-op on those platforms.

Timeout and Cancellation

All async operations support timeouts and cancellation:

using var cts = new CancellationTokenSource();

try
{
    await device.ConnectAsync(
        connectionOptions: new ConnectionOptions(),
        timeout: TimeSpan.FromSeconds(10),
        cancellationToken: cts.Token
    );
}
catch (TimeoutException)
{
    Console.WriteLine("Connection timed out");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was cancelled");
}

Event-Driven Architecture

Subscribe to events for reactive programming:

// Scanner events
_scanner.RunningStateChanged += (s, e) => Console.WriteLine($"Scanning: {_scanner.IsRunning}");
_scanner.DeviceListChanged += OnDeviceListChanged;

// Device events
device.ConnectionStateChanged += (s, e) => Console.WriteLine($"State: {device.ConnectionState}");
device.Connected += (s, e) => Console.WriteLine("Connected");
device.Disconnected += (s, e) => Console.WriteLine("Disconnected");
device.UnexpectedDisconnection += (s, e) => Console.WriteLine($"Lost connection: {e.Exception}");

// Service events
service.CharacteristicListChanged += OnCharacteristicsChanged;

// Characteristic events
characteristic.ValueUpdated += OnValueUpdated;

Caching and Performance

The exploration APIs use intelligent caching by default:

// First call: Queries the device
await device.ExploreServicesAsync();  // UseCache = true (default)

// Subsequent calls: Returns cached results instantly
await device.ExploreServicesAsync();  // Cached, no device query

// Force refresh: Ignore cache
await device.ExploreServicesAsync(new ServiceExplorationOptions
{
    UseCache = false  // Forces device query
});

Cleanup and Disposal

Proper cleanup ensures resources are released:

// Clear services (stops notifications, clears cache)
await device.ClearServicesAsync();

// Clear specific service characteristics
await service.ClearCharacteristicsAsync();

// Clear characteristic descriptors
await characteristic.ClearDescriptorsAsync();

// Disconnect and dispose device
await device.DisconnectAsync();
await device.DisposeAsync();  // Implements IAsyncDisposable

Platform-Specific Setup

Android

Add Bluetooth permissions to AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                 android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

iOS / MacCatalyst

Add Bluetooth usage description to Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs Bluetooth to scan for BLE devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app needs Bluetooth to scan for BLE devices</string>

Windows

Add Bluetooth capability to Package.appxmanifest:

<Capabilities>
  <DeviceCapability Name="bluetooth" />
</Capabilities>

Architecture

Plugin.Bluetooth/
├── Bluetooth.Abstractions/              # Core interfaces (platform-agnostic)
│   ├── Exceptions/
│   ├── Extensions/
│   └── Options/
├── Bluetooth.Abstractions.Scanning/     # Scanning-specific interfaces
│   ├── Events/
│   ├── Exceptions/
│   └── Options/
├── Bluetooth.Abstractions.Broadcasting/ # Broadcasting interfaces
│   ├── Events/
│   ├── Exceptions/
│   └── Options/
├── Bluetooth.Core/                      # Base implementations
│   ├── Base classes
│   └── Infrastructure
├── Bluetooth.Core.Scanning/             # Scanning base implementations
├── Bluetooth.Core.Broadcasting/         # Broadcasting base implementations
├── Bluetooth.Maui/                      # MAUI integration & DI
└── Platform Implementations/
    ├── Bluetooth.Maui.Platforms.Apple/     # iOS & MacCatalyst
    ├── Bluetooth.Maui.Platforms.Droid/     # Android
    ├── Bluetooth.Maui.Platforms.Windows/   # Windows
    └── Bluetooth.Maui.Platforms.DotNetCore # Fallback (throws NotImplementedException)

Core Interfaces

Scanning

  • IBluetoothScanner - Device discovery and scanning control
  • IBluetoothRemoteDevice - Remote device representation and connection
  • IBluetoothRemoteService - GATT service on a remote device
  • IBluetoothRemoteCharacteristic - GATT characteristic with read/write/notify
  • IBluetoothRemoteDescriptor - GATT descriptor

Broadcasting (Android only)

  • IBluetoothBroadcaster - Peripheral/advertising mode
  • IBluetoothLocalService - Local GATT service
  • IBluetoothLocalCharacteristic - Local characteristic for broadcasting
  • IBluetoothConnectedDevice - Connected central device

Exception Handling

Comprehensive exception hierarchy for error handling:

try
{
    await device.ConnectAsync(connectionOptions);
}
catch (DeviceNotConnectedException ex)
{
    // Device is not connected when operation requires it
}
catch (ServiceNotFoundException ex)
{
    // Requested service not found on device
}
catch (CharacteristicNotFoundException ex)
{
    // Requested characteristic not found in service
}
catch (TimeoutException ex)
{
    // Operation timed out
}
catch (OperationCanceledException ex)
{
    // Operation was cancelled
}
catch (BluetoothException ex)
{
    // Base exception for all Bluetooth errors
}

API Design Principles

  1. Async First - All I/O operations are async with cancellation support
  2. Options Pattern - Flexible configuration via options objects
  3. Caching - Intelligent caching enabled by default for performance
  4. Events - Event-driven architecture for reactive patterns
  5. IAsyncDisposable - Proper resource cleanup with async disposal
  6. Immutability - ReadOnlyMemory<byte> for value types
  7. Platform Parity - Consistent API across all platforms

Contributing

Contributions are welcome! Please:

  1. Ensure all public APIs have XML documentation
  2. Follow the existing code style and patterns
  3. Add unit tests for new features
  4. Update the README for API changes

Requirements

  • .NET 10.0 or higher
  • .NET MAUI application
  • Platform-specific Bluetooth permissions (see setup section)

License

MIT License - Copyright (c) 2025 Laerdal Medical

See LICENSE.md for details.

Support

For issues, feature requests, or questions:

Changelog

Recent Changes

v1.0.0 (Current)

  • ✅ Windows platform implementation complete
  • ✅ Simplified exploration APIs (single method with options)
  • ✅ Modern DI registration with AddBluetoothServices()
  • ✅ Comprehensive XML documentation
  • ✅ Options pattern for all configuration

Built with ❤️ by Laerdal Medical

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-android36.1 is compatible.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-ios26.0 is compatible.  net10.0-maccatalyst was computed.  net10.0-maccatalyst26.0 is compatible.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed.  net10.0-windows10.0.22621 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.2 94 2/26/2026
4.0.1 85 2/25/2026
4.0.0 89 2/24/2026
1.0.3 142 1/7/2026
1.0.2 105 1/5/2026
1.0.1 106 1/5/2026
1.0.0 100 1/5/2026