Custom devices
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";
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
serviceslists GATT service UUIDs and one or more monitored characteristics. Every service needs at least one monitor.framedescribes 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.caseSelectorBytesselects a case using the hex values at those byte positions.- A
resultcase 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. streamandidlecases update device state without emitting a measurement.repeatedRecordsdecodes fixed-width records separately from a history frame.additionalMeasurementscan emit multiple measurement types from a single result frame.- An optional
services[].writesends one static hex command after connection and after each processed frame. - Optional
preferredMturequests an MTU from 23 through 517 after connection. - Optional
oneReadingPerConnectionlimits output for that custom definition.
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.