Shiny.BluetoothLE.Hubs.Client
1.0.0-alpha-0006-g9e9412ee84
Prefix Reserved
dotnet add package Shiny.BluetoothLE.Hubs.Client --version 1.0.0-alpha-0006-g9e9412ee84
NuGet\Install-Package Shiny.BluetoothLE.Hubs.Client -Version 1.0.0-alpha-0006-g9e9412ee84
<PackageReference Include="Shiny.BluetoothLE.Hubs.Client" Version="1.0.0-alpha-0006-g9e9412ee84" />
<PackageVersion Include="Shiny.BluetoothLE.Hubs.Client" Version="1.0.0-alpha-0006-g9e9412ee84" />
<PackageReference Include="Shiny.BluetoothLE.Hubs.Client" />
paket add Shiny.BluetoothLE.Hubs.Client --version 1.0.0-alpha-0006-g9e9412ee84
#r "nuget: Shiny.BluetoothLE.Hubs.Client, 1.0.0-alpha-0006-g9e9412ee84"
#:package Shiny.BluetoothLE.Hubs.Client@1.0.0-alpha-0006-g9e9412ee84
#addin nuget:?package=Shiny.BluetoothLE.Hubs.Client&version=1.0.0-alpha-0006-g9e9412ee84&prerelease
#tool nuget:?package=Shiny.BluetoothLE.Hubs.Client&version=1.0.0-alpha-0006-g9e9412ee84&prerelease
Shiny.BluetoothLE.Hubs
Shiny.BluetoothLE.Hubs gives you SignalR-style hubs over Bluetooth LE. One device hosts a hub, and nearby devices discover it, connect, and call it through a source-generated, strongly typed proxy. The host pushes events back to everyone, to some clients, or to groups. Files move over L2CAP, a faster direct channel between the devices.
It's built on Shiny.BluetoothLE and Shiny.BluetoothLE.Hosting, and it is AOT- and trim-safe with no reflection.
// shared contract
[BleHubClient]
public interface IGameHub
{
Task<MoveResult> MakeMove(int cell); // client -> host
IAsyncEnumerable<int> Countdown(int from, CancellationToken ct);
event Action<GameState> StateChanged; // host -> clients
}
// host
public class GameHub(GameEngine engine) : BleHub<IGameHub>
{
public async Task<MoveResult> MakeMove(int cell)
{
// Context.Client, Context.Abort(), Groups, OnConnectedAsync/OnDisconnectedAsync
await Clients.All.StateChanged(engine.Snapshot()); // generated typed push
return new MoveResult(true, null);
}
...
}
// client - GameHubClient is generated
client.Hub.StateChanged += state => ...;
await client.Connect(host, new BleHubConnectOptions("Allan"));
var result = await client.Hub.MakeMove(4);
await client.UploadFile(path, "avatar.jpg"); // L2CAP
Packages
| Project | Purpose |
|---|---|
Shiny.BluetoothLE.Hubs |
Wire protocol (framing, chunking, reassembly), argument codec, serializer, [BleHubClient]. Ships the source generator |
Shiny.BluetoothLE.Hubs.Host |
BleHub<T>, IBleHubHost, IHubContext<THub>, groups, L2CAP file server |
Shiny.BluetoothLE.Hubs.Client |
BleHubClient (base of the generated proxies), discovery, shared connections, file upload and download |
Setup
Json.AddContext(MyJsonContext.Default); // hub arguments/results are AOT-safe JSON by default
// host
services.AddBluetoothLeHosting();
services.AddBleHub<GameHub>(ServiceUuid, CharacteristicUuid, o => o.MaxClients = 6);
services.ConfigureBleHubHost(o => o.EnableFileTransfers(Path.Combine(FileSystem.AppDataDirectory, "files")));
await serviceProvider.GetRequiredService<IBleHubHost>().Start();
// client
services.AddBluetoothLE();
services.AddBleHubClient<IGameHub>(ServiceUuid, CharacteristicUuid); // inject IBleHubClient<IGameHub>, GameHubClient or IGameHub
Contract: hub methods return
Task,Task<T>orIAsyncEnumerable<T>. A trailingCancellationTokenis passed through to the host. Events areAction/Action<T1..T4>. Compile errors are reported asSBH001–SBH006.Hubs: a new hub instance runs, in its own DI scope, for every call, like SignalR. The hub may take the contract's
CancellationTokenor leave it out.Outside a hub: inject
IHubContext<GameHub>and callcontext.Clients.All.StateChanged(state).Clientsis a generated C# 14 extension property.Start/stop:
IBleHubHost.Start()/Stop()start or stop every hub.IHubContext<THub>.Start()/Stop(reason)control one hub. A stopped hub disconnects its clients and refuses new ones.- A GATT service shared by several hubs stays up while any of them is running.
- The advertisement follows the running hubs.
Disconnect is cooperative, because iOS peripherals can't drop a central.
Context.Abort()andIHubContext.Disconnect()ask the client to leave. The client library does so and raisesDisconnectedwith the reason.Multiple hubs: each hub needs its own characteristic. Sharing one service UUID is recommended so the advertisement holds only one 128-bit UUID. Hub clients on the same device share one BLE connection.
Over Wi-Fi too: Shiny.SwitchboardR serves the same hubs over Wi-Fi (with mDNS discovery) alongside BLE, and lets clients connect over whichever transport is available, with no change to hub or contract code. It builds on hidden transport seams in this library (
IHubContext<THub>.TransportEndpoint,BleHubClient.ConnectExternal).
See PLAN.md for the wire protocol, the design decisions and the roadmap.
Sample: Tic Tac Toe
samples/TicTacToe is a .NET MAUI app for iOS and Android that uses Shiny.Maui.Shell.
- One phone taps Host a game and plays X.
- The next phone to tap Join a game plays O. Later phones join the
spectatorsgroup. - Moves are hub calls, and board updates and emotes are hub pushes.
- Avatars are uploaded and downloaded over L2CAP.
BLE needs two physical devices: simulators and emulators have no usable Bluetooth.
dotnet build samples/TicTacToe -f net10.0-android -t:Run
dotnet build samples/TicTacToe -f net10.0-ios -t:Run
Tests
dotnet test tests/Shiny.BluetoothLE.Hubs.Tests
The tests run the generated hub dispatcher and the generated client proxy against each other through an in-memory "radio", plus diagnostic tests for the generator. No hardware is needed.
Documentation and release notes: https://shinylib.net/blehubs/
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Shiny.BluetoothLE (>= 5.9.0-beta-0015)
- Shiny.BluetoothLE.Hubs (>= 1.0.0-alpha-0006-g9e9412ee84)
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 |
|---|---|---|
| 1.0.0-alpha-0006-g9e9412ee84 | 22 | 10/8/2026 |
| 1.0.0-alpha-0006 | 20 | 10/8/2026 |
| 1.0.0-alpha-0005-gd56d166e6b | 33 | 10/7/2026 |
| 1.0.0-alpha-0005 | 33 | 10/7/2026 |
| 1.0.0-alpha-0004-gbc068ff52e | 46 | 10/3/2026 |
| 1.0.0-alpha-0004 | 42 | 10/3/2026 |