From 4c38b6d7429244e260e534202c112751f4140ea6 Mon Sep 17 00:00:00 2001 From: Rudd-O Date: Sun, 12 Mar 2023 20:00:11 +0000 Subject: [PATCH 01/15] Add logging for start / stop / advertise on disconnect. --- components/ble_keyboard/ble_keyboard.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/ble_keyboard/ble_keyboard.cpp b/components/ble_keyboard/ble_keyboard.cpp index 178d316..68ebddc 100644 --- a/components/ble_keyboard/ble_keyboard.cpp +++ b/components/ble_keyboard/ble_keyboard.cpp @@ -21,13 +21,16 @@ void Esp32BleKeyboard::setup() { pServer = BLEDevice::getServer(); + ESP_LOGI("ble", "advertiseOnDisconnect(%s)", this->reconnect_ ? "true" : "false"); pServer->advertiseOnDisconnect(this->reconnect_); bleKeyboard.releaseAll(); } void Esp32BleKeyboard::stop() { + ESP_LOGI("ble", "stop()"); if (this->reconnect_) { + ESP_LOGI("ble", "advertiseOnDisconnect(false)"); pServer->advertiseOnDisconnect(false); } @@ -43,7 +46,9 @@ void Esp32BleKeyboard::stop() { } void Esp32BleKeyboard::start() { + ESP_LOGI("ble", "start()"); if (this->reconnect_) { + ESP_LOGI("ble", "advertiseOnDisconnect(true)"); pServer->advertiseOnDisconnect(true); } @@ -111,4 +116,4 @@ void Esp32BleKeyboard::release() { } // namespace ble_keyboard } // namespace esphome -#endif \ No newline at end of file +#endif From 483880a906bf363145436127447ada77d465b53e Mon Sep 17 00:00:00 2001 From: "Manuel Amador (Rudd-O)" Date: Sun, 12 Mar 2023 20:23:50 +0000 Subject: [PATCH 02/15] Add advertise on start option. --- README.md | 2 ++ components/ble_keyboard/__init__.py | 3 +++ components/ble_keyboard/ble_keyboard.cpp | 15 ++++++++++----- components/ble_keyboard/ble_keyboard.h | 4 +++- components/ble_keyboard/const.py | 1 + 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e7d7953..9c876dd 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ ble_keyboard: manufacturer_id: "MamonTech" battery_level: 50 reconnect: true + advertise_on_start: true buttons: true use_default_libs: false ``` @@ -73,6 +74,7 @@ ble_keyboard: * **manufacturer_id** (Optional, string): Keyboard manufacturer (default: Esp32BleKeyboard); * **battery_level** (Optional, int): Keyboard battery level (default: 100); * **reconnect** (Optional, bool): Automatic reconnect service after disconnecting the device. (default: true); +* **advertise_on_start** (Optional, bool): Automatic advertisement when the ESP device starts. (default: true); * **buttons** (Optional, bool): Whether to add separate buttons for [keys](https://github.com/dmamontov/esphome-blekeyboard/wiki/Keys) (default: true); * **use_default_libs** (Optional, bool): Whether to use the arduino standard library. (default: false). diff --git a/components/ble_keyboard/__init__.py b/components/ble_keyboard/__init__.py index 2e79757..8ff8419 100644 --- a/components/ble_keyboard/__init__.py +++ b/components/ble_keyboard/__init__.py @@ -42,6 +42,7 @@ CONF_BUTTONS, CONF_KEYS, CONF_RECONNECT, + CONF_ADVERTISE_ON_START, CONF_TEXT, CONF_USE_DEFAULT_LIBS, DOMAIN, @@ -66,6 +67,7 @@ cv.Optional(CONF_MANUFACTURER_ID, default=COMPONENT_CLASS): cv.Length(min=1), cv.Optional(CONF_BATTERY_LEVEL, default=100): cv.int_range(min=0, max=100), cv.Optional(CONF_RECONNECT, default=True): cv.boolean, + cv.Optional(CONF_ADVERTISE_ON_START, default=True): cv.boolean, cv.Optional(CONF_USE_DEFAULT_LIBS, default=False): cv.boolean, cv.Optional(CONF_BUTTONS, default=True): cv.boolean, } @@ -90,6 +92,7 @@ async def to_code(config: dict) -> None: config[CONF_MANUFACTURER_ID], config[CONF_BATTERY_LEVEL], config[CONF_RECONNECT], + config[CONF_ADVERTISE_ON_START], ) await cg.register_component(var, config) diff --git a/components/ble_keyboard/ble_keyboard.cpp b/components/ble_keyboard/ble_keyboard.cpp index 68ebddc..d2de64e 100644 --- a/components/ble_keyboard/ble_keyboard.cpp +++ b/components/ble_keyboard/ble_keyboard.cpp @@ -21,16 +21,21 @@ void Esp32BleKeyboard::setup() { pServer = BLEDevice::getServer(); - ESP_LOGI("ble", "advertiseOnDisconnect(%s)", this->reconnect_ ? "true" : "false"); + ESP_LOGD(TAG, "advertiseOnDisconnect(%s)", this->reconnect_ ? "true" : "false"); pServer->advertiseOnDisconnect(this->reconnect_); + if (!this->advertise_on_start_) { + ESP_LOGD(TAG, "stopAdvertising()"); + pServer->stopAdvertising(); + } + bleKeyboard.releaseAll(); } void Esp32BleKeyboard::stop() { - ESP_LOGI("ble", "stop()"); + ESP_LOGD("ble", "stop()"); if (this->reconnect_) { - ESP_LOGI("ble", "advertiseOnDisconnect(false)"); + ESP_LOGD(TAG, "advertiseOnDisconnect(false)"); pServer->advertiseOnDisconnect(false); } @@ -46,9 +51,9 @@ void Esp32BleKeyboard::stop() { } void Esp32BleKeyboard::start() { - ESP_LOGI("ble", "start()"); + ESP_LOGD(TAG, "start()"); if (this->reconnect_) { - ESP_LOGI("ble", "advertiseOnDisconnect(true)"); + ESP_LOGD(TAG, "advertiseOnDisconnect(true)"); pServer->advertiseOnDisconnect(true); } diff --git a/components/ble_keyboard/ble_keyboard.h b/components/ble_keyboard/ble_keyboard.h index c575f1f..08bee4b 100644 --- a/components/ble_keyboard/ble_keyboard.h +++ b/components/ble_keyboard/ble_keyboard.h @@ -12,9 +12,10 @@ namespace esphome { namespace ble_keyboard { class Esp32BleKeyboard : public PollingComponent { public: - Esp32BleKeyboard(std::string name, std::string manufacturer_id, uint8_t battery_level, bool reconnect) + Esp32BleKeyboard(std::string name, std::string manufacturer_id, uint8_t battery_level, bool reconnect, bool advertise_on_start) : PollingComponent(1000), bleKeyboard(name, manufacturer_id, battery_level) { reconnect_ = reconnect; + advertise_on_start_ = advertise_on_start; } void setup() override; @@ -47,6 +48,7 @@ class Esp32BleKeyboard : public PollingComponent { BleKeyboard bleKeyboard; bool reconnect_{true}; + bool advertise_on_start_{true}; uint32_t default_delay_{100}; uint32_t release_delay_{8}; }; diff --git a/components/ble_keyboard/const.py b/components/ble_keyboard/const.py index 1f20b16..53e9fc8 100644 --- a/components/ble_keyboard/const.py +++ b/components/ble_keyboard/const.py @@ -37,6 +37,7 @@ CONF_TEXT: Final = "text" CONF_KEYS: Final = "keys" CONF_RECONNECT: Final = "reconnect" +CONF_ADVERTISE_ON_START: Final = "advertise_on_start" CONF_BUTTONS: Final = "buttons" CONF_USE_DEFAULT_LIBS: Final = "use_default_libs" From 0afb63ea85413996331c4d2c614c0632f0728f65 Mon Sep 17 00:00:00 2001 From: "Manuel Amador (Rudd-O)" Date: Sun, 12 Mar 2023 20:35:21 +0000 Subject: [PATCH 03/15] Document option. --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index 9c876dd..7d1d03c 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,45 @@ ble_keyboard: * **buttons** (Optional, bool): Whether to add separate buttons for [keys](https://github.com/dmamontov/esphome-blekeyboard/wiki/Keys) (default: true); * **use_default_libs** (Optional, bool): Whether to use the arduino standard library. (default: false). +#### Controlling keyboard availability when `advertise_on_start` is `False` + +By default, the keyboard advertises its existence on start. You can turn +this off, but this creates a problem — how to ensure that the keyboard +is available on demand? + +In your ESP description, you can create a switch that does exactly that: + +``` +globals: + - id: keyboard_enabled + type: bool + restore_value: no + initial_value: "false" + +switch: + - platform: template + name: Enabled + icon: mdi:power + lambda: |- + if (id(keyboard_enabled)) { + return true; + } else { + return false; + } + turn_on_action: + - ble_keyboard.start: le_keyboard + - lambda: |- + id(keyboard_enabled) = true; + turn_off_action: + - ble_keyboard.stop: le_keyboard + - lambda: |- + id(keyboard_enabled) = false; +``` + +When toggled on, the switch will start the keyboard and begin advertising +it. When toggled off, the keyboard will disconnect all clients and stop +advertising — ensuring clients cannot reconnect. + ### Actions #### ble_keyboard.print From 8040eecdddeb55073cc98240da980fba380b5919 Mon Sep 17 00:00:00 2001 From: "Manuel Amador (Rudd-O)" Date: Wed, 2 Aug 2023 23:08:20 +0000 Subject: [PATCH 04/15] Work around issue with ESPHome 2023.7.0. Fixed in 2023.7.1. --- components/ble_keyboard/ble_keyboard.cpp | 40 +++++++++++++++++++++--- components/ble_keyboard/ble_keyboard.h | 4 ++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/components/ble_keyboard/ble_keyboard.cpp b/components/ble_keyboard/ble_keyboard.cpp index d2de64e..b93e3f3 100644 --- a/components/ble_keyboard/ble_keyboard.cpp +++ b/components/ble_keyboard/ble_keyboard.cpp @@ -15,25 +15,37 @@ namespace ble_keyboard { static const char *const TAG = "ble_keyboard"; void Esp32BleKeyboard::setup() { - ESP_LOGI(TAG, "Setting up..."); + if (this->setup_) { + ESP_LOGW(TAG, "BLE keyboard already setup."); + return; + } + + ESP_LOGCONFIG(TAG, "Setting up BLE keyboard..."); bleKeyboard.begin(); pServer = BLEDevice::getServer(); - ESP_LOGD(TAG, "advertiseOnDisconnect(%s)", this->reconnect_ ? "true" : "false"); + ESP_LOGCONFIG(TAG, "advertiseOnDisconnect(%s)", this->reconnect_ ? "true" : "false"); pServer->advertiseOnDisconnect(this->reconnect_); if (!this->advertise_on_start_) { - ESP_LOGD(TAG, "stopAdvertising()"); + ESP_LOGCONFIG(TAG, "stopAdvertising() because advertise_on_start is false"); pServer->stopAdvertising(); } bleKeyboard.releaseAll(); + this->setup_ = true; + ESP_LOGCONFIG(TAG, "Finished setting up up BLE keyboard."); } void Esp32BleKeyboard::stop() { - ESP_LOGD("ble", "stop()"); + if (!this->setup_) { + ESP_LOGW(TAG, "Attempting to use without setup. Not doing anything."); + return; + } + + ESP_LOGD(TAG, "stop()"); if (this->reconnect_) { ESP_LOGD(TAG, "advertiseOnDisconnect(false)"); pServer->advertiseOnDisconnect(false); @@ -51,6 +63,10 @@ void Esp32BleKeyboard::stop() { } void Esp32BleKeyboard::start() { + if (!this->setup_) { + ESP_LOGW(TAG, "Attempting to use without setup. Not doing anything."); + return; + } ESP_LOGD(TAG, "start()"); if (this->reconnect_) { ESP_LOGD(TAG, "advertiseOnDisconnect(true)"); @@ -78,6 +94,10 @@ void Esp32BleKeyboard::update_timer() { } void Esp32BleKeyboard::press(std::string message) { + if (!this->setup_) { + ESP_LOGW(TAG, "Attempting to use without setup. Not doing anything."); + return; + } if (this->is_connected()) { if (message.length() >= 5) { for (unsigned i = 0; i < message.length(); i += 5) { @@ -94,6 +114,10 @@ void Esp32BleKeyboard::press(std::string message) { } void Esp32BleKeyboard::press(uint8_t key, bool with_timer) { + if (!this->setup_) { + ESP_LOGW(TAG, "Attempting to use without setup. Not doing anything."); + return; + } if (this->is_connected()) { if (with_timer) { this->update_timer(); @@ -104,6 +128,10 @@ void Esp32BleKeyboard::press(uint8_t key, bool with_timer) { } void Esp32BleKeyboard::press(MediaKeyReport key, bool with_timer) { + if (!this->setup_) { + ESP_LOGW(TAG, "Attempting to use without setup. Not doing anything."); + return; + } if (this->is_connected()) { if (with_timer) { this->update_timer(); @@ -113,6 +141,10 @@ void Esp32BleKeyboard::press(MediaKeyReport key, bool with_timer) { } void Esp32BleKeyboard::release() { + if (!this->setup_) { + ESP_LOGW(TAG, "Attempting to use without setup. Not doing anything."); + return; + } if (this->is_connected()) { this->cancel_timeout((const std::string) TAG); bleKeyboard.releaseAll(); diff --git a/components/ble_keyboard/ble_keyboard.h b/components/ble_keyboard/ble_keyboard.h index 08bee4b..d3580ce 100644 --- a/components/ble_keyboard/ble_keyboard.h +++ b/components/ble_keyboard/ble_keyboard.h @@ -12,7 +12,8 @@ namespace esphome { namespace ble_keyboard { class Esp32BleKeyboard : public PollingComponent { public: - Esp32BleKeyboard(std::string name, std::string manufacturer_id, uint8_t battery_level, bool reconnect, bool advertise_on_start) + Esp32BleKeyboard(std::string name, std::string manufacturer_id, uint8_t battery_level, bool reconnect, + bool advertise_on_start) : PollingComponent(1000), bleKeyboard(name, manufacturer_id, battery_level) { reconnect_ = reconnect; advertise_on_start_ = advertise_on_start; @@ -47,6 +48,7 @@ class Esp32BleKeyboard : public PollingComponent { BLEServer *pServer; BleKeyboard bleKeyboard; + bool setup_{false}; bool reconnect_{true}; bool advertise_on_start_{true}; uint32_t default_delay_{100}; From b598b36b4942fef49020948c4742bf35a637cfa2 Mon Sep 17 00:00:00 2001 From: Rudd-O Date: Fri, 23 Feb 2024 03:22:36 +0000 Subject: [PATCH 05/15] Globals set in README Co-authored-by: Johannes Dilli --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d1d03c..ac4b745 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,9 @@ switch: id(keyboard_enabled) = true; turn_off_action: - ble_keyboard.stop: le_keyboard - - lambda: |- + - globals.set: + id: keyboard_enabled + value: "false" id(keyboard_enabled) = false; ``` From 66618877012899e87ad7ba07ddaf20e9d2467f2d Mon Sep 17 00:00:00 2001 From: Rudd-O Date: Fri, 23 Feb 2024 03:22:44 +0000 Subject: [PATCH 06/15] Globals set in README Co-authored-by: Johannes Dilli --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ac4b745..e8edbcf 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,9 @@ switch: } turn_on_action: - ble_keyboard.start: le_keyboard - - lambda: |- + - globals.set: + id: keyboard_enabled + value: "true" id(keyboard_enabled) = true; turn_off_action: - ble_keyboard.stop: le_keyboard From b81ddd0175f468b314e58aa7e21942ab1c1604ea Mon Sep 17 00:00:00 2001 From: Rudd-O Date: Fri, 23 Feb 2024 03:23:09 +0000 Subject: [PATCH 07/15] Simplify lambda in README Co-authored-by: Johannes Dilli --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8edbcf..6a6af81 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ switch: name: Enabled icon: mdi:power lambda: |- - if (id(keyboard_enabled)) { + return id(keyboard_enabled); return true; } else { return false; From adc8de8f1e80ca7fb99cb2cd0060d6e24952f8eb Mon Sep 17 00:00:00 2001 From: Zach de Koning Date: Fri, 31 May 2024 00:19:32 +1000 Subject: [PATCH 08/15] Update NimBLE-Arduino version to 1.4.1 --- components/ble_keyboard/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ble_keyboard/const.py b/components/ble_keyboard/const.py index 53e9fc8..dcc1f29 100644 --- a/components/ble_keyboard/const.py +++ b/components/ble_keyboard/const.py @@ -60,7 +60,7 @@ LIBS_ADDITIONAL: Final = [ ( "h2zero/NimBLE-Arduino", - "1.4.0", + "1.4.1", None, ), ( From 0a69f0a2abfd591bf2953221feabfb438082b8ff Mon Sep 17 00:00:00 2001 From: Zach de Koning <1870988+zachdekoning@users.noreply.github.com> Date: Thu, 20 Nov 2025 17:57:43 +1100 Subject: [PATCH 09/15] Attempt to use NimBLE-Arduino 2.3.6 Probably won't work seeing as 2.x re-wrote a lot, but lets try it first --- components/ble_keyboard/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ble_keyboard/const.py b/components/ble_keyboard/const.py index dcc1f29..4c90b4e 100644 --- a/components/ble_keyboard/const.py +++ b/components/ble_keyboard/const.py @@ -60,7 +60,7 @@ LIBS_ADDITIONAL: Final = [ ( "h2zero/NimBLE-Arduino", - "1.4.1", + "2.3.6", None, ), ( From c2b9b04852289130014087794ed40e171a696720 Mon Sep 17 00:00:00 2001 From: Zach de Koning <1870988+zachdekoning@users.noreply.github.com> Date: Thu, 20 Nov 2025 18:03:10 +1100 Subject: [PATCH 10/15] Use NimBLE-Arduino 1.4.3 --- components/ble_keyboard/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ble_keyboard/const.py b/components/ble_keyboard/const.py index 4c90b4e..2abcc50 100644 --- a/components/ble_keyboard/const.py +++ b/components/ble_keyboard/const.py @@ -60,7 +60,7 @@ LIBS_ADDITIONAL: Final = [ ( "h2zero/NimBLE-Arduino", - "2.3.6", + "1.4.3", None, ), ( From 6b9326ac9c3a34d110184acf27bcef8a7ee69e24 Mon Sep 17 00:00:00 2001 From: Zach de Koning Date: Thu, 1 Jan 2026 20:32:44 +1100 Subject: [PATCH 11/15] Fix iPad/iOS devices not auto-reconnecting on ESP32 reboot --- components/ble_keyboard/__init__.py | 7 +++++++ components/ble_keyboard/ble_keyboard.cpp | 6 ++++++ components/ble_keyboard/const.py | 9 ++------- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/components/ble_keyboard/__init__.py b/components/ble_keyboard/__init__.py index 8ff8419..8f5a061 100644 --- a/components/ble_keyboard/__init__.py +++ b/components/ble_keyboard/__init__.py @@ -174,6 +174,13 @@ def adding_dependencies(use_default_libs: bool = True) -> None: for lib in LIBS_ADDITIONAL: # type: ignore cg.add_library(*lib) + # Add updated ESP32 keyboard lib + cg.add_library( + name="ESP32 BLE Keyboard", + repository="https://github.com/zachdekoning/ESP32-BLE-Keyboard", + version=None, + ) + cg.add_build_flag(BUILD_FLAGS) diff --git a/components/ble_keyboard/ble_keyboard.cpp b/components/ble_keyboard/ble_keyboard.cpp index b93e3f3..d4e775e 100644 --- a/components/ble_keyboard/ble_keyboard.cpp +++ b/components/ble_keyboard/ble_keyboard.cpp @@ -24,6 +24,12 @@ void Esp32BleKeyboard::setup() { bleKeyboard.begin(); + // Set security settings for bonding so iPad will properly reconnect on device reboot + NimBLEDevice::setSecurityAuth(true, true, true); + NimBLEDevice::setSecurityPasskey(123456); + NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); + NimBLEDevice::setSecurityInitKey(3); + pServer = BLEDevice::getServer(); ESP_LOGCONFIG(TAG, "advertiseOnDisconnect(%s)", this->reconnect_ ? "true" : "false"); diff --git a/components/ble_keyboard/const.py b/components/ble_keyboard/const.py index 2abcc50..575c5af 100644 --- a/components/ble_keyboard/const.py +++ b/components/ble_keyboard/const.py @@ -60,14 +60,9 @@ LIBS_ADDITIONAL: Final = [ ( "h2zero/NimBLE-Arduino", - "1.4.3", + "2.3.0", None, - ), - ( - "t-vk/ESP32 BLE Keyboard", - "0.3.2", - None, - ), + ) ] BUILD_FLAGS: Final = "-D USE_NIMBLE" From 6373d7c6b81d70410596bc471391ef52ccc0d4ee Mon Sep 17 00:00:00 2001 From: Zach de Koning Date: Sat, 3 Jan 2026 16:44:49 +1100 Subject: [PATCH 12/15] Make pairing code configurable --- README.md | 15 ++++++++++++++- components/ble_keyboard/__init__.py | 3 +++ components/ble_keyboard/ble_keyboard.cpp | 7 +++++-- components/ble_keyboard/ble_keyboard.h | 4 +++- components/ble_keyboard/const.py | 1 + 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6a6af81..24d96cc 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,18 @@ Custom [esphome](https://esphome.io/) component to implement a virtual BLE keybo ```yaml external_components: - - source: github://dmamontov/esphome-blekeyboard + - source: github://zachdekoning/esphome-blekeyboard +``` + +### Enable BT support (required for ESPHome 2025.10+) +As of ESPHome 2025.10+ onwards, you must manually enable 'CONFIG_BT_ENABLED' in the framework sdkconfig_options + +```yaml +framework: + type: arduino + version: latest + sdkconfig_options: + CONFIG_BT_ENABLED: "y" ``` ### Configuration @@ -67,6 +78,7 @@ ble_keyboard: advertise_on_start: true buttons: true use_default_libs: false + pairing_code: 123456 ``` * **id** (Optional, string): Component ID. Needed for action; @@ -77,6 +89,7 @@ ble_keyboard: * **advertise_on_start** (Optional, bool): Automatic advertisement when the ESP device starts. (default: true); * **buttons** (Optional, bool): Whether to add separate buttons for [keys](https://github.com/dmamontov/esphome-blekeyboard/wiki/Keys) (default: true); * **use_default_libs** (Optional, bool): Whether to use the arduino standard library. (default: false). +* **pairing_code** (Optional, int): The pairing code required to be entered on the connecting device. (default: 123456). #### Controlling keyboard availability when `advertise_on_start` is `False` diff --git a/components/ble_keyboard/__init__.py b/components/ble_keyboard/__init__.py index 8f5a061..b492bb5 100644 --- a/components/ble_keyboard/__init__.py +++ b/components/ble_keyboard/__init__.py @@ -45,6 +45,7 @@ CONF_ADVERTISE_ON_START, CONF_TEXT, CONF_USE_DEFAULT_LIBS, + CONF_PAIRING_CODE, DOMAIN, LIBS_ADDITIONAL, LIBS_DEFAULT, @@ -70,6 +71,7 @@ cv.Optional(CONF_ADVERTISE_ON_START, default=True): cv.boolean, cv.Optional(CONF_USE_DEFAULT_LIBS, default=False): cv.boolean, cv.Optional(CONF_BUTTONS, default=True): cv.boolean, + cv.Optional(CONF_PAIRING_CODE, default=123456): cv.int_range(min=0, max=999999), } ) @@ -93,6 +95,7 @@ async def to_code(config: dict) -> None: config[CONF_BATTERY_LEVEL], config[CONF_RECONNECT], config[CONF_ADVERTISE_ON_START], + config[CONF_PAIRING_CODE], ) await cg.register_component(var, config) diff --git a/components/ble_keyboard/ble_keyboard.cpp b/components/ble_keyboard/ble_keyboard.cpp index d4e775e..eee485e 100644 --- a/components/ble_keyboard/ble_keyboard.cpp +++ b/components/ble_keyboard/ble_keyboard.cpp @@ -24,12 +24,15 @@ void Esp32BleKeyboard::setup() { bleKeyboard.begin(); - // Set security settings for bonding so iPad will properly reconnect on device reboot + // Set security settings for bonding so phones/tablets will properly reconnect on device reboot NimBLEDevice::setSecurityAuth(true, true, true); - NimBLEDevice::setSecurityPasskey(123456); + NimBLEDevice::setSecurityPasskey(this->pairing_code_); NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); NimBLEDevice::setSecurityInitKey(3); + // Set BLE Tx power to max to prevent disconnects + NimBLEDevice::setPower(ESP_PWR_LVL_P9, NimBLETxPowerType::All); + pServer = BLEDevice::getServer(); ESP_LOGCONFIG(TAG, "advertiseOnDisconnect(%s)", this->reconnect_ ? "true" : "false"); diff --git a/components/ble_keyboard/ble_keyboard.h b/components/ble_keyboard/ble_keyboard.h index d3580ce..eec2312 100644 --- a/components/ble_keyboard/ble_keyboard.h +++ b/components/ble_keyboard/ble_keyboard.h @@ -13,10 +13,11 @@ namespace ble_keyboard { class Esp32BleKeyboard : public PollingComponent { public: Esp32BleKeyboard(std::string name, std::string manufacturer_id, uint8_t battery_level, bool reconnect, - bool advertise_on_start) + bool advertise_on_start, uint32_t pairing_code) : PollingComponent(1000), bleKeyboard(name, manufacturer_id, battery_level) { reconnect_ = reconnect; advertise_on_start_ = advertise_on_start; + pairing_code_ = pairing_code; } void setup() override; @@ -51,6 +52,7 @@ class Esp32BleKeyboard : public PollingComponent { bool setup_{false}; bool reconnect_{true}; bool advertise_on_start_{true}; + uint32_t pairing_code_{123456}; uint32_t default_delay_{100}; uint32_t release_delay_{8}; }; diff --git a/components/ble_keyboard/const.py b/components/ble_keyboard/const.py index 575c5af..1cec802 100644 --- a/components/ble_keyboard/const.py +++ b/components/ble_keyboard/const.py @@ -40,6 +40,7 @@ CONF_ADVERTISE_ON_START: Final = "advertise_on_start" CONF_BUTTONS: Final = "buttons" CONF_USE_DEFAULT_LIBS: Final = "use_default_libs" +CONF_PAIRING_CODE: Final = "pairing_code" COMPONENT_CLASS: Final = "Esp32BleKeyboard" COMPONENT_NUMBER_CLASS: Final = "Esp32BleKeyboardNumber" From e19075ec1aa167529035b90cc1553218f7ddd56a Mon Sep 17 00:00:00 2001 From: "Manuel Amador (Rudd-O)" Date: Thu, 23 Apr 2026 11:06:45 +0000 Subject: [PATCH 13/15] Fix README.md. --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 24d96cc..b927651 100644 --- a/README.md +++ b/README.md @@ -121,13 +121,11 @@ switch: - globals.set: id: keyboard_enabled value: "true" - id(keyboard_enabled) = true; turn_off_action: - ble_keyboard.stop: le_keyboard - globals.set: id: keyboard_enabled value: "false" - id(keyboard_enabled) = false; ``` When toggled on, the switch will start the keyboard and begin advertising @@ -215,4 +213,4 @@ ble_keyboard.stop: my_ble_keyboard ## Credits * Thanks to all [ESPHome](https://github.com/esphome/esphome) contributors; * Thanks to [@T-vK](https://github.com/T-vK) for the [ESP32-BLE-Keyboard](https://github.com/T-vK/ESP32-BLE-Keyboard) library; -* Thanks to [@h2zero](https://github.com/h2zero) for the [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) library. \ No newline at end of file +* Thanks to [@h2zero](https://github.com/h2zero) for the [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) library. From 3afdc8315c4e07e2ac2f2d6cf24a8816352d2599 Mon Sep 17 00:00:00 2001 From: ThreeThirtyAM <220411925+ThreeThirtyAM@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:24:07 +0200 Subject: [PATCH 14/15] Fix ESPHome 2026.7 deprecation (timeout API) --- components/ble_keyboard/ble_keyboard.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/ble_keyboard/ble_keyboard.cpp b/components/ble_keyboard/ble_keyboard.cpp index eee485e..1c910f4 100644 --- a/components/ble_keyboard/ble_keyboard.cpp +++ b/components/ble_keyboard/ble_keyboard.cpp @@ -98,8 +98,8 @@ bool Esp32BleKeyboard::is_connected() { } void Esp32BleKeyboard::update_timer() { - this->cancel_timeout((const std::string) TAG); - this->set_timeout((const std::string) TAG, release_delay_, [this]() { this->release(); }); + this->cancel_timeout(TAG); + this->set_timeout(TAG, release_delay_, [this]() { this->release(); }); } void Esp32BleKeyboard::press(std::string message) { @@ -155,7 +155,7 @@ void Esp32BleKeyboard::release() { return; } if (this->is_connected()) { - this->cancel_timeout((const std::string) TAG); + this->cancel_timeout(TAG); bleKeyboard.releaseAll(); } } From 9709655a419608a1fef9d78139183dcd13f85eb1 Mon Sep 17 00:00:00 2001 From: "Manuel Amador (Rudd-O)" Date: Sun, 26 Apr 2026 11:30:01 +0000 Subject: [PATCH 15/15] Indicate to the peer that there is no I/O capability on this device. --- components/ble_keyboard/ble_keyboard.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/ble_keyboard/ble_keyboard.cpp b/components/ble_keyboard/ble_keyboard.cpp index 1c910f4..5bded15 100644 --- a/components/ble_keyboard/ble_keyboard.cpp +++ b/components/ble_keyboard/ble_keyboard.cpp @@ -27,7 +27,8 @@ void Esp32BleKeyboard::setup() { // Set security settings for bonding so phones/tablets will properly reconnect on device reboot NimBLEDevice::setSecurityAuth(true, true, true); NimBLEDevice::setSecurityPasskey(this->pairing_code_); - NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); + // Indicate to peer device that there is no way to confirm a pairing code. + NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT); NimBLEDevice::setSecurityInitKey(3); // Set BLE Tx power to max to prevent disconnects