@amazon-devices/kepler-connectivity-controller
v2.1.5
Published
**AccClient** is the Vega script API library of AmazonConnectivityControl. It provides a set of APIs for Accessory Control, such as discovering, pairing, connecting and disconnecting, as well as any other accessory management APIs (e.g, Remote Firmware Up
Readme
Get Started with AccClient API
AccClient is the Vega script API library of AmazonConnectivityControl. It provides a set of APIs for Accessory Control, such as discovering, pairing, connecting and disconnecting, as well as any other accessory management APIs (e.g, Remote Firmware Update). This would enable our customer teams such as VegaTV OOBE/Settings to create application logic in VegaScript/React Native.
Key Concept
This section provides information about the key concepts associated with Bluetooth connectivity. At a minimum, you need to understand the concepts for pairing and connection.
|Item |Description | |--- |--- | |Device |Any type of device that runs software. From connectivity point of view, we also refer to the device runs our software in question as Local Device, and other devices that are pairing/connecting with us as Peer Devices.Peer Devices are considered as accessories of Local Device in this document (from ACC software point of view).Where its not confusing, we can interchangeably use Device and Accessory, for example, when we say “nearby devices”, we also mean “nearby accessories”. | |Local Device |This is the device on which the apps runs. | |Peer Device/Remote Device |This refers to the alternate device with Bluetooth capabilities that interfaces with the Local device. For example a smartphone, remote, tablet, speaker, etc | |Accessory |A Peer Device that the Local Device tries to discover, then pair and connect whith through the APIs for this document. From the perspective of a Local Device, all peer devices that it pairs ir connects with it an accessory. | |Accessory Discovery |The procedure that the Local Device uses to scan or inquire about nearby accessories. After an accessory is discovered it reports the accessory basic information to the requesting app. Nearby accessories must be in Discoverable Mode to be discovered. | |Local Device Visibility |The Local Device can be made visible (discoverable or connectable) by nearby devices, so that a user can initiate pairing/connecting procedures from the nearby devices after they are seen from their side. | |Pairing/Bonding |When an accessory is discovered by the Local Device, it typically needs a pairing procedure to establish a two-way bond, so that each side of the bond stores the link keys used to create a connection between these two devices. Thus a Pairing procedure is also known a Bonding procedure. Pairing state changes are reported to apps during this pairing procedure.During a Pairing procedure, based on the different security configuration of both Local Device and Peer Device, different pairing behavior maybe undergone on both sides. Eg. silently accepted by the lower level of SW stack, prompt user consent or pin input dialog. | |Connection/Reconnection/Disconnection | Connection means if there is physical link estabashed between two devices which are usually paired/bonded. Disconnection mean link broken due to any reasons(initiate by use from either side, power cycle, evironment interference). Reconnection means the link re-establish procedure. The reconnection can be initiated automatically from either side of the two peer devices without user action. | |Unpairing/Unbonding | Unpairing/Unbonding means to release the two-way bond between two devices include the connection. Both side will erase the information like keys stored. The unpairing/unbonding can be initiated from either device. It does not require active connection between the two devices. But if there is active connection while the unpairing procedure is being performed, some interaction might be performed between two devices. Factory Reset on either side of the device effectively unpairs the peer devices since it removes the link keys which are stored on the device persistent storage. | |Link Lost/Lost Link/Lost Connection |From connectivity domain, this means connection between the two peers are not established (or lost), maybe due to air congestion, far distance between the two devices (weak signal) etc, but it is still possible that the two devices can establish connection again (link keys are still valid on both sides). It is often heard from non-connectivity people say “Link Lost” which they actually mean “can never reconnect”, which really means “Lost Pairing” as described below. | |Pairing Lost/Lost Pairing |This means either side (or both sides) of the two devices got invalid link keys for the Peer Device, either due to intendedly unpaired from either side, or just due to some storage corruption of the link key storage where it can not retrieve valid link keys.The two devices have to go through a new Pairing procedure in order to be able to connect with each other again. |
Install and Setup the AccClient API
Application can consume AccClient through KeplerSDK. To use AccClient follow the instruction below:
Configure the package dependency and manifest
Update Config file
Add AccClient dependency in Application Config file
dependencies = {
1.0 = {
...
AccClient = 1.0.0;
};
};Update package.json
In package.json, add kepler-conn-accclient dependency
"dependencies": {
...
"@amazon-devices/kepler-conn-accclient": "1.0.0"
},Update manifest.toml
Declare dependency on com.amazon.connectivity.control service
[[wants.service]]
id = "com.amazon.connectivity.control"Declare relative permissions required by each APIs used by Apps. E.g
[[needs.privilege]]
id = "com.amazon.acc.privilege.access"
[[needs.privilege]]
id = "com.amazon.acc.privilege.pair"
...How to Use AccClient API in Your App
In this section, AccessoryBasicInfo is introduced firstly before starting the APIs sections. AccessoryBasicInfo is used to represent the basic information of a accessory. It's frequently used in the callback function or when application actively query accessories from ACC server.
/**
* @interface AccessoryBasicInfo
*
* @description Accessory information - Basics. This is basic information of an accessory
* which are available when it is discovered.
*/
export interface AccessoryBasicInfo {
/** Accessory identity string */
identity: string;
/** Accessory name string */
name: string;
/** Accessory protocol ID */
protocol: AccessoryProtocol;
/** Accessory category ID */
category: AccessoryCategory;
}identityis a unique string allocated by acc server to be used across different APIs.nameis user friendly name usually retrived from the accessory.protocolindicate which protocol (e.g Bluetooth, Wifi... the accessory works over). SeeAccessoryProtocolin later section.catogoryindicate the device type (e.g remote controller, audio device, phone ...). SeeAccessoryCategoryin later section.
Instantiation of AccessoryControlClientInterface
All APIs are provided in the AccessoryControlClientInterface class. An application process can only have one instance of this class. When instatiating andApplication needs to implement and install a AccessoryControlEventDelegate instance with a predefined set of callback handlers.
AccessoryControlEventDelegate definitions:
/**
* @interface Accessory Control Event Callbacks
*/
export interface AccessoryControlEventDelegate {
/** Accessory Discovery State Changed Callback */
onAccessoryDiscoveryStateChange: AccessoryDiscoveryStateChangeHandler;
/** Accessory Discovered Callback */
onAccessoryDiscovery: AccessoryDiscoveryHandler;
/** Accessory Autopair State Changed Callback */
onAccessoryAutopairStateChange: AccessoryAutopairStateChangeHandler;
/** Accessory Autopaired Callback */
onAccessoryAutopair: AccessoryAutopairHandler;
/** Accessory Pairing Requested Callback */
onAccessoryPairingRequest: AccessoryPairingRequestHandler;
/** Accessory Pair State Changed Callback */
onAccessoryPairingStateChange: AccessoryPairingStateChangeHandler;
/** Accessory Connect State Changed Callback */
onAccessoryConnectionStateChange: AccessoryConnectionStateChangeHandler;
/** Accessory Information Updated Callback */
onAccessoryInfoUpdate: AccessoryInfoUpdateHandler;
/** Accessory DFU State Changed Callback */
onAccessoryDfuStateChange: AccessoryDfuStateChangeHandler;
}Apps are responsible to interpret the event parameters of each event in order to update UI components and drive app state machines. These parameters are detailed in later sections for each use case. Based on the app functions to be implemented, it may not need to implement all the callback handlers in AccessoryControlEventDelegate.
Code Examples
const accCallbacks: AccessoryControlEventDelegate = {
onAccessoryDiscoveryStateChange: (state: AccessoryDiscoveryState) => {
...
},
onAccessoryDiscovery: (basics: AccessoryBasicInfo) => {
...
},
...
};
const accClient = new AccessoryControlClientInterface(accCallbacks);Initialization & Deinitialization
Apps need to always invoke initialize before starting perform other APIs calls. initialize performs registration of the application to ACC server, which is creating a bookkeeping in the ACC server side. deinitialize does the deregistration of the application from ACC server.
Permission "com.amazon.acc.privilege.access" is required before calling initialize.
/**
* @brief initialize as a client to ACC server.
*
* @privilege com.amazon.acc.privilege.access
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async initialize();
/**
* @brief Deinitialize as a client to ACC server.
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async deinitialize();Performing Accessory Discovery
Apps can initiate Accessory Discovery with an expected duration. When accessory is discovered onAccessoryDiscovery will be invoked to report to the requesting app with AccessoryBasicInfo. App can cancel the active Accessory Discovery during the time of discovery. onAccessoryDiscoveryStateChange will be invoked to notify the requesting app with AccessoryDiscoveryState.
Permission "com.amazon.acc.privilege.discovery" is required before calling discovery APIs and geting corresponding callback.
Permission "com.amazon.location.privilege.access.fine" is required additionally to startAccessoryDiscovery and onAccessoryDiscovery.
/**
* @brief Start accessory discovery
*
* @privilege com.amazon.acc.privilege.discover
* @privilege com.amazon.location.privilege.access.fine
*
* @param[in] protocol Protocol bridge to start discovery
* @param[in] category Accessory category to be discovered
* @param[in] duration Accessory discovery duration in seconds, default to be 30 seconds
* if not specified by the caller.
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async startAccessoryDiscovery(
protocol: AccessoryProtocol,
category: AccessoryCategory,
duration?: number
);
/**
* @brief Cancel accessory discovery
*
* @privilege com.amazon.acc.privilege.discover
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async cancelAccessoryDiscovery(protocol: AccessoryProtocol);The protocol specify expected protocol bridge to initiate for Accessory Discovery, since ACC is designed to be able to support multiple protocols using Protocol Bridges. However, currently, only Bluetooth protocol bridge is supported, and a Mocked protocol bridge is provided for debug purposes.
/**
* @enum AccessoryProtocol
*
* @description Accessory Protocol ID. The ACC server has multiple protocol bridges
* to interface with low level protocol middlewares for accessory management, which
* are identified by protocol IDs.
*
* Note that only `AccessoryProtocol.BLUETOOTH` and `AccessoryProtocol.MOCKED` bridges
* are implemented currently, and `AccessoryProtocol.MOCKED` is only intended for ACC
* testing and debugging, not intended for customer application use.
*
* When accessory is reported from protocol bridges, it will specify its protocol ID
* which is integer based value, and will be mapped to AccessoryProtocol before it is
* sent to the application. When a protocol bridge is implmented in ACC server but
* application (and client library) are not adapted (not aware of the protocol bridge),
* the `AccessoryProtocol.UNKNOWN` will be set for the protocol field.
*/
export enum AccessoryProtocol {
/** Bluetooth */
BLUETOOTH = 'accessory.protocol.BLUETOOTH',
/** Alexa Mobile Accessory (AMA) - bridge not implemented yet! */
AMA = 'accessory.protocol.AMA',
/** ZigBee Mesh - bridge not implemented yet! */
ZIGBEE_MESH = 'accessory.protocol.ZIGBEE_MESH',
/** BLE Mesh - bridge not implemented yet! */
BLE_MESH = 'accessory.protocol.BLE_MESH',
/** HALO - bridge not implemented yet! */
HALO = 'accessory.protocol.HALO',
/** Secure Streams - bridge not implemented yet! */
SECURE_STREAMS = 'accessory.protocol.SECURE_STREAMS',
/** Matter - bridge not implemented yet! */
MATTER = 'accessory.protocol.MATTER',
/** Wi-Fi - bridge not implemented yet! */
WIFI = 'accessory.protocol.WIFI',
/** Mocked - a mocked bridge implemented for testing & debugging */
MOCKED = 'accessory.protocol.MOCKED',
/** Unknown protocol - not aware by the application */
UNKNOWN = 'accessory.protocol.UNKNOWN'
}The category is the expected Accessory Category to be discovered. The following categories are currently exposed.
/**
* @enum AccessoryCategory
*
* @description Accessory Category ID. This is used to identify the accessory
* device types. The categorizing is mostly based on Bluetooth Specification
* definitions of `Major Device Class` and ZigBee Cluster Library Specification
* definitions of `Cluster ID`, with some extensions. Different protocol bridges
* may have their own mapping of this category into/from their own classification
* value. This is based on the fact that the same device type can be implemented
* with different data transport protocols, but from the user's perspective they
* just care about the functionlity of the device. For example, TV Settings App
* may present a menu item to only discover "Remote Controllers", but there maybe
* "Remote Controllers" implemented using Bluetooth Classic, Bluetooth Low Energy,
* or ZigBee, and even for Bluetooth Low Energy based "Remote Controllers", it may
* need product level filtering based on Vendor ID and Product ID. Thus, the user
* can express "what I want" but actual implementation can determine "what I have".
*
* In case the user (or application) doesn't care about pre-defined categorizing,
* the `AccessoryCategory.ANY` can be specified for discovering. In this case, the
* protocol bridges would report all discovered accessories, but would still try
* to set the category based on its internal knowledge. However, when it can not
* find an exact map of the category, the `AccessoryCategory.UNKNOWN` will be set.
*/
export enum AccessoryCategory {
/* Remote Controllers (e.g, Amazon Alexa voice remotes) */
REMOTE_CONTROLLER = 'accessory.category.REMOTE_CONTROLLER',
/** Game Controllers (e.g, Amazon Luna game controllers) */
GAME_CONTROLLER = 'accessory.category.GAME_CONTROLLER',
/** Audio/Video (e.g, headphones, microphoens) */
AUDIO_VIDEO = 'accessory.category.AUDIO_VIDEO',
/** Networking (e.g, network access points) */
NETWORKING = 'accessory.category.NETWORKING',
/** Peripheral (e.g, mouses, joysticks, keyboards) */
PERIPHERAL = 'accessory.category.PERIPHERAL',
/** Computer (e.g, laptops, desktops) */
COMPUTER = 'accessory.category.COMPUTER',
/** Wearable (e.g, smart watches) */
WEARABLE = 'accessory.category.WEARABLE',
/** Imaging (e.g, printers, scanners) */
IMAGING = 'accessory.category.IMAGING',
/** Health (e.g, thermometers) */
HEALTH = 'accessory.category.HEALTH',
/** Telephone (e.g, smart phones) */
PHONE = 'accessory.category.PHONE',
/** TOY (e.g, toy robots) */
TOY = 'accessory.category.TOY',
/** Unknown category (not categoried) */
UNKNOWN = 'accessory.category.UNKNOWN',
/** Any category */
ANY = 'accessory.category.ANY'
}Callback onAccessoryDiscoveryStateChange is defined as below, with AccessoryDiscoveryState as parameter.
/**
* @type Accessory Discovery State Changed Callback
*
* @privilege com.amazon.acc.privilege.discover
* @privilege com.amazon.location.privilege.access.fine
*
* @param[in] accessoryDiscoveryState Accessory discovery state
*/
export type AccessoryDiscoveryStateChangeHandler = (
accessoryDiscoveryState: AccessoryDiscoveryState,
) => void;
/**
* @enum AccessoryDiscoveryState
*
* @description Accessory Discovery State
*/
export enum AccessoryDiscoveryState {
/** Unkown discovery state */
UNKNOWN = 0,
/** Discovery has been successfully started */
STARTED = 1,
/** Discovery has completed after specified timeout */
STOPPED = 2,
/** Discovery has been canceled */
ABORTED = 3
}Callback onAccessoryDiscovery is defined as below, with AccessoryBasicInfo as parameters.
/**
* @type Accessory Discovered Callback
*
* @privilege com.amazon.acc.privilege.discover
* @privilege com.amazon.location.privilege.access.fine
*
* @param[in] accessoryBasicInfo Accessory basic information
*/
export type AccessoryDiscoveryHandler = (
accessoryBasicInfo: AccessoryBasicInfo,
) => void;Setting Device Visibility Mode
For privacy reasons, local device should not be visiable by nearby devices. Applications need the following capabilities to set local device visibility in specific case.
Permission "com.amazon.acc.privilege.discoverable" is required before accessing set visibility API.
/**
* @brief Set local device visibility mode
*
* @privilege com.amazon.acc.privilege.discoverable
*
* @param[in] protocol Accessory protocol
* @param[in] visibility Local device visibility mode
* @param[in] duration Accessory discoverable duration in seconds when visibility
* is set to LocalDeviceVisibility.CONNECTABLE_DISCOVERABLE, default to be 120
* seconds if not specified by the caller.
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async setLocalDeviceVisibility(
protocol: AccessoryProtocol,
visibility: LocalDeviceVisibility,
duration?: number,
)3 modes are currently provides:
- visible (or discoverable). user can initiate discovery/pairing/connecting of local device from the nearby devices.
- invisible (or not discoverable) but in the meantime still connectable from nearby devices, which hide local Device from public but allow being connected by known devices.
- hidden state (not visible and not connectable from any nearby devices).
/**
* @enum LocalDeviceVisibility
*
* @description Local Device Visibility Modes. This is a generalization of the Bluetooth
* scan mode concept.
*
* From Bluetooth perspective, there are 3 modes, "not connectable and not discoverable",
* "connectable but not discoverable" and "connectable and discoverable", but there is no
* "discoverable but not connectable" mode.
*
* However, as a generalization, the `LocalDeviceVisibility.DISCOVERABLE_NOT_CONNECTABLE`
* is defined, to support possibly other protocols that can exhibit this visibility behavior.
* If this mode is requested to Bluetooth bridge, `AccessoryControlStatus.NOT_SUPPORTED` will
* be returned.
*/
export enum LocalDeviceVisibility {
/** Not connectable and not discoverable */
NOT_CONNECTABLE_NOT_DISCOVERABLE = 0,
/** Connectable but not discoverable */
CONNECTABLE_NOT_DISCOVERABLE = 1,
/** Connectable and discoverable */
CONNECTABLE_DISCOVERABLE = 2,
/** Discoverable but not connectable */
DISCOVERABLE_NOT_CONNECTABLE = 3
}Performing Accessory Pairing
To initiate outgoing pairing procedure, call the following API.
Permission "com.amazon.acc.privilege.pair" is required before accessing pairing relative APIs and get corresponding callback.
/**
* @brief Pair accessory
*
* @privilege com.amazon.acc.privilege.pair
*
* @param[in] accessoryIdentity Accessory identity to pair
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async pairAccessory(accessoryIdentity: string);Callback onAccessoryPairingStateChange defined as below function will be invoked to notify application the AccessoryBasicInfo and AccessoryPairingState.
/**
* @type Accessory Pair State Changed Callback
*
* @privilege com.amazon.acc.privilege.pair
*
* @param[in] accessoryBasicInfo Accessory basic information
* @param[in] AccessoryPairingState Accessory pair state
*/
export type AccessoryPairingStateChangeHandler = (
accessoryBasicInfo: AccessoryBasicInfo,
accessoryPairingState: AccessoryPairingState,
) => void;
/**
* @enum AccessoryPairingState
*
* @description Accessory Pairing State
*/
export enum AccessoryPairingState {
/** Unkown pairing state */
UNKNOWN = 0,
/** Accessory has been paired */
PAIRED = 1,
/** Accessory has been unpaired */
UNPAIRED = 2,
/** Accessory pairing is in progress */
PAIRING = 3,
/** Accessory unpairing is in progress */
UNPAIRING = 4,
/** Accessory pairing is canceled */
PAIRING_ABORTED = 5,
/** Accessory unpairing is canceled */
UNPAIRING_ABORTED = 6,
}During the pairing procedure, depending on the actually protocol, security and I/O capability configuration of the two peer devices being paired, the low level SW stack may prompt pairing request to the application, which requires the application to display some message to the user, asking for actions from the user. Applications should display corresponding pairing messages and instructions for user accordingly. Based on the user actions, application should send corresponding pairing response to the low level stack to complete the pairing (either accepted or rejected).
No matter if it is an outgoing or incoming pairing, the low level SW stack might prompt the pairing request to the app, through callback onAccessoryPairingRequest with AccessoryBasicInfo and AccessoryPairingRequest indicating application the accessory information and action to take. In handling of the pairing request, it might require the application to send back the user response through AccessoryPairingResponse.
/**
* @interface AccessoryPairingRequest
*
* @description Accessory pairing request. This is a wrapper of pairing message for different
* protocols. Right now only Bluetooth pairing message is supported, it may be expaned with
* other pairing message types using the TypeScript union type operator ("|").
*
* @@example
* Here is an example of `onAccessoryPairingRequested()` callback in the application, which
* casts the message into `BluetoothPairingRequest` for Bluetooth accessory.
* ```
* onAccessoryPairingRequested: (
* accessoryBasicInfo: AccessoryBasicInfo,
* accessoryPairingRequest: AccessoryPairingRequest
* ) => {
* // Cast into specific pairing message type based on accessory protocol.
* if (accessoryBasicInfo.protocol == AccessoryProtocol.BLUETOOTH) {
* const pairingRequest = accessoryPairingRequest.message as BluetoothPairingRequest;
* if (pairingRequest.request === BluetoothPairingVariant.CONFIRM) {
* ...
* }
* }
* }
* ```
*/
export interface AccessoryPairingRequest {
/** Request message data */
message: BluetoothPairingRequest;
}
/**
* @interface AccessoryPairingResponse
*
* @description Accessory pairing response.
*/
export interface AccessoryPairingResponse {
/** Response message data */
message: BluetoothPairingResponse;
}For Bluetooth Accessories, actually pairing message is in BluetoothPairingRequest and BluetoothPairingResponse.
/**
* @interface BluetoothPairingRequest
*
* @description Bluetooth pairing request message.
*
* Based on the `request` value of this pairing message, different actions should be taken by
* the application/user.
*
* If the `BluetoothPairingVariant.DISPLAY` is set for the request, the application is required
* to display the PIN code value or passkey value in the `keydata` field (whether its PIN code
* or passkey type should be determined by `keytype`), and ask the user to input the PIN code
* or passkey on the peer device side.
*
* If the `BluetoothPairingVariant.INPUT` is set for the request, the application is required
* to ask user to input the PIN code value or passkey, the user input value should be sent as
* `keydata` for pairing response message in `AccessoryPairingResponse`.
*
* If the `BluetoothPairingVariant.COMPARE` is set for the request, the application is required
* to display the passkey value in the `keydata` field, and also ask the user to compare and
* confirm if the same passkeys are displayed on both sides of the pairing devices.
*
* If the `BluetoothPairingVariant.CONSENT` is set for the request, the application is required
* to ask user to accept or reject to pair with the accessory.
*
* For both `BluetoothPairingVariant.COMPARE` and `BluetoothPairingVariant.CONSENT` cases, the
* `BluetoothPairingConfirmation.CONFIRMED` or `BluetoothPairingConfirmation.REJECTED` should be set by
* the application as result of user responding to the pairing request UI, as `keydata` filed of
* the pairing response message in `AccessoryPairingResponse`.
*
* The `keysize` can be used by the application to tell the user how long the user is expected to
* input (on either side of the pairing devices).
*/
export interface BluetoothPairingRequest {
/** Pairing request variant */
variant: BluetoothPairingVariant;
/** Key type */
keytype?: BluetoothPairingKeyType;
/** Key size */
keysize?: number;
/** Key data */
keydata?: string;
}
/**
* @interface BluetoothPairingResponse
*
* @description Bluetooth pairing response message.
*
* Note that for Bluetooth accessory pairing, `BluetoothPairingResponse` has same information
* fileds as `BluetoothPairingRequest`. When application sends pairing response, it should copy
* same values for the `request`, `keytype`, `keysize` fileds from the `AccessoryPairingRequest`
* into `BluetoothPairingResponse`, and for `keydata` it should set with actual user input (for
* `BluetoothPairingVariant.INPUT`), or set it with either `BluetoothPairingConfirmation.CONFIRMED`
* or `BluetoothPairingConfirmation.REJECTED` (for variant of `BluetoothPairingVariant.COMPARE` or
* `BluetoothPairingVariant.CONSENT`).
*/
export interface BluetoothPairingResponse {
/** Pairing request variant */
variant: BluetoothPairingVariant;
/** Key type */
keytype?: BluetoothPairingKeyType;
/** Key size */
keysize?: number;
/** Key data */
keydata?: string;
}Apps only needs to support the following scenarios based on four different values for the variant: BluetoothPairingVariant from the BluetoothPairingRequest.
|Value |Description |
|--- |--- |
|DISPLAY |This BluetoothPairingRequest asks the application to display a pairing message with PIN code or Passkey, which instructs the user to input what he sees on the screen on the peer device side. |
|INPUT |This BluetoothPairingRequest asks the application to display a pairing message, which instructs the user to input PIN code or Passkey (the PIN code or Passkey can be either shown on the peer device, or just be some known values like “0000” or “1234”, that maybe printed on the peer device, or on the peer device user manual). |
|COMPARE |This BluetoothPairingRequest asks the application to display a pairing message with Passkey, which instructs the user to compare if he sees the same key on both pairing devices (the peer device will also display the Passkey). |
|CONSENT |This BluetoothPairingRequest asks the application to display a pairing message, without any key, which just instructs if the user consent whether to accept or reject the pairing. |
/**
* @enum BluetoothPairingKeyType
*
* @description Bluetooth Pairing Key Type, as part of `BluetoothPairingRequest`.
*
* From the Bluetooth Core Specification (v5.3, page 1253):
*
* There are a number of different representations of the Bluetooth Passkey. At
* a high level there are two distinct representations: one used with the Secure
* Simple Pairing and Security Manager, and another used with legacy pairing
* (where it is generally referred to as the Bluetooth PIN).
*
* For Secure Simple Pairing and Security Manager, the Bluetooth Passkey is a
* 6-digit numerical value. It is represented as integer value in the range
* 0x00000000 – 0x000F423F (000000 to 999999).
*
* PIN codes may be up to 16 characters. In order to take advantage of the full
* level of security all PINs should be 16 characters long. Variable PINs should
* be composed of alphanumeric characters chosen from within the Unicode range
* U+0000 to U+007F. If the PIN contains any decimal digits these shall be
* encoded using the Unicode Basic Latin characters (i.e., U+0030 to U+0039).
*
* For compatibility with devices with numeric keypads, fixed PINs shall be
* composed of only decimal digits, and variable PINs should be composed of
* only decimal digits.
*/
export enum BluetoothPairingKeyType {
/** Pincode - 4 ~ 16 alpha-numeric characters */
PINCODE = 'PINCODE',
/** Passkey - 6 digits */
PASSKEY = 'PASSKEY'
}
/**
* @enum BluetoothPairingVariant
*
* @description Bluetooth Accessory Pairing Request, as part of `BluetoothPairingRequest`.
*
* This is the pairing request to be made on the application/user to perform some actions
* in order to complete the pairing.
*/
export enum BluetoothPairingVariant {
/** App to display the key (Pincode or Passkey) */
DISPLAY = 'DISPLAY',
/** App to request user to input key (Pincode or Passkey) */
INPUT = 'INPUT',
/** App to request user to compare and confirm passkey on both sides */
COMPARE = 'COMPARE',
/** App to request user to accept or reject pairing (no key displayed) */
CONSENT = 'CONSENT'
}Code Examples
The following code shows a sample onAccessoryPairingRequest() callback handler.
/** Accessory Pairing Requested Callback */
onAccessoryPairingRequest: (
basics: AccessoryBasicInfo,
request: AccessoryPairingRequest
) => {
setResult("pair", "[" + basics.name + "," + basics.category + "] request pairing!");
/** Bluetooth and Mocked bridges use same Bluetooth pairing messaging */
if (basics.protocol == AccessoryProtocol.BLUETOOTH ||
basics.protocol == AccessoryProtocol.MOCKED) {
const pairingRequest = request.message as BluetoothPairingRequest;
console.log("App - onAccessoryPairingRequest variant - " + pairingRequest.variant);
if (pairingRequest.variant === BluetoothPairingVariant.COMPARE) {
let pairingMessage = "Do you want to pair with " + basics.name + "(" + basics.category + ")?\n"
pairingMessage += "If so please confirm passkeys are same on both devices!\n";
pairingMessage += "Passkey on this device:" + pairingRequest.keydata;
setSelectedAccessory(basics);
setLastPairingRequest(request);
showPairingDiaglog(pairingMessage,
"Confirm",
true,
false,
20000);
}
else if (pairingRequest.variant === BluetoothPairingVariant.INPUT) {
let pairingMessage = "Do you want to pair with " + basics.name + "(" + basics.category + ")?\n"
pairingMessage += "If so please input passkey which is displayed on the peer device!\n";
setSelectedAccessory(basics);
setLastPairingRequest(request);
showPairingDiaglog(pairingMessage,
"OK",
true,
true,
20000);
}
else if (pairingRequest.variant === BluetoothPairingVariant.DISPLAY) {
let pairingMessage = "Do you want to pair with " + basics.name + "(" + basics.category + ")?\n"
pairingMessage += "If so please input below " + pairingRequest.keytype + " on the peer device!\n";
pairingMessage += "The " + pairingRequest.keytype + " on this device:" + pairingRequest.keydata;
setSelectedAccessory(basics);
setLastPairingRequest(request);
showPairingDiaglog(pairingMessage,
"Done",
false,
false,
20000);
}
else if (pairingRequest.variant === BluetoothPairingVariant.CONSENT) {
let pairingMessage = "Do you want to pair with " + basics.name + "(" + basics.category + ")?\n"
setSelectedAccessory(basics);
setLastPairingRequest(request);
showPairingDiaglog(pairingMessage,
"Accept",
true,
false,
20000);
}
}
},The sample code shows how an app can respond to dialog actions.
onConfirmPressed={() => {
console.log("userInput=" + userInput);
setShowPairingDialog(false);
try {
if (selectedAccessory.identity !== undefined) {
console.log("calling accClient.replyAccessoryPairing - start");
console.log("replying accessory - " + selectedAccessory.identity);
/** Bluetooth and Mocked bridges use same Bluetooth pairing messaging */
if (selectedAccessory.protocol == AccessoryProtocol.BLUETOOTH ||
selectedAccessory.protocol == AccessoryProtocol.MOCKED) {
const pairingRequest = lastPairingRequest.message as BluetoothPairingRequest;
if (pairingRequest.variant == BluetoothPairingVariant.COMPARE ||
pairingRequest.variant == BluetoothPairingVariant.CONSENT) {
const pairingResponse: AccessoryPairingResponse = {
message: {
variant: pairingRequest.variant,
keytype: pairingRequest.keytype,
keysize: pairingRequest.keysize,
keydata: BluetoothPairingConfirmation.YES
}
};
accClient.sendAccessoryPairingResponse(
selectedAccessory.identity,
pairingResponse
).then(() => {
setResult("pair", "PASS - replied pairing request");
});
} else if (pairingRequest.variant == BluetoothPairingVariant.INPUT) {
const pairingResponse: AccessoryPairingResponse = {
message: {
variant: pairingRequest.variant,
keytype: pairingRequest.keytype,
keysize: pairingRequest.keysize,
keydata: userInput
}
};
accClient.sendAccessoryPairingResponse(
selectedAccessory.identity,
pairingResponse
).then(() => {
setResult("pair", "PASS - replied pairing request");
});
}
console.log("calling accClient.replyAccessoryPairing - end");
} else {
setResult("pair", "Nothing Selected");
}
}
} catch (e) {
console.error(e);
// @ts-ignore
setResult(key, e.message);
}
}1) BluetoothPairingVariant.DISPLAY
This can be a typical situation for KeplerTV pairing with a Bluetooth Keyboard, which uses the Legacy Pin Code Pairing method or Passkey Entry pairing method. Since KeplerTV has a display and the keyboard has input, the application (e.g KeplerTV Settings) would receive AccessoryPairingRequest with BluetoothPairingRequest like below (as an example).
{
"variant":"DISPLAY",
"keytype":"PINCODE",
"keysize":6,
"keydata":"777235"
}In this situation, the KeplerTV Settings should display the PIN code (or Passkey) string in keydata, asking the user to input the Pin code (or Passkey) on the keyboard to match exactly the same PIN code or Passkey displayed, then press Enter on the keyboard.
Note: This PIN code should be generated by ACC (on Android its generated by Bluetooth Stack in the Java layer).
2) BluetoothPairingVariant.INPUT
What if its a Bluetooth device which does not have any input method to input the PIN code or Passkey on its own (e.g a Bluetooth Mouse, or a Bluetooth Earbuds)? In this case, the app receives a AccessoryPairingRequest with BluetoothPairingRequest like below (as an example with PIN code):
{
"variant":"INPUT",
"keytype":"PINCODE",
"keysize":6
}On receiving the BluetoothPairingRequest, the application should display an input box and ask the user to input the PIN code (or Passkey) on the Local Device (assume the Local Device has both display and input, like KeplerTV, which has both display and a Remote for key input). The PIN code or Passkey to be entered by the user is typically provided by the accessory vendor (normally printed on the accessory itself, or written in user manual, or some publicly known default like “0000”), or displayed on a screen on the peer device.
After a user inputs the PIN code or Passkey, it should be used by the app to populate keydata field in the BluetoothPairingResponse and call sendAccessoryPairingResponse() API to send the response back to the ACC service. For other fields in the BluetoothPairingResponse , it can copy from corresponding BluetoothPairingRequest.
{
"variant":"INPUT",
"keytype":"PINCODE",
"keysize":6,
"keydata":"777235"
}3) BluetoothPairingVariant.COMPARE
When both pairing devices are designed with a display to show a six-digit number and are capable to verify those numbers with a simple YES/NO UI (e.g, pairing a Smart Phone with Carkit), the application may receive a AccessoryPairingRequest with BluetoothPairingRequest like below (as an example):
{
"variant":"COMPARE",
"keytype":"PASSKEY",
"keysize":6,
"keydata":"777235"
}On receiving this request, the application should display the Passkey in keydata, and instructs the user to compare if the same key are shown on both sides of the two devices, if so, press the affirmative button to confirm the pairing on both sides.
If user confirms, the following response message should be formed by the application in BluetoothPairingResponse with AccessoryPairingConfirmation.YES set to the keydata.
{
"variant":"COMPARE",
"keytype":"PASSKEY",
"keysize":6,
"keydata":"YES"
}If user rejects, the following response message should be formed by the application in BluetoothPairingResponse with AccessoryPairingConfirmation.NO set to the keydata.
{
"variant":"COMPARE",
"keytype":"PASSKEY",
"keysize":6,
"keydata":"NO"
}Then the application should call sendAccessoryPairingResponse() API to send the response back to the ACC service. For other fields in the BluetoothPairingResponse, it can copy from corresponding BluetoothPairingRequest.
4) BluetoothPairingVariant.CONSENT
Bluetooth specification supports a JustWorks pairing method, as a special kind of Numeric Comparison pairing method, which is normally handled (auto-accepted) in the lower level SW stacks, thus no user interactions are required. This is the case for KeplerTV pairing a FireTV Remote. This might be considered as easy for use for the user. However, in some cases, instead of accept the pairing automatically, the application may just still need to raise a consent ask to the user, to query if user wants to accept or reject pairing of an accessory (but without any other action like input some key or compare the keys on both pairing devices),
In this case, the application may receive a AccessoryPairingRequest with BluetoothPairingRequest like below (as an example):
{
"variant":"CONSENT"
}If user confirms, the following response message should be formed by the application in BluetoothPairingResponse with AccessoryPairingConfirmation.YES set to the keydata.
{
"variant":"CONSENT",
"keydata":"YES"
}If user rejects, the following response message should be formed by the application in BluetoothPairingResponse with AccessoryPairingConfirmation.NO set to the keydata.
{
"variant":"CONSENT",
"keydata":"NO"
}Performing Accessory Unpairing
To unpair a paired accessory, no matter if it is connected or not, call the following API.
/**
* @brief Unpair accessory
*
* @param[in] accessoryIdentity Accessory identity to unpair
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async unpairAccessory(accessoryIdentity: string);If an accessory is unpaired successfully, callback onAccessoryPairingStateChange will be invoked to nofity with the AccessoryPairingState to be AccessoryPairingState.UNPAIRED.
Performing Accessory Connecting & Disconnecting
When an accessory is paired, you can disconnect it from the Local Device, e.g, from Settings, and you can connect it again, by using the following APIs.
Permission "com.amazon.acc.privilege.connect" is required before accessing connection related APIs and get corresponding callback.
/**
* @brief Connect accessory
*
* @privilege com.amazon.acc.privilege.connect
*
* @param[in] accessoryIdentity Accessory identity to connect
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async connectAccessory(accessoryIdentity: string);
/**
* @brief Disconnect accessory
*
* @privilege com.amazon.acc.privilege.connect
*
* @param[in] accessoryIdentity Accessory identity to disconnect
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async disconnectAccessory(accessoryIdentity: string);Callback onAccessoryConnectionStateChange defined as below function will be invoked let the app know an accessory connection state change with the AccessoryConnectionState
/**
* @type Accessory Connection State Changed Callback
*
* @privilege com.amazon.acc.privilege.access
*
* @param[in] accessoryBasicInfo Accessory basic information
* @param[in] accessoryConnectionState Accessory connection state
*/
export type AccessoryConnectionStateChangeHandler = (
accessoryBasicInfo: AccessoryBasicInfo,
accessoryConnectionState: AccessoryConnectionState,
) => void;
/**
* @enum AccessoryConnectionState
*
* @description Accessory Connection State
*/
export enum AccessoryConnectionState {
/** Unkown connection state */
UNKNOWN = 0,
/** Accessory has been connected */
CONNECTED = 1,
/** Accessory has been disconnected */
DISCONNECTED = 2,
/** Accessory connection has been initiated */
CONNECTING = 3,
/** Accessory connection has been initiated */
DISCONNECTING = 4,
/** Accessory connection request has been canceled */
CONNECTION_ABORTED = 5,
/** Accessory disconnection request has been canceled */
DISCONNECTION_ABORTED = 6,
}The Peer Device can also initiate connection/disconnection to Local Device for some devices (e.g if you paired with a Smart Phone on KeplerTV, you can either connect/disconnect from Local Device, or you can connect/disconnect from the Settings on the Smart Phone). Disconnection can also happen in other scenarios, such as bad air congestion condition, device going too far away from each other. onAccessoryConnectionStateChange will be invoked for these scenarios.
Performing Accessory List Querying
Permission "com.amazon.acc.privilege.access" is required before accessing the API.
In the ACC service, accessories are organized in following lists, which we can query from apps.
- Paired accessories list, which keeps all the current paired accessories (this is actually backed by persistent storage).
- Connected accessories list, which keeps all the actively connected accessories (paired accessories are not necessarily connected at the time of query).
- Discovered accessories list, this is a temporary list use during discovery, when discovery is started it is cleared, and would populate with the newly discovered accessories during discovery. For each newly discovered accessory it would be reported to application in real time. But applications can still query this list to get a summary of the whole discovery.
/**
* @brief Get accessory list
*
* @privilege com.amazon.acc.privilege.access
*
* @param[in] accessoryList Accessory list type
*
* @returns {AccessoryListType} Accessory list
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async getAccessoryList(
accessoryList: AccessoryListType
): Promise<Array<AccessoryBasicInfo>>;Each node in the list is reported as an AccessoryBasicInfo object.
Performing Accessory Info Querying
Permission "com.amazon.acc.privilege.access" is required before accessing the API and get corresponding callback.
The following API is provided to let the application query information of a specific accessory. Besides AccessoryBasicInfo, application can also query AccessoryExtraInfo by specifiying extraInfoKeys parameter.
/**
* @brief Get accessory information
*
* @privilege com.amazon.acc.privilege.access
*
* @param[in] accessoryIdentity Accessory identity to get information
* @param[in] needExtraInfo Boolean flag to get extra information, default as false.
* @param[in] extraInfoKeys Array of extra information keys, valid when `needExtra` is true.
*
* @returns {AccessoryInfo} Accessory information
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async getAccessoryInfo(
accessoryIdentity: string,
needExtraInfo?: boolean,
extraInfoKeys?: Array<string>,
): Promise<AccessoryInfo>The Api return AccessoryInfo which include AccessoryBasicInfo and AccessoryExtraInfo
/**
* @interface AccessoryInfo
*
* @description Accessory information. This is an aggregate of accessory information which
* can be retrived using `getAccessoryInfo()`.
*/
export interface AccessoryInfo {
/** Accessory information - basics */
basics: AccessoryBasicInfo;
/** Accessory information - extras */
extras?: AccessoryExtraInfo;
}
/**
* @interface AccessoryExtraInfo
*
* @description Accessory information - Extras. This is some extra information that maybe
* populated dynamically, some maybe populated after the accessory is paired/connected.
*
* If the application needs such information, it can call the `getAccessoryInfo()` and
* specifiy `extraInfoKeys` with the key of `AccessoryExtraInfo`. Since these information fileds
* may not be fully available at the time when `getAccessoryInfo()` is called, it is
* necessory to test the validity of the information fileds when using them.
*/
export interface AccessoryExtraInfo {
/** Accessory serial number string (e.g DSN) */
serial?: string;
/** Accessory version string (e.g Firmware version) */
version?: string;
/** Accessory Vendor ID */
vendorId?: number;
/** Accessory Product ID */
productId?: number;
/** Accessory SKU ID */
skuId?: number;
/** Accessory battery level, range 0 ~ 100 */
batteryLevel?: number;
/** Accessory pairing state */
pairingState?: AccessoryPairingState;
/** Accessory connection state */
connectionState?: AccessoryConnectionState;
/** Accessory mac address, string with format like DC:A0:D0:4C:21:38, not case-sensitive.
* It's presented in network byte order (big-endian). The underlying protocol shall ensure
* the consistency. Length could be different for different protocol. */
address?: string;
/** Accessory capabilities, indicate what service, functionality are supported */
capabilities?: AccessoryCapability[];
}Note that not all information fields are present at the time of query, some information are populated overtime dynamically. And some information maybe updated/notified from peer accessory dynamically. Actively call getAccessoryInfo and specify address to get Accessory mac address.
The application may implement the onAccessoryInfoUpdate callback to get dynamical updates on the Accessory Information fields.
/** Accessory Information Updated Callback */
onAccessoryInfoUpdate: (
basics: AccessoryBasicInfo,
extras: AccessoryExtraInfo
) => {
console.log("App - onAccessoryInfoUpdate - " + basics.identity);
setUpdatedAccessory(basics);
setUpdatedAccessoryExtra(extras);
},
Performing Accessory Configuration Querying
Permission "com.amazon.acc.privilege.restricted" is required before accessing the API.
The following API is provided to let the application query configuration of a specific accessory.
/**
* @brief Get accessory configuration
*
* @privilege com.amazon.acc.privilege.restricted
*
* @param[in] accessoryIdentity Accessory identity
*
* @returns {AccessoryConfigurationEntry} Accessory configuration
*
* @throws {AccClientErrors.NotSupportedError} Thrown when the configuration interface is not supported.
* @throws {AccClientErrors.NotFoundError} Thrown when there are no available configuration.
* @throws {AccClientErrors.TryAgainError} Thrown when the application need to try again.
*/
async getAccessoryConfiguration(
accessoryIdentity: string,
): Promise<AccessoryConfigurationEntry>The Api returns an instance of AccessoryConfigurationEntry which includes the configuration for AlexaFeature
/**
* @interface AccessoryConfigurationEntry
*
* @description Accessory configuration information. `AlexaFeature` is saved in it.
*/
export interface AccessoryConfigurationEntry {
/** Alexa feature configuration */
alexa?: AlexaFeature;
}
/**
* @interface AlexaFeature
*
* @description Alexa Feature configuration information.
*/
export interface AlexaFeature {
/** Alexa Icon type, indicate the Alexa icon style */
icon: AlexaIconType;
}AlexaFeature includes AlexaIconType
/**
* @enum AlexaIconType
*
* @description Alexa Icon type. Used to identify the Alexa Button style.
*/
export enum AlexaIconType {
/** White Mic icon on balck background */
MIC = 1,
/** White Circle icon on blue background */
ALEXA = 2,
/** White Mic icon on blue background */
ALEXAMIC = 3,
}Performing FireTV Remote Autopairing
The Apis are specially for VegaTV OOBE and lost Remote recovery scenario for FireTV parity. (e.g, if an previously paired Remote was broken, or lost pairing). The underlying logic in ACC will initiate Remote discovery and automatically pair with the Remote when a Remote is discovered.
Permission "com.amazon.acc.privilege.pair" is required before accessing auto pair APIs and get the corresponding callback.
/**
* @brief Start accessory autopair.
*
* @privilege com.amazon.acc.privilege.pair
*
* @param[in] protocol Accessory protocol to start autopair
* @param[in] category Accessory category
* @param[in] duration Accessory autopair duration in seconds, default to be 120 seconds if not
* specified by the caller.
*
* @remarks This is specially used to automatically discover and pair with Remote Controllers
* in TV use cases, only AccessoryProtocol.BLUTOOTH & AccessoryCategory.REMOTE_CONTROLLER
* is supported currently. It may be enhanced to support other use cases in future release.
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async startAccessoryAutopair(
protocol: AccessoryProtocol,
category: AccessoryCategory,
duration?: number
);
/**
* @brief Cancel accessory autopair
*
* @privilege com.amazon.acc.privilege.pair
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async cancelAccessoryAutopair(protocol: AccessoryProtocol);Callback onAccessoryAutopairStateChange defined as below function will be invoked to notify application the autopair state change.
/**
* @enum AccessoryAutopairState
*
* @description Accessory Auto Pair State
*/
export enum AccessoryAutopairState {
/** Unkown discovery state */
UNKNOWN = 0,
/** Autopair has been successfully started */
STARTED = 1,
/** Autopair has completed after specified timeout */
STOPPED = 2,
/** Autopair has been canceled */
ABORTED = 3,
}
/**
* @type Accessory Autopair State Changed Callback
*
* @privilege com.amazon.acc.privilege.pair
*
* @param[in] accessoryAutopairState Accessory autopair state
*/
export type AccessoryAutopairStateChangeHandler = (
accessoryAutopairState: AccessoryAutopairState,
) => void;Callback onAccessoryAutopair defined as below function will be invoked when a new Remote got paired.
/**
* @type Accessory Autopaired Callback
*
* @privilege com.amazon.acc.privilege.pair
*
* @param[in] accessoryBasicInfo Accessory basic information
*/
export type AccessoryAutopairHandler = (
accessoryBasicInfo: AccessoryBasicInfo,
) => void;Performing Accessory Firmware Upgrading
The Accessory Firmware Upgrading procedure is specially defined for Amazon FireTV Remote as of now.
Permission "com.amazon.acc.privilege.control" is required for startAccessoryDfu and onAccessoryDfuStateChange.
/**
* @brief start accessory DFU
*
* @privilege com.amazon.acc.privilege.control
*
* @param[in] accessoryIdentity Accessory identity to start DFU
* @param[in] dfuMode Accessory DFU Type to be triggered
*
* @throws Error of {AccessoryControlStatus} if not successful.
*/
async startAccessoryDfu(
accessoryIdentity: string,
dfuMode: AccessoryDfuMode,
);As per FireTV current design, two DFU mode are provided for application actively requesting:
- Forced DFU - During OOBE, after Remote paired OOBE calls the API to start DFU if remote DFU is needed. In fored DFU case, RDM starts DFU without showing DFU Dialog, instead OOBE will show the DFU in progress & status based on
onAccessoryDfuStateChange. - Manual DFU - When user navigates to Settings menu item for the Remote, user could see if it is updatable with new firmware version, and user could select to manually trigger a DFU update if it is updatable. Settings calls the API to initiate manual DFU procedure, in turn ACC calls RDM API, RDM starts the DFU procedure and shows DFU Dialog.
/**
* @enum AccessoryDfuMode
*
* @description Accessory DFU Modes.
*/
export enum AccessoryDfuMode {
/** Manual DFU - RDM internally check if it needs to do DFU and if so RDM itself pop up DFU UX */
MANUAL = 'accessory.dfumode.MANUAL',
/** Forced DFU - APP externally asks ACC/RDM to check if it needs to do DFU and if so APP pop up DFU UX */
FORCED = 'accessory.dfumode.FORCED',
}availableVersion in AccessoryExtraInfo is used to notify application (e.g VegaTvSettings) the available firmware version. A larger availableVersion than version indicate a new firmware is available.
When DFU is started, completed or failed, the onAccessoryDfuStateChange define as below function will be invoked to nofity application AccessoryDfuState.
/**
* @enum AccessoryDfuState
*
* @description Accessory DFU State
*/
export enum AccessoryDfuState {
/** Unkown DFU state */
UNKNOWN = 0,
/** Accessory DFU has been successfully done */
UPDATED = 1,
/** Accessory DFU has failed */
FAILED = 2,
/** Accessory DFU has been started */
STARTED = 3,
/** Accessory DFU has been aborted */
ABORTED = 4,
/** Accessory DFU has been rejected (from peer) */
REJECTED = 5,
/** Accessory DFU has been requested (from peer) */
REQUESTED = 6,
}
/**
* @type Accessory DFU State Changed Callback
*
* @privilege com.amazon.acc.privilege.control
*
* @param[in] accessoryBasicInfo Accessory basic information
* @param[in] accessoryDfuState Accessory DFU state
*/
export type AccessoryDfuStateChangeHandler = (
accessoryBasicInfo: AccessoryBasicInfo,
accessoryDfuState: AccessoryDfuState,
) => void;