Merge pull request #133 from petejohanson/bluetooth/ident-management
feat(bluetooth): Proper basic bond management, new `bt` behavior for resetting bond to host.
This commit is contained in:
commit
160f296bfb
24 changed files with 727 additions and 72 deletions
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
|
@ -17,6 +17,7 @@ jobs:
|
|||
- lily58_left
|
||||
- lily58_right
|
||||
- romac
|
||||
- settings_reset
|
||||
include:
|
||||
- board: proton_c
|
||||
shield: clueboard_california
|
||||
|
|
|
@ -29,10 +29,13 @@ target_sources(app PRIVATE src/hid.c)
|
|||
target_sources(app PRIVATE src/sensors.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_DISPLAY app PRIVATE src/display.c)
|
||||
target_sources(app PRIVATE src/event_manager.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble_unpair_combo.c)
|
||||
target_sources(app PRIVATE src/events/position_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/keycode_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/sensor_event.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
|
||||
if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
|
||||
|
@ -42,9 +45,10 @@ target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
|
|||
target_sources(app PRIVATE src/behaviors/behavior_none.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
|
||||
target_sources(app PRIVATE src/keymap.c)
|
||||
endif()
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/behaviors/behavior_bt.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble_unpair_combo.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL app PRIVATE src/split_listener.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL app PRIVATE src/split/bluetooth/service.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL app PRIVATE src/split/bluetooth/central.c)
|
||||
|
|
34
app/Kconfig
34
app/Kconfig
|
@ -37,11 +37,12 @@ menuconfig ZMK_BLE
|
|||
select BT
|
||||
select BT_SMP
|
||||
select BT_SMP_SC_PAIR_ONLY
|
||||
select BT_SMP_APP_PAIRING_ACCEPT
|
||||
select BT_PERIPHERAL
|
||||
select BT_GATT_DIS
|
||||
select BT_GATT_BAS
|
||||
select BT_SETTINGS
|
||||
select SETTINGS
|
||||
# select BT_SETTINGS
|
||||
|
||||
if ZMK_BLE
|
||||
|
||||
|
@ -52,6 +53,10 @@ config ZMK_BLE_INIT_PRIORITY
|
|||
config SYSTEM_WORKQUEUE_STACK_SIZE
|
||||
default 2048
|
||||
|
||||
config ZMK_BLE_CLEAR_BONDS_ON_START
|
||||
bool "Configuration that clears all bond information from the keyboard on startup."
|
||||
default n
|
||||
|
||||
# HID GATT notifications sent this way are *not* picked up by Linux, and possibly others.
|
||||
config BT_GATT_NOTIFY_MULTIPLE
|
||||
default n
|
||||
|
@ -101,28 +106,20 @@ config ZMK_SPLIT_BLE_ROLE_CENTRAL
|
|||
select BT_CENTRAL
|
||||
select BT_GATT_CLIENT
|
||||
|
||||
if ZMK_SPLIT_BLE_ROLE_CENTRAL
|
||||
|
||||
config BT_MAX_CONN
|
||||
default 5
|
||||
|
||||
config BT_MAX_PAIRED
|
||||
# Bump this everywhere once we support switching active connections!
|
||||
default 2
|
||||
|
||||
endif
|
||||
|
||||
config ZMK_SPLIT_BLE_ROLE_PERIPHERAL
|
||||
bool "Peripheral"
|
||||
select BT_KEYS_OVERWRITE_OLDEST
|
||||
|
||||
if ZMK_SPLIT_BLE_ROLE_PERIPHERAL
|
||||
|
||||
config ZMK_USB
|
||||
default n
|
||||
|
||||
config BT_MAX_PAIRED
|
||||
default 1
|
||||
|
||||
config BT_MAX_CONN
|
||||
default 5
|
||||
default 1
|
||||
|
||||
config BT_GAP_AUTO_UPDATE_CONN_PARAMS
|
||||
default n
|
||||
|
@ -135,8 +132,17 @@ endif
|
|||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
if ZMK_BLE && (!ZMK_SPLIT_BLE || ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
|
||||
config BT_MAX_CONN
|
||||
default 6
|
||||
|
||||
config BT_MAX_PAIRED
|
||||
default 5
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
||||
config ZMK_KSCAN_MOCK_DRIVER
|
||||
bool "Enable mock kscan driver to simulate key presses"
|
||||
|
|
10
app/boards/shields/settings_reset/Kconfig.defconfig
Normal file
10
app/boards/shields/settings_reset/Kconfig.defconfig
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
if SHIELD_SETTINGS_RESET
|
||||
|
||||
config ZMK_KEYBOARD_NAME
|
||||
default "SETTINGS RESET"
|
||||
|
||||
endif
|
||||
|
5
app/boards/shields/settings_reset/Kconfig.shield
Normal file
5
app/boards/shields/settings_reset/Kconfig.shield
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
config SHIELD_SETTINGS_RESET
|
||||
def_bool $(shields_list_contains,settings_reset)
|
1
app/boards/shields/settings_reset/settings_reset.conf
Normal file
1
app/boards/shields/settings_reset/settings_reset.conf
Normal file
|
@ -0,0 +1 @@
|
|||
CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START=y
|
22
app/boards/shields/settings_reset/settings_reset.keymap
Normal file
22
app/boards/shields/settings_reset/settings_reset.keymap
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&reset
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
24
app/boards/shields/settings_reset/settings_reset.overlay
Normal file
24
app/boards/shields/settings_reset/settings_reset.overlay
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/matrix-transform.h>
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zmk,kscan = &kscan0;
|
||||
};
|
||||
|
||||
kscan0: kscan {
|
||||
compatible = "zmk,kscan-gpio-direct";
|
||||
label = "KSCAN";
|
||||
|
||||
input-gpios
|
||||
= <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
;
|
||||
};
|
||||
|
||||
};
|
||||
|
|
@ -8,3 +8,4 @@
|
|||
#include <behaviors/reset.dtsi>
|
||||
#include <behaviors/sensor_rotate_key_press.dtsi>
|
||||
#include <behaviors/rgb_underglow.dtsi>
|
||||
#include <behaviors/bluetooth.dtsi>
|
9
app/dts/behaviors/bluetooth.dtsi
Normal file
9
app/dts/behaviors/bluetooth.dtsi
Normal file
|
@ -0,0 +1,9 @@
|
|||
/ {
|
||||
behaviors {
|
||||
bt: behavior_bluetooth {
|
||||
compatible = "zmk,behavior-bluetooth";
|
||||
label = "BLUETOOTH";
|
||||
#binding-cells = <2>;
|
||||
};
|
||||
};
|
||||
};
|
8
app/dts/bindings/behaviors/zmk,behavior-bluetooth.yaml
Normal file
8
app/dts/bindings/behaviors/zmk,behavior-bluetooth.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2020, Peter Johanson
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Bluetooth Behavior
|
||||
|
||||
compatible: "zmk,behavior-bluetooth"
|
||||
|
||||
include: two_param.yaml
|
21
app/include/dt-bindings/zmk/bt.h
Normal file
21
app/include/dt-bindings/zmk/bt.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define BT_CLR_CMD 0
|
||||
#define BT_NXT_CMD 1
|
||||
#define BT_PRV_CMD 2
|
||||
#define BT_SEL_CMD 3
|
||||
// #define BT_FULL_RESET_CMD 4
|
||||
|
||||
/*
|
||||
Note: Some future commands will include additional parameters, so we
|
||||
defines these aliases up front.
|
||||
*/
|
||||
|
||||
#define BT_CLR BT_CLR_CMD 0
|
||||
#define BT_NXT BT_NXT_CMD 0
|
||||
#define BT_PRV BT_PRV_CMD 0
|
||||
#define BT_SEL BT_SEL_CMD
|
|
@ -7,6 +7,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <zmk/keys.h>
|
||||
#include <zmk/ble/profile.h>
|
||||
|
||||
int zmk_ble_clear_bonds();
|
||||
int zmk_ble_prof_next();
|
||||
int zmk_ble_prof_prev();
|
||||
int zmk_ble_prof_select(u8_t index);
|
||||
|
||||
bt_addr_le_t *zmk_ble_active_profile_addr();
|
||||
char *zmk_ble_active_profile_name();
|
||||
|
||||
int zmk_ble_unpair_all();
|
||||
bool zmk_ble_handle_key_user(struct zmk_key_event *key_event);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
void zmk_ble_set_peripheral_addr(bt_addr_le_t *addr);
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
|
16
app/include/zmk/ble/profile.h
Normal file
16
app/include/zmk/ble/profile.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bluetooth/addr.h>
|
||||
|
||||
#define ZMK_BLE_PROFILE_NAME_MAX 15
|
||||
|
||||
struct zmk_ble_profile {
|
||||
char name[ZMK_BLE_PROFILE_NAME_MAX];
|
||||
bt_addr_le_t peer;
|
||||
};
|
22
app/include/zmk/events/ble-active-profile-changed.h
Normal file
22
app/include/zmk/events/ble-active-profile-changed.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <zmk/event-manager.h>
|
||||
#include <device.h>
|
||||
|
||||
#include <zmk/ble/profile.h>
|
||||
|
||||
|
||||
struct ble_active_profile_changed {
|
||||
struct zmk_event_header header;
|
||||
u8_t index;
|
||||
struct zmk_ble_profile *profile;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(ble_active_profile_changed);
|
60
app/src/behaviors/behavior_bt.c
Normal file
60
app/src/behaviors/behavior_bt.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_bluetooth
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/behavior.h>
|
||||
|
||||
#include <dt-bindings/zmk/bt.h>
|
||||
|
||||
#include <bluetooth/conn.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/ble.h>
|
||||
|
||||
static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t command, u32_t arg)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case BT_CLR_CMD:
|
||||
return zmk_ble_clear_bonds();
|
||||
case BT_NXT_CMD:
|
||||
return zmk_ble_prof_next();
|
||||
case BT_PRV_CMD:
|
||||
return zmk_ble_prof_prev();
|
||||
case BT_SEL_CMD:
|
||||
return zmk_ble_prof_select(arg);
|
||||
default:
|
||||
LOG_ERR("Unknown BT command: %d", command);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int behavior_bt_init(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t command, u32_t arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_bt_driver_api = {
|
||||
.binding_pressed = on_keymap_binding_pressed,
|
||||
.binding_released = on_keymap_binding_released,
|
||||
};
|
||||
|
||||
DEVICE_AND_API_INIT(behavior_bt, DT_INST_LABEL(0),
|
||||
behavior_bt_init,
|
||||
NULL,
|
||||
NULL,
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&behavior_bt_driver_api);
|
348
app/src/ble.c
348
app/src/ble.c
|
@ -8,6 +8,8 @@
|
|||
#include <init.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <settings/settings.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
@ -15,33 +17,245 @@
|
|||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/uuid.h>
|
||||
#include <bluetooth/gatt.h>
|
||||
#include <bluetooth/hci_err.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
|
||||
#include <settings/settings.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/ble.h>
|
||||
#include <zmk/keys.h>
|
||||
#include <zmk/split/bluetooth/uuid.h>
|
||||
#include <zmk/event-manager.h>
|
||||
#include <zmk/events/ble-active-profile-changed.h>
|
||||
|
||||
static struct bt_conn *auth_passkey_entry_conn;
|
||||
static u8_t passkey_entries[6] = {0, 0, 0, 0, 0, 0};
|
||||
static u8_t passkey_digit = 0;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
#define ZMK_ADV_PARAMS BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \
|
||||
BT_LE_ADV_OPT_USE_NAME | \
|
||||
BT_LE_ADV_OPT_ONE_TIME, \
|
||||
BT_GAP_ADV_FAST_INT_MIN_2, \
|
||||
BT_GAP_ADV_FAST_INT_MAX_2, NULL)
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
#define PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
|
||||
#else
|
||||
#define ZMK_ADV_PARAMS BT_LE_ADV_CONN_NAME
|
||||
#define PROFILE_COUNT CONFIG_BT_MAX_PAIRED
|
||||
#endif
|
||||
|
||||
|
||||
static struct zmk_ble_profile profiles[PROFILE_COUNT];
|
||||
static u8_t active_profile;
|
||||
|
||||
static const struct bt_data zmk_ble_ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA_BYTES(BT_DATA_UUID16_SOME,
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
0x12, 0x18, /* HID Service */
|
||||
#endif
|
||||
0x0f, 0x18 /* Battery Service */
|
||||
),
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL,
|
||||
ZMK_SPLIT_BT_SERVICE_UUID)
|
||||
#endif
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
|
||||
static bt_addr_le_t peripheral_addr;
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
|
||||
|
||||
static void raise_profile_changed_event()
|
||||
{
|
||||
struct ble_active_profile_changed *ev = new_ble_active_profile_changed();
|
||||
ev->index = active_profile;
|
||||
ev->profile = &profiles[active_profile];
|
||||
|
||||
ZMK_EVENT_RAISE(ev);
|
||||
}
|
||||
|
||||
static bool active_profile_is_open()
|
||||
{
|
||||
return !bt_addr_le_cmp(&profiles[active_profile].peer, BT_ADDR_LE_ANY);
|
||||
}
|
||||
|
||||
void set_profile_address(u8_t index, const bt_addr_le_t *addr)
|
||||
{
|
||||
char setting_name[15];
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
|
||||
|
||||
memcpy(&profiles[index].peer, addr, sizeof(bt_addr_le_t));
|
||||
sprintf(setting_name, "ble/profiles/%d", index);
|
||||
LOG_DBG("Setting profile addr for %s to %s", log_strdup(setting_name), log_strdup(addr_str));
|
||||
settings_save_one(setting_name, &profiles[index], sizeof(struct zmk_ble_profile));
|
||||
raise_profile_changed_event();
|
||||
}
|
||||
|
||||
int zmk_ble_adv_pause()
|
||||
{
|
||||
int err = bt_le_adv_stop();
|
||||
if (err) {
|
||||
LOG_ERR("Failed to stop advertising (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int zmk_ble_adv_resume()
|
||||
{
|
||||
LOG_DBG("active_profile %d, directed? %s", active_profile, active_profile_is_open() ? "no" : "yes");
|
||||
|
||||
int err = bt_le_adv_start(
|
||||
BT_LE_ADV_CONN_NAME,
|
||||
zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad),
|
||||
NULL, 0);
|
||||
if (err)
|
||||
{
|
||||
LOG_ERR("Advertising failed to start (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int zmk_ble_clear_bonds()
|
||||
{
|
||||
LOG_DBG("");
|
||||
|
||||
if (bt_addr_le_cmp(&profiles[active_profile].peer, BT_ADDR_LE_ANY)) {
|
||||
LOG_DBG("Unpairing!");
|
||||
bt_unpair(BT_ID_DEFAULT, &profiles[active_profile].peer);
|
||||
set_profile_address(active_profile, BT_ADDR_LE_ANY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int zmk_ble_prof_select(u8_t index)
|
||||
{
|
||||
LOG_DBG("profile %d", index);
|
||||
if (active_profile == index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
active_profile = index;
|
||||
return settings_save_one("ble/active_profile", &active_profile, sizeof(active_profile));
|
||||
|
||||
raise_profile_changed_event();
|
||||
};
|
||||
|
||||
int zmk_ble_prof_next()
|
||||
{
|
||||
LOG_DBG("");
|
||||
return zmk_ble_prof_select((active_profile + 1) % PROFILE_COUNT);
|
||||
};
|
||||
|
||||
int zmk_ble_prof_prev()
|
||||
{
|
||||
LOG_DBG("");
|
||||
return zmk_ble_prof_select((active_profile + PROFILE_COUNT - 1) % PROFILE_COUNT);
|
||||
};
|
||||
|
||||
bt_addr_le_t *zmk_ble_active_profile_addr()
|
||||
{
|
||||
return &profiles[active_profile].peer;
|
||||
}
|
||||
|
||||
char *zmk_ble_active_profile_name()
|
||||
{
|
||||
return profiles[active_profile].name;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
|
||||
void zmk_ble_set_peripheral_addr(bt_addr_le_t *addr)
|
||||
{
|
||||
memcpy(&peripheral_addr, addr, sizeof(bt_addr_le_t));
|
||||
settings_save_one("ble/peripheral_address", addr, sizeof(bt_addr_le_t));
|
||||
}
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
|
||||
static int ble_profiles_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg)
|
||||
{
|
||||
const char *next;
|
||||
|
||||
LOG_DBG("Setting BLE value %s", log_strdup(name));
|
||||
|
||||
if (settings_name_steq(name, "profiles", &next) && next) {
|
||||
char *endptr;
|
||||
u8_t idx = strtoul(next, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
LOG_WRN("Invalid profile index: %s", log_strdup(next));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (len != sizeof(struct zmk_ble_profile)) {
|
||||
LOG_ERR("Invalid profile size (got %d expected %d)", len, sizeof(struct zmk_ble_profile));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (idx >= PROFILE_COUNT) {
|
||||
LOG_WRN("Profile address for index %d is larger than max of %d", idx, PROFILE_COUNT);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int err = read_cb(cb_arg, &profiles[idx], sizeof(struct zmk_ble_profile));
|
||||
if (err <= 0) {
|
||||
LOG_ERR("Failed to handle profile address from settings (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
bt_addr_le_to_str(&profiles[idx].peer, addr_str, sizeof(addr_str));
|
||||
|
||||
LOG_DBG("Loaded %s address for profile %d", log_strdup(addr_str), idx);
|
||||
} else if (settings_name_steq(name, "active_profile", &next) && !next) {
|
||||
if (len != sizeof(active_profile)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int err = read_cb(cb_arg, &active_profile, sizeof(active_profile));
|
||||
if (err <= 0) {
|
||||
LOG_ERR("Failed to handle active profile from settings (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
else if (settings_name_steq(name, "peripheral_address", &next) && !next) {
|
||||
if (len != sizeof(bt_addr_le_t)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int err = read_cb(cb_arg, &peripheral_addr, sizeof(bt_addr_le_t));
|
||||
if (err <= 0) {
|
||||
LOG_ERR("Failed to handle peripheral address from settings (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
struct settings_handler profiles_handler = {
|
||||
.name = "ble",
|
||||
.h_set = ble_profiles_handle_set
|
||||
};
|
||||
#endif /* IS_ENABLED(CONFIG_SETTINGS) */
|
||||
|
||||
static void connected(struct bt_conn *conn, u8_t err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (err)
|
||||
|
@ -71,6 +285,14 @@ static void disconnected(struct bt_conn *conn, u8_t reason)
|
|||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
LOG_DBG("Disconnected from %s (reason 0x%02x)", log_strdup(addr), reason);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
// if (bt_addr_le_cmp(&peripheral_addr, BT_ADDR_LE_ANY) && bt_addr_le_cmp(&peripheral_addr, bt_conn_get_dst(conn))) {
|
||||
// zmk_ble_adv_resume();
|
||||
// }
|
||||
#else
|
||||
// zmk_ble_adv_resume();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void security_changed(struct bt_conn *conn, bt_security_t level,
|
||||
|
@ -137,7 +359,52 @@ static void auth_cancel(struct bt_conn *conn)
|
|||
LOG_DBG("Pairing cancelled: %s", log_strdup(addr));
|
||||
}
|
||||
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
static enum bt_security_err auth_pairing_accept(struct bt_conn *conn, const struct bt_conn_pairing_feat *const feat)
|
||||
{
|
||||
struct bt_conn_info info;
|
||||
bt_conn_get_info(conn, &info);
|
||||
|
||||
LOG_DBG("role %d, open? %s", info.role, active_profile_is_open() ? "yes" : "no");
|
||||
if (info.role == BT_CONN_ROLE_SLAVE && !active_profile_is_open()) {
|
||||
LOG_WRN("Rejecting pairing request to taken profile %d", active_profile);
|
||||
return BT_SECURITY_ERR_PAIR_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
return BT_SECURITY_ERR_SUCCESS;
|
||||
};
|
||||
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */
|
||||
|
||||
static void auth_pairing_complete(struct bt_conn *conn, bool bonded)
|
||||
{
|
||||
struct bt_conn_info info;
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
const bt_addr_le_t *dst = bt_conn_get_dst(conn);
|
||||
|
||||
bt_addr_le_to_str(dst, addr, sizeof(addr));
|
||||
bt_conn_get_info(conn, &info);
|
||||
|
||||
if (info.role != BT_CONN_ROLE_SLAVE) {
|
||||
LOG_DBG("SKIPPING FOR ROLE %d", info.role);
|
||||
return;
|
||||
}
|
||||
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
if (!active_profile_is_open()) {
|
||||
LOG_ERR("Pairing completed but current profile is not open: %s", log_strdup(addr));
|
||||
bt_unpair(BT_ID_DEFAULT, dst);
|
||||
return;
|
||||
}
|
||||
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */
|
||||
|
||||
set_profile_address(active_profile, dst);
|
||||
};
|
||||
|
||||
static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
.pairing_accept = auth_pairing_accept,
|
||||
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */
|
||||
.pairing_complete = auth_pairing_complete,
|
||||
// .passkey_display = auth_passkey_display,
|
||||
|
||||
#ifdef CONFIG_ZMK_BLE_PASSKEY_ENTRY
|
||||
|
@ -146,19 +413,6 @@ static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
|
|||
.cancel = auth_cancel,
|
||||
};
|
||||
|
||||
static const struct bt_data zmk_ble_ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA_BYTES(BT_DATA_UUID16_SOME,
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
0x12, 0x18, /* HID Service */
|
||||
#endif
|
||||
0x0f, 0x18 /* Battery Service */
|
||||
),
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL,
|
||||
ZMK_SPLIT_BT_SERVICE_UUID)
|
||||
#endif
|
||||
};
|
||||
|
||||
static void zmk_ble_ready(int err)
|
||||
{
|
||||
|
@ -169,12 +423,7 @@ static void zmk_ble_ready(int err)
|
|||
return;
|
||||
}
|
||||
|
||||
err = bt_le_adv_start(ZMK_ADV_PARAMS, zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), NULL, 0);
|
||||
if (err)
|
||||
{
|
||||
LOG_ERR("Advertising failed to start (err %d)", err);
|
||||
return;
|
||||
}
|
||||
zmk_ble_adv_resume();
|
||||
}
|
||||
|
||||
static int zmk_ble_init(struct device *_arg)
|
||||
|
@ -187,11 +436,37 @@ static int zmk_ble_init(struct device *_arg)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS))
|
||||
{
|
||||
settings_load();
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
settings_subsys_init();
|
||||
|
||||
err = settings_register(&profiles_handler);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to setup the profile settings handler (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
settings_load();
|
||||
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START)
|
||||
LOG_WRN("Clearing all existing BLE bond information from the keyboard");
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
bt_unpair(i, NULL);
|
||||
}
|
||||
|
||||
for (int i = 0; i < PROFILE_COUNT; i++) {
|
||||
char setting_name[15];
|
||||
sprintf(setting_name, "ble/profiles/%d", i);
|
||||
|
||||
err = settings_delete(setting_name);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to delete setting: %d", err);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bt_conn_cb_register(&conn_callbacks);
|
||||
bt_conn_auth_cb_register(&zmk_ble_auth_cb_display);
|
||||
|
||||
|
@ -202,8 +477,17 @@ static int zmk_ble_init(struct device *_arg)
|
|||
|
||||
int zmk_ble_unpair_all()
|
||||
{
|
||||
LOG_DBG("");
|
||||
return bt_unpair(BT_ID_DEFAULT, NULL);
|
||||
int resp = 0;
|
||||
for (int i = BT_ID_DEFAULT; i < CONFIG_BT_ID_MAX; i++) {
|
||||
|
||||
int err = bt_unpair(BT_ID_DEFAULT, NULL);
|
||||
if (err) {
|
||||
resp = err;
|
||||
LOG_ERR("Failed to unpair devices (err %d)", err);
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
bool zmk_ble_handle_key_user(struct zmk_key_event *key_event)
|
||||
|
|
10
app/src/events/ble_active_profile_changed.c
Normal file
10
app/src/events/ble_active_profile_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <zmk/events/ble-active-profile-changed.h>
|
||||
|
||||
ZMK_EVENT_IMPL(ble_active_profile_changed);
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
#include <settings/settings.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/gatt.h>
|
||||
|
||||
|
@ -148,12 +152,40 @@ BT_GATT_SERVICE_DEFINE(hog_svc,
|
|||
BT_GATT_PERM_WRITE,
|
||||
NULL, write_ctrl_point, &ctrl_point));
|
||||
|
||||
struct bt_conn *destination_connection() {
|
||||
struct bt_conn *conn;
|
||||
bt_addr_le_t *addr = zmk_ble_active_profile_addr();
|
||||
LOG_DBG("Address pointer %p", addr);
|
||||
if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
|
||||
LOG_WRN("Not sending, no active address for current profile");
|
||||
return NULL;
|
||||
} else if ((conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr)) == NULL) {
|
||||
LOG_WRN("Not sending, not connected to active profile");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conn;
|
||||
|
||||
}
|
||||
|
||||
int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *report)
|
||||
{
|
||||
return bt_gatt_notify(NULL, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_keypad_report_body));
|
||||
struct bt_conn *conn = destination_connection();
|
||||
if (conn == NULL) {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
LOG_DBG("Sending to NULL? %s", conn == NULL ? "yes" : "no");
|
||||
|
||||
return bt_gatt_notify(conn, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_keypad_report_body));
|
||||
};
|
||||
|
||||
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report)
|
||||
{
|
||||
return bt_gatt_notify(NULL, &hog_svc.attrs[10], report, sizeof(struct zmk_hid_consumer_report_body));
|
||||
struct bt_conn *conn = destination_connection();
|
||||
if (conn == NULL) {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
return bt_gatt_notify(conn, &hog_svc.attrs[10], report, sizeof(struct zmk_hid_consumer_report_body));
|
||||
};
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
#include <bluetooth/conn.h>
|
||||
#include <bluetooth/uuid.h>
|
||||
#include <bluetooth/gatt.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <sys/byteorder.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/ble.h>
|
||||
#include <zmk/split/bluetooth/uuid.h>
|
||||
#include <zmk/event-manager.h>
|
||||
#include <zmk/events/position-state-changed.h>
|
||||
|
@ -71,6 +73,27 @@ static u8_t split_central_notify_func(struct bt_conn *conn,
|
|||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static int split_central_subscribe(struct bt_conn *conn)
|
||||
{
|
||||
int err = bt_gatt_subscribe(conn, &subscribe_params);
|
||||
switch (err) {
|
||||
case -EALREADY:
|
||||
LOG_DBG("[ALREADY SUBSCRIBED]");
|
||||
break;
|
||||
// break;
|
||||
// bt_gatt_unsubscribe(conn, &subscribe_params);
|
||||
// return split_central_subscribe(conn);
|
||||
case 0:
|
||||
LOG_DBG("[SUBSCRIBED]");
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Subscribe failed (err %d)", err);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8_t split_central_discovery_func(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr,
|
||||
struct bt_gatt_discover_params *params)
|
||||
|
@ -112,12 +135,7 @@ static u8_t split_central_discovery_func(struct bt_conn *conn,
|
|||
subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
subscribe_params.ccc_handle = attr->handle;
|
||||
|
||||
err = bt_gatt_subscribe(conn, &subscribe_params);
|
||||
if (err && err != -EALREADY) {
|
||||
LOG_ERR("Subscribe failed (err %d)", err);
|
||||
} else {
|
||||
LOG_DBG("[SUBSCRIBED]");
|
||||
}
|
||||
split_central_subscribe(conn);
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
@ -136,7 +154,7 @@ static void split_central_process_connection(struct bt_conn *conn) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (conn == default_conn) {
|
||||
if (conn == default_conn && !subscribe_params.value) {
|
||||
discover_params.uuid = &uuid.uuid;
|
||||
discover_params.func = split_central_discovery_func;
|
||||
discover_params.start_handle = 0x0001;
|
||||
|
@ -194,6 +212,8 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data)
|
|||
|
||||
LOG_DBG("Found the split service");
|
||||
|
||||
zmk_ble_set_peripheral_addr(addr);
|
||||
|
||||
err = bt_le_scan_stop();
|
||||
if (err) {
|
||||
LOG_ERR("Stop LE scan failed (err %d)", err);
|
||||
|
@ -206,10 +226,11 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data)
|
|||
split_central_process_connection(default_conn);
|
||||
} else {
|
||||
param = BT_LE_CONN_PARAM(0x0006, 0x0006, 30, 400);
|
||||
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
|
||||
param, &default_conn);
|
||||
if (err) {
|
||||
LOG_ERR("Create conn failed (err %d)", err);
|
||||
LOG_ERR("Create conn failed (err %d) (create conn? 0x%04x)", err, BT_HCI_OP_LE_CREATE_CONN);
|
||||
start_scan();
|
||||
}
|
||||
|
||||
|
@ -263,8 +284,9 @@ static void split_central_connected(struct bt_conn *conn, u8_t conn_err)
|
|||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
|
||||
if (conn_err) {
|
||||
LOG_ERR("Failed to connect to %s (%u)", addr, conn_err);
|
||||
LOG_ERR("Failed to connect to %s (%u)", log_strdup(addr), conn_err);
|
||||
|
||||
bt_conn_unref(default_conn);
|
||||
default_conn = NULL;
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
#include <zephyr/types.h>
|
||||
#include <sys/util.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <bluetooth/gatt.h>
|
||||
#include <bluetooth/uuid.h>
|
||||
|
||||
|
@ -28,6 +33,7 @@ static ssize_t split_svc_num_of_positions(struct bt_conn *conn, const struct bt_
|
|||
|
||||
static void split_svc_pos_state_ccc(const struct bt_gatt_attr *attr, u16_t value)
|
||||
{
|
||||
LOG_DBG("value %d", value);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -21,12 +21,13 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
|
||||
int split_listener(const struct zmk_event_header *eh)
|
||||
{
|
||||
LOG_DBG("");
|
||||
if (is_position_state_changed(eh)) {
|
||||
const struct position_state_changed *ev = cast_position_state_changed(eh);
|
||||
if (ev->state) {
|
||||
zmk_split_bt_position_pressed(ev->position);
|
||||
return zmk_split_bt_position_pressed(ev->position);
|
||||
} else {
|
||||
zmk_split_bt_position_released(ev->position);
|
||||
return zmk_split_bt_position_released(ev->position);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
76
docs/docs/behavior/bluetooth.md
Normal file
76
docs/docs/behavior/bluetooth.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
title: Bluetooth Behavior
|
||||
sidebar_label: Bluetooth
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The bluetooth behavior allows management of various settings and states related to the bluetooth connection(s)
|
||||
between the keyboard and the host. By default, ZMK supports five "profiles" for selecting which bonded host
|
||||
computer/laptop/keyboard should receive the keyboard input; many of the commands here operation on those profiles.
|
||||
|
||||
## Bluetooth Command Defines
|
||||
|
||||
Bluetooth command defines are provided through the [`dt-bindings/zmk/bt.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/bt.h) header,
|
||||
which is added at the top of the keymap file:
|
||||
|
||||
```
|
||||
#include <dt-bindings/zmk/bt.h>
|
||||
```
|
||||
|
||||
This will allow you to reference the actions defined in this header such as `BT_CLR_CMD`.
|
||||
|
||||
Here is a table describing the command for each define:
|
||||
|
||||
| Define | Action |
|
||||
| ------------ | ---------------------------------------------------------------------------------------------- |
|
||||
| `BT_CLR_CMD` | Clear bond information between the keyboard and host for the selected profile [^1] |
|
||||
| `BT_NXT_CMD` | Switch to the next profile, cycling through to the first one when the end is reached. |
|
||||
| `BT_PRV_CMD` | Switch to the previous profile, cycling through to the last one when the beginning is reached. |
|
||||
| `BT_SEL_CMD` | Select the 0-indexed profile by number. |
|
||||
|
||||
Because at least one bluetooth commands takes an additional parameter, it is recommended to use
|
||||
the following aliases in your keymap to avoid having to specify an ignored second parameter:
|
||||
|
||||
| Define | Action |
|
||||
| -------- | -------------------------------------------------------------------------------- |
|
||||
| `BT_CLR` | Alias for `BT_CLR_CMD 0` to clear the current profile's bond to the current host |
|
||||
| `BT_NXT` | Alias for `BT_NXT_CMD 0` to select the next profile |
|
||||
| `BT_PRV` | Alias for `BT_PRV_CMD 0` to select the previous profile |
|
||||
| `BT_SEL` | Alias for `BT_SEL_CMD` to select the given profile, e.g. `&bt BT_SEL 1` |
|
||||
|
||||
## Bluetooth Behavior
|
||||
|
||||
The bluetooth behavior completes an bluetooth action given on press.
|
||||
|
||||
### Behavior Binding
|
||||
|
||||
- Reference: `&bt`
|
||||
- Parameter #1: The bluetooth command define, e.g. `BT_CLR_CMD`
|
||||
- Parameter #2: (Reserved for future bluetooth command types)
|
||||
|
||||
### Examples
|
||||
|
||||
1. Behavior binding to clear the paired host for the selected profile:
|
||||
|
||||
```
|
||||
&bt BT_CLR
|
||||
```
|
||||
|
||||
1. Behavior binding to select the next profile:
|
||||
|
||||
```
|
||||
&bt BT_NXT
|
||||
```
|
||||
|
||||
1. Behavior binding to select the previous profile:
|
||||
|
||||
```
|
||||
&bt BT_NXT
|
||||
```
|
||||
|
||||
1. Behavior binding to select the 2nd profile (passed parameters are [zero based](https://en.wikipedia.org/wiki/Zero-based_numbering)):
|
||||
|
||||
```
|
||||
&bt BT_SEL 1
|
||||
```
|
|
@ -22,6 +22,7 @@ module.exports = {
|
|||
"behavior/hold-tap",
|
||||
"behavior/mod-tap",
|
||||
"behavior/reset",
|
||||
"behavior/bluetooth",
|
||||
"behavior/lighting",
|
||||
],
|
||||
Development: [
|
||||
|
|
Loading…
Add table
Reference in a new issue