ESPHome Custom Component

ESP32 BLE Keyboard

Transform your ESP32 into a Bluetooth HID keyboard. Control paired Windows, Android, and iOS devices directly from Home Assistant β€” send keystrokes, combos, and power commands. Use passkey_mode: legacy for Windows (Just Works for Android), passkey_mode: secure_connections for iOS. Tested on Windows 11, Android 16, and iOS.

View on GitHub ⌨️ Keycode Reference

Features

πŸ”΅

Native BLE HID

Uses the ESP-IDF Bluedroid GATTS API directly. Recognised as a standard keyboard by Windows, Android, and iOS. Full HOGP-compliant BLE HID with Device Information and Battery services.

πŸ”’

Secure Pairing

Optional 6-digit static passkey (PIN) for secure bonding. For fastest pairing, use Just Works (no passkey). With passkey, use passkey_mode: legacy for Windows. For iOS, use passkey_mode: secure_connections (required). Android uses Just Works only.

⌨️

Key Combos

Send any modifier + key combination using hex keycodes. Win+R, Ctrl+C, Alt+F4 and more.

πŸ’€

Power Commands

Native HID sleep, hibernate and shutdown β€” clean OS-level signals, no Run dialog or lingering key state.

🎡

Media Keys

Volume up/down, mute, play/pause, next/prev track and stop via HID consumer control reports.

🏠

Home Assistant

Each button appears as an entity in Home Assistant. Trigger from automations, dashboards or scripts. Direct API implementation.

πŸ“Ά

Pairing State Sensor

Optional binary sensor turns on after a successful GAP pairing event and turns off on disconnect/unpair.

✏️

Custom Text Input

Type any text in Home Assistant and send it directly to the paired host device. Drive it manually or from automations.

πŸ–±οΈ

Mouse Control

Left, right and middle click, cursor movement, and scroll wheel via HID mouse reports. Drive from buttons or automations.

⌨️

HA Keyboard Card

Full on-screen QWERTY keyboard card for Home Assistant. Sticky modifiers, Caps Lock, F-keys, and arrow keys.

Home Assistant Keyboard Card
πŸ–₯️

HA Mouse Card

Custom Lovelace card with a touchpad, 3 mouse buttons, and scroll controls. Drag to move cursor, wheel to scroll.

Home Assistant Mouse Card
πŸ“Ί

HA Remote Card

Home Assistant remote control card with D-pad, media controls, and app shortcuts. Control media and navigation from your dashboard.

Home Assistant Remote Card
🌐

Web Control

Built-in web page with full keyboard, mouse, and remote UI. Access from any browser β€” no Home Assistant needed. Enable with web_control: true.

Web Control Page
πŸ”€

Multi-Host Switching

Pair with up to 10 hosts and switch between them with a button press. Uses directed advertising for fast reconnection. Slots persist across reboots.

🌍

Keyboard Layouts

Pick us, uk, or de (German QWERTZ) in YAML and switch live from the web UI (persisted to NVS). Bind a layout per host slot so switching hosts also switches layout. UK adds Β£ Β¬ €; DE adds Γ€ ΓΆ ΓΌ ß € Β§ Β° via UTF-8 (dead keys auto-composed). The architecture supports more layouts with three small additions per language.

πŸ”§

Web Macros

Create, edit, and delete macro keys directly from the web UI β€” no reflash needed. Macros support multi-step commands separated by | with optional delay:N timing. Persist in NVS across reboots.

Quick Start

bluetooth-keyboard.yaml
external_components:
  - source:
      type: git
      url: https://github.com/markusg1234/ESPHome-espidf_ble_keyboard
      ref: main
    components: [ espidf_ble_keyboard ]

espidf_ble_keyboard:
  id: my_keyboard
  device_name: "ESP32 BLE KB"  # optional, max 29 chars
  key_delay_ms: 80  # optional, increase if characters drop
  passkey: 123456  # optional 6-digit PIN
  passkey_mode: legacy  # use secure_connections for iOS (required)
  web_control: true  # optional, built-in web UI
  host_slots: 4  # optional, multi-host switching (1–10)
  mouse_sensitivity: 1.0  # optional, web mouse base speed (default: 1.0)
  mouse_acceleration: 0.15  # optional, web mouse accel factor (default: 0.15)
  mouse_max_speed: 4.0  # optional, web mouse max sensitivity (default: 4.0)
  scroll_sensitivity: 2.0  # optional, web mouse scroll speed (default: 2.0)
  keyboard_layout: us  # optional, us | uk | de β€” must match host PC layout (default: us)
  custom_text_id:        # optional, link text entities for Send buttons
    - custom_text

button:
  - platform: espidf_ble_keyboard
    keyboard_id: my_keyboard
    name: "Win + R"
    action: "combo:0x08:0x15"

  - platform: espidf_ble_keyboard
    keyboard_id: my_keyboard
    name: "Shutdown PC"
    action: "shutdown"

Pairing State Sensor

binary_sensor
binary_sensor:
  - platform: espidf_ble_keyboard
    keyboard_id: my_keyboard
    name: "BLE Keyboard Paired"

# ON  = GAP pairing completed successfully on current connection
# OFF = disconnected/unpaired, or no successful pairing yet in this session

Sensors

sensor β€” RSSI & Active Host
sensor:
  # RSSI β€” signal strength of connected host
  - platform: espidf_ble_keyboard
    keyboard_id: my_keyboard
    name: "BLE Host RSSI"
    update_interval: 10s
  # Active Host β€” publishes current host slot (0-based)
  # Required for keyboard card host switcher to stay in sync
  - platform: espidf_ble_keyboard
    keyboard_id: my_keyboard
    type: active_host
    name: "BLE Keyboard Active Host"

Built-in Actions

ActionDescription
"Hello\n"Type a string. Printable ASCII supported on every layout; non-ASCII (e.g. Β£ Β¬ € on UK, Γ€ ΓΆ ΓΌ ß on DE) works via UTF-8 when the active layout exposes it. Use \n for Enter.
"combo:0x08:0x15"Key combo β€” modifier + keycode in hex. Use 0x00 as modifier for a plain keypress. See keycode reference.
type: comboDict format alternative β€” type: combo, modifier: 0x01, key: 0x04. More readable for complex actions.
type: consumerDict format for consumer codes β€” type: consumer, code: 0x0192.
"ctrl_alt_del"Send Ctrl+Alt+Del secure login sequence.
"sleep"HID System Sleep signal β€” clean OS-level sleep.
"hibernate"Hibernate via Run dialog β€” saves to disk, full power off.
"shutdown"HID System Power Down signal β€” clean OS-level shutdown (Windows).
"power"HID power button β€” triggers the host device power button action (Windows).
"mute"Toggle mute.
"volume_up"Volume up.
"volume_down"Volume down.
"play_pause"Play / pause media.
"next_track"Skip to next track.
"prev_track"Previous track.
"stop"Stop media playback.
"consumer:0x0192"Send any HID consumer control code β€” open apps, control brightness and more. See keycode reference.
"left_click"Mouse left click.
"right_click"Mouse right click.
"middle_click"Mouse middle click.
"mouse_move:50:0"Move mouse cursor β€” relative X:Y pixels (-127 to 127).
"mouse_scroll:3"Scroll mouse wheel β€” positive = up, negative = down (-127 to 127).
type: mouse_clickDict format β€” type: mouse_click, buttons: 0x01. 0x01=left, 0x02=right, 0x04=middle.
"send_custom_text"Send linked text entity content. Use send_custom_text:N for multiple entities. Requires custom_text_id in config.
"switch_host:0"Switch to host slot 0–9. Reconnects to stored host or advertises for new pairing.
"forget_host:0"Remove BLE bond for host slot 0–9 and clear the stored address.
"string:hello"Explicit text typing β€” useful in multi-step macros to distinguish text from action names.
"delay:200"Pause for N milliseconds (max 10000). Used between steps in multi-step macros.
"a | b | c"Multi-step macro β€” chain actions with |. 50ms auto-delay between steps. E.g. "combo:2:6 | delay:100 | combo:2:25" (Copy, wait, Paste).
execute_action()Run any action string from a lambda β€” id(kb).execute_action("combo:2:6 | delay:100 | combo:2:25");. Supports multi-step.
execute_macro(N)Run a web-defined macro by index β€” id(kb).execute_macro(0);. Index shown in web UI as [0], [1], etc.

Home Assistant Cards

Mouse Card β€” dashboard YAML
type: custom:ble-mouse-card
device: bluetooth_keyboard
name: Mouse Control        # optional card title
sensitivity: 1.5            # optional cursor speed (default: 1.5)
mouse_acceleration: 0.15   # optional acceleration factor (default: 0.15)
mouse_max_speed: 4.5       # optional max sensitivity cap (default: 4.5)
scroll_sensitivity: 2      # optional scroll speed (default: 2)
tap_to_click: true          # optional tap = left click (default: true)
Keyboard Card β€” dashboard YAML
type: custom:ble-keyboard-card
device: bluetooth_keyboard
name: BLE Keyboard          # optional card title
show_fkeys: true            # optional show F1-F12 row (default: true)
host_slots: 4              # optional host switcher (default: 0 = hidden)
host_names:                  # optional custom names per slot
  - TV
  - Phone
Remote Card β€” dashboard YAML
type: custom:ble-remote-card
device: bluetooth_keyboard
name: Media Remote           # optional card title (auto from HA if omitted)
show_numpad: true            # optional number pad (default: false)
show_apps: true              # optional app launch row (default: true)
show_color: true             # optional color buttons (default: false)
ESPHome services required for cards
api:
  services:
    # Mouse card services
    - service: mouse_move
      variables:
        x: int
        y: int
      then:
        - lambda: |-
            id(my_keyboard).send_mouse_move(x, y);
    - service: mouse_scroll
      variables:
        amount: int
      then:
        - lambda: |-
            id(my_keyboard).send_mouse_scroll(amount);
    - service: mouse_click
      variables:
        btn: int
      then:
        - lambda: |-
            id(my_keyboard).send_mouse_click(btn);
    # Keyboard card services
    - service: send_string
      variables:
        keys: string
      then:
        - lambda: |-
            id(my_keyboard).send_string(keys);
    - service: send_key
      variables:
        modifier: int
        keycode: int
      then:
        - lambda: |-
            id(my_keyboard).send_key_combo(modifier, keycode);
    # Remote card service
    - service: send_consumer
      variables:
        code: int
      then:
        - lambda: |-
            id(my_keyboard).send_consumer(code);
    # Host switching service
    - service: switch_host
      variables:
        slot: int
      then:
        - lambda: |-
            id(my_keyboard).switch_host(slot);