muka/go-bluetooth

Support both standard 16-bit and custom UUIDs in the same service.

Closed this issue · 2 comments

Thank you for creating this library! It's pretty easy to follow and use once you're familiar with bluez. I'm having an issue with using the right UUIDs for the components of my application though:

When creating a service.App, a UUIDSuffix is supplied. For all services, characteristics, etc created within this app, the UUIDSuffix is attached to the provided UUID prefix string.

When implementing standard BLE services like Battery, Device Information, etc, the service UUID is expected to be one of these 16bit codes: https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf

attached to the "standard" suffix: -0000-1000-8000-00805F9B34FB

But a given bluetooth application shouldn't use the standard suffix with a 16-bit prefix - it should implement a fully custom 128-bit UUID, and it should do the same for the custom services or characteristics it exposes.

So, if building a custom application that supports HID-over-GATT (a standard profile for keyboards, mice, etc), the application should:

  1. Have its own 128-bit UUID that doesn't use the standard suffix.
  2. Expose an HID service with UUID: 0x1812
  3. Expose a Device Information Service with UUID: 0x180A
  4. Expose a Battery Service with UUID: 0x180F

Each of these services also have characteristics with constant, assigned UUIDs. I can't find a straightforward way to implement that using this library. I need to manually UUID-hack each member of the hierarchy like:

blueToothDeviceInfoUUID := "0000180A"
standardSuffix := "-0000-1000-8000-00805F9B34FB"
appOpts := AppOptions{
  UUID: "1123",
  UUIDSuffix: "some-custom-very-long-value",
}

app, _ := service.NewApp(appOpts)
svc := app.NewService("11111111") // device info service
svc.UUID = blueToothDeviceInfoUUID + standardSuffix
svc.Properties.UUID = svc.UUID

I could of course go the other way as well - simply UUID-hack the app itself once I've set up all the services and characteristics, but both of these approaches feel "off."

One possibile change to support these:

  1. Allow UUIDs passed in arguments to be full 128-bit UUIDs. In this case, skip all of the base + value + suffix logic inside GenerateUUID. This should be true for services, characteristics, and descriptors.
  2. Provide at least a partial list of the UUIDs provided by the SIG for common service and characteristic values so that users can easily refer to them. Something like types.ServiceBattery, types.CharacteristicBatteryLevel

Then I could write something like:

appOpts := AppOptions{
  UUID: "1123",
  UUIDSuffix: "some-custom-very-long-value",
}

app, _ := service.NewApp(appOpts)
svc := app.NewService(types.ServiceDevice) // device info service
char := svc.NewChar(types.CharacteristicModelNumber)

An alternative might be dispensing with the UUID "generation" in the App entirely. Instead of supplying a suffix to the application, provide transformation functions for the short codes:

func ShortUUID(arg uint16) string {
  // convert to string and attach standard suffix
}
muka commented

Hi @joeblubaugh glad you find the library useful. If you want to provide a PR I will be happy to review. Ideally the API should be backward compatible but still open to suggestions. Thank you!

muka commented

Closing, please reopen if you have updates. Thanks