This an ESPHome custom component that exposes a "pin pad" BluetoothLE server.
It is a component which can trigger other things (such as an output
device) when the correct value is received over BLE.
Status: 🚨 Experimental! Use at your own risk.
- Why?
- Compatibility
- Usage
- Configuration options
- Protocol (how it works)
- Testing
- Security
- Developer Instructions
- Help, Feedback, and Ideas
- License & Credit
A "pin pad" is a simple input device which receives a string over BLE, checks it against the valid PIN(s), and triggers an "accepted" or "rejected" callback within ESPHome.
You could use it to make a switch, lock, or other device which is unlocked via BLE.
I wrote it because I had exactly that need, and specifically wanted something that didn't require wifi in order to work. Oh, and of course, I was bored/curious.
- ESPHome: Version
2023.11.0
or newer.
In your esphome YML, first import this component:
external_components:
- source:
type: git
url: https://github.com/mik3y/esp32-ble-pinpad
ref: main
components: [ esp32_ble_pinpad ]
Next, and optionally, set up a status LED.
output:
- platform: ledc
pin: GPIO2
id: onboard_status_led
Finally, add the component itself:
esp32_ble_pinpad:
secret_passcode: changeme
security_mode: hotp
status_indicator: onboard_status_led
on_pinpad_accepted:
- logger.log: "Rad! Correct pin was given!"
on_pinpad_rejected:
- logger.log: "Bummer! Incorrect pin was given."
Of course, you'll probably want to do something other than log when the pin comes in. You can use any availabe ESPHome trigger here. For example, if you have a garage door set up as a cover, you could do:
on_pinpad_accepted:
- logger.log: "Got pin over BLE, opening door."
- cover.toggle: garage_door
You can provide these options in the esp32_ble_pinpad
yaml block:
secret_passcode
(required): The secret passcode. Follow strong password rules for best security (i.e. longer values are better).security_mode
(required): One ofnone
,hotp
, ortotp
. If in doubt,hotp
is recommended.- Warning: Using
none
is highly insecure, as it means the passcode will be exposed over-the-wire. See Security for further information.
- Warning: Using
status_indicator
(optional): Anoutput
to use for blinking status.on_pinpad_accepted
(optional): Trigger(s) to fire when a PIN is accepted.on_pinpad_rejected
(optional): Trigger(s) to fire when a PIN is rejected.
Two configuration factors are set by you and flashed into the device:
secret_passcode
: The value you will use and give to anyone wishing to "pin in".security_mode
: One ofnone
,hotp
, ortotp
, this determines how the passcode is used.
To authenticate ("pin in"), a client performs the following steps:
- Discover the device and create a BLE connection to the pinpad service.
- Read the
security_mode
setting of the device. This is available at thePINPAD_SECURITY_MODE_CHR
BLE GATT characteristic. - Compute the secret to send:
- If
security_mode = none
, the value ofsecret_passcode
is sent in full and in the clear. This is the least secure option and is not recommended. - If
security_mode = hotp
, the client must read the current HOTP counter fromPINPAD_HOTP_COUNTER_CHR
, then generate and send a 6-digit HOTP code based on that value and usingsecret_passcode
as the key. - If
security_mode = totp
, the client must generate and send a 6-digit TOTP code based on the current time and usingsecret_passcode
as the key.
- If
- Optionally, read
PINPAD_STATUS_CHR
to determine whether the operation succeeded.
A debug program is included at tools/pypinpad/pypinpad.py
which can be used to send a pin to a locally-discoverable BLE device.
To install the tool's dependencies, poetry
is required.
cd tools/pypinpad
poetry install
Example usage:
$ poetry shell
(pypinpad-py3.10) $ python pypinpad.py pin
Searching for BLE devices ...
Found device ble-pinpad-example (9A4935AD-7909-4E76-8481-1D42C213B689). Use it? [Y/n]: y
Device security mode: hotp
Please enter the device password: changeme
Current hotp counter: 13
Sending pin "100596" to device ...
Warning: You should consider this component totally insecure. No warranty. Please review LICENSE.txt
.
When security_mode
is hotp
or totp
, the secret passcode is never revealed on the wire. Captured payloads should not be replayable: In the case of hotp
, the counter is incremented after every successful authentication and persisted to flash. totp
follows a standard 30-second window.
Notes for folks working on the component itself.
See example.yml
for how to pull the component in.
Use doctoc
to update the table of contents:
npm install -g doctoc
doctoc --notitle --github README.md
Here are some things I found helpful while learning about BLE:
- An overview of BLE (protocol & stack): https://novelbits.io/bluetooth-low-energy-ble-complete-guide
- An unrelated ble stack bugreport, but with great discussion: espressif/arduino-esp32#1038
- BLE officially assigned numbers (PDF): https://btprodspecificationrefs.blob.core.windows.net/assigned-numbers/Assigned%20Number%20Types/Assigned%20Numbers.pdf
Please open an issue on GitHub!
Here are some ideas I haven't gotten around to yet.
- BLE interface for adding/revoking pins (using some sort of admin pin).
- BLE interface for reading attempt history.
Written by @mik3y.
All code is offered under the MIT license. See LICENSE.txt
for full terms.
This project includes a copy of the esp-totp library by Jackson Ming Hu, also MIT licensed. See otp.{cpp,h}
.
Inspired by & indebted to the built-in esp32_improv component.