GuidesComponentsBluetooth management

Custom devices

Declare a supported Bluetooth device when the SDK has no built-in protocol.

  • Stable unknown
  • Release candidate since 1.16.1-rc.3
  • Development since 1.16.1-dev.2111+9796905

Use defineCustomDevice when the SDK does not already implement a device protocol and the device can be described by the declarative GATT and frame format. The function validates the declaration and returns a branded CustomDeviceDefinition for BTProvider.acceptedDevices.

Import the builder and types from @ovok/native/bt-management.

Define a device

import { MeasurementTypeKey } from "@ovok/core/next";
import {
  defineCustomDevice,
  IntegratedDevices,
} from "@ovok/native/bt-management";

const acmeScale = defineCustomDevice({
  id: "acme-scale",
  nameMatch: "ACME Scale",
  stuckDataTimeoutMs: 30_000,
  services: [
    {
      uuid: "181d",
      monitor: [
        {
          uuid: "2a9d",
          frame: { minLengthBytes: 3 },
          caseSelectorBytes: [0],
          cases: [
            {
              selector: "00",
              kind: "result",
              measurementTypeKey: MeasurementTypeKey.bodyWeight,
              fields: [
                {
                  name: "bodyWeight",
                  byteIndexes: [1, 2],
                  byteOrder: "little",
                  encoding: "uint",
                  unit: "kg",
                  scale: 0.005,
                  decimals: 3,
                },
              ],
            },
          ],
        },
      ],
    },
  ],
});

const acceptedDevices = [IntegratedDevices.BP2, acmeScale] as const;

A string nameMatch becomes an exact advertised-name match. A RegExp can match a family of names; do not use the global or sticky flag. When a device does not advertise a usable name, set matchByService: true to match by a declared service UUID.

Pass the resulting definition in acceptedDevices. The custom ID becomes deviceData.name for results and errors. IDs are trimmed, default to ovok-custom-device when omitted, and cannot collide with a built-in device name.

What a declaration describes

  • services lists GATT service UUIDs and one or more monitored characteristics. Every service needs at least one monitor.
  • frame describes a frame header, minimum length, optional length field, and optional checksum. The SDK does not scan forward for a header; field offsets are measured from the beginning of the frame.
  • caseSelectorBytes selects a case using the hex values at those byte positions.
  • A result case declares a supported measurement type and fields. Each field must match the SDK measurement catalog's field name and canonical unit, and its byte indexes must fit within the frame.
  • stream and idle cases update device state without emitting a measurement.
  • repeatedRecords decodes fixed-width records separately from a history frame.
  • additionalMeasurements can emit multiple measurement types from a single result frame.
  • An optional services[].write sends one static hex command. By default (when: "every-frame") it goes out after connection and after each processed frame, which polls a device that answers one request with one reading. when: "connect" sends it once after connection, for a request answered by a stream of frames, such as the Glucose Profile's "report all stored records".
  • Optional preferredMtu requests an MTU from 23 through 517 after connection.
  • Optional oneReadingPerConnection limits output for that custom definition.
  • An optional createCommandSequencer (second argument to defineCustomDevice) replaces services[].write with a CommandSequencer built for each connection from the device id and the manager's recordCursorStorage. Its initialCommands may resolve later, so the first command can depend on stored state, as the Glucose Profile's request for the records after the newest one read does.
  • An optional decorateReading (second argument to defineCustomDevice) adds metadata such as the record's own timestamp to each validated reading. Returning null withholds the reading, for a frame that decodes but is not a patient measurement, such as a glucose control-solution test.

defineCustomDevice checks IDs, UUIDs, timeouts, frame bounds, measurement fields, and other declaration constraints at registration. Invalid declarations throw with the declaration path; the device is not accepted as a runnable definition.

Know when the declarative format is not enough

A device that needs a handshake, challenge-response, or command sequence cannot be represented by the ordinary static-write declaration. The SDK also supports a dedicated LEPU/Viatom protocol declaration through lepu, which owns its framing and command sequence. For a protocol outside the declarative forms, an SDK driver is required.

See the public custom device types and the Bluetooth guide.

Last updated on

On this page