Generate code using Shiny.BluetoothLE.Hosting, a BLE peripheral hosting library for .NET with GATT server, advertising, and managed characteristic patterns
You are an expert in Shiny.BluetoothLE.Hosting, a .NET library for turning a mobile device into a BLE peripheral. It provides a GATT server, BLE advertising, iBeacon broadcasting, and a managed characteristic pattern using dependency injection and attribute-based registration.
Invoke this skill when the user wants to:
BleGattCharacteristic pattern with DI registrationShiny.BluetoothLE.HostingShiny.BluetoothLE.Hosting, Shiny.BluetoothLE.Hosting.ManagedShiny.Core, Shiny.BluetoothLE.CommonThe library has two usage patterns:
IBleHostingManager, call AddService with a builder lambda to configure characteristics inlineBleGattCharacteristic subclasses decorated with [BleGattCharacteristic], register them via AddBleHostedCharacteristic<T>(), and attach/detach them at runtimedotnet add package Shiny.BluetoothLE.Hosting
Imperative (no managed characteristics):
builder.Services.AddBluetoothLeHosting();
Managed pattern (preferred for structured services):
builder.Services.AddBleHostedCharacteristic<MyReadCharacteristic>();
builder.Services.AddBleHostedCharacteristic<MyWriteCharacteristic>();
// AddBluetoothLeHosting() is called automatically by AddBleHostedCharacteristic
When generating code for Shiny.BluetoothLE.Hosting projects, follow these conventions:
Always request access before advertising or adding services:
var access = await hostingManager.RequestAccess();
if (access != AccessState.Available)
{
// Handle denied/disabled/not supported
return;
}
Use the builder pattern to add services and characteristics inline:
var service = await hostingManager.AddService("12345678-1234-1234-1234-123456789abc", true, sb =>
{
sb.AddCharacteristic("12345678-1234-1234-1234-123456789ab1", cb =>
{
cb.SetRead(request =>
{
var data = System.Text.Encoding.UTF8.GetBytes("Hello");
return Task.FromResult(GattResult.Success(data));
});
cb.SetWrite(request =>
{
var received = request.Data;
if (request.IsReplyNeeded)
request.Respond(GattState.Success);
return Task.CompletedTask;
}, WriteOptions.Write);
cb.SetNotification(sub =>
{
// sub.IsSubscribing tells you if subscribing or unsubscribing
// sub.Peripheral is the central device
return Task.CompletedTask;
}, NotificationOptions.Notify);
});
});
Create a class that extends BleGattCharacteristic and decorate it with [BleGattCharacteristic]:
[BleGattCharacteristic("12345678-1234-1234-1234-123456789abc", "12345678-1234-1234-1234-123456789ab1")]
public class MyCharacteristic : BleGattCharacteristic
{
public override Task OnStart()
{
// Called when the service is attached
return Task.CompletedTask;
}
public override void OnStop()
{
// Called when the service is detached
}
public override Task<GattResult> OnRead(ReadRequest request)
{
var data = System.Text.Encoding.UTF8.GetBytes("Hello");
return Task.FromResult(GattResult.Success(data));
}
public override Task OnWrite(WriteRequest request)
{
var received = request.Data;
if (request.IsReplyNeeded)
request.Respond(GattState.Success);
return Task.CompletedTask;
}
public override Task OnSubscriptionChanged(IPeripheral peripheral, bool subscribed)
{
// React to central subscribing/unsubscribing
return Task.CompletedTask;
}
}
Register in DI:
builder.Services.AddBleHostedCharacteristic<MyCharacteristic>();
Attach at runtime:
await hostingManager.AttachRegisteredServices();
await hostingManager.StartAdvertising(new AdvertisementOptions("MyDevice", "12345678-1234-1234-1234-123456789abc"));
// Advertise with local name and service UUIDs
await hostingManager.StartAdvertising(new AdvertisementOptions(
LocalName: "MyDevice",
ServiceUuids: "12345678-1234-1234-1234-123456789abc"
));
// Advertise with defaults (no name, no service UUIDs)
await hostingManager.StartAdvertising();
// Stop advertising
hostingManager.StopAdvertising();
await hostingManager.AdvertiseBeacon(
uuid: Guid.Parse("12345678-1234-1234-1234-123456789abc"),
major: 1,
minor: 100,
txpower: -59
);
// From an IGattCharacteristic reference
var data = System.Text.Encoding.UTF8.GetBytes("Updated value");
// Notify all subscribed centrals
await characteristic.Notify(data);
// Notify specific centrals
await characteristic.Notify(data, specificPeripheral1, specificPeripheral2);
In the managed pattern, override Request instead of OnWrite to receive a write, process it, and automatically respond via notification:
[BleGattCharacteristic("service-uuid", "char-uuid")]
public class MyRequestCharacteristic : BleGattCharacteristic
{
public override Task<GattResult> Request(WriteRequest request)
{
// Process incoming data
var received = System.Text.Encoding.UTF8.GetString(request.Data);
// Return result -- this is sent back as a notification to the requesting central
var response = System.Text.Encoding.UTF8.GetBytes($"Echo: {received}");
return Task.FromResult(GattResult.Success(response));
}
}
Note: Request and OnWrite cannot both be overridden on the same characteristic.
When WriteRequest.IsReplyNeeded is true, you must call Respond:
cb.SetWrite(request =>
{
try
{
// Process data
if (request.IsReplyNeeded)
request.Respond(GattState.Success);
}
catch
{
if (request.IsReplyNeeded)
request.Respond(GattState.Failure);
}
return Task.CompletedTask;
}, WriteOptions.Write);
BleHosting/{Name}Characteristic.csFeatures/{Feature}/{Name}Characteristic.csIPeripheral: Both Shiny.BluetoothLE (client) and Shiny.BluetoothLE.Hosting define an IPeripheral interface with different members. If both packages are referenced in the same project, do NOT add both namespaces as global usings. Use file-level using directives or FQN (Shiny.BluetoothLE.Hosting.IPeripheral) to disambiguate.RequestAccess() and check the result before any hosting operationsBleGattCharacteristic subclasses are easier to test and maintainxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) or short 16-bit UUIDs for standard Bluetooth SIG servicesWriteRequest.IsReplyNeeded and call Respond with the appropriate GattStateGattResult.Error(GattState.Failure) in read handlers when an error occursStopAdvertising() and ClearServices() when doneBleGattCharacteristic instances to the GATT serverOnStop on each characteristicStartAdvertising if already advertisingFor detailed API signatures and examples, see:
reference/api-reference.md - Full API surface, interfaces, enums, records, and usage examples