Well, there is a code:
Main tagreader_keypad.yaml:
# I.S. esphome input and presence detection device
# intended for use with HA alarm panel
#
# NFC tag reader & writter (Based on Tag Reader for Home Assistant, https://github.com/adonno/tagreader, thanks to Andrea Donno)
#
# Keypad, 4x4 matrix with both line and character input (based on ESPhome Keypad, https://github.com/Syralist/esphomekeypad, thanks to Thomas Helmke)
#
# Bluetooth device presence detector using ESP32 bluetooth, passive scan
#
# Exposed entities:
# Tagreader-Keypad sensors:
# - switch.${friendly_name}_buzzer_enabled
# - light.${friendly_name}_led_p1
# - light.${friendly_name}_led_p2
# - switch.${friendly_name}_led_enabled
# - binary_sensor.${friendly_name}_status
# - sensor.${friendly_name}_keypad_charsensor
# - sensor.${friendly_name}_keypad_textsensor
# - sensor.${friendly_name}_esphome_version
# - switch.${friendly_name}_restart
# - sensor.${friendly_name}_connected_bssid
# - sensor.${friendly_name}_connected_ssid
# - sensor.${friendly_name}_ip
# - sensor.${friendly_name}_mac_wifi_address
#
# BLE presence
# - binary_sensor.itag_blue
# - binary_sensor.itag_red
# - binary_sensor.itag_black
#
# Hardware:
# Board:
# esp32doit devkit v1, ESP32 240MHz, 320KB RAM, 4MB Flash (EBay)
# PN532 Tag R/W:
# (EBay, 1set PN532 NFC RFID Wireless Module V3 User Breakout For Arduino For Android, https://www.ebay.com/itm/1set-PN532-NFC-RFID-Wireless-Module-V3-User-Breakout-For-Arduino-For-Android/183458101571?hash=item2ab6f47943:g:lsEAAOSwPU9bXDxM)
# Keypad:
# Matrix 4x4 (EBay, Switch Keypad Array Module Keys Button Membrane Switch DIY Kit For New, https://www.ebay.com/itm/Switch-Keypad-Array-Module-Keys-Button-Membrane-Switch-DIY-Kit-For-New/264963446275)
# LED PIXEL WS2801:
# (LED PIXEL S WS2801 KOCKASTI IP68, https://e-radionica.com/hr/led-pixel-s-ws2801-kockasti-ip68.html)
# Buzzer:
# Not really active buzzer, this module don't have oscilator, just transistor, so it can be driven with very low current
# (EBay, https://www.ebay.com/itm/5Pcs-Active-Buzzer-Module-3-3-5V-Low-Level-Trigger-Buzzer-3-Pin-Control-Board/372767092644?hash=item56caa66fa4:g:IiYAAOSwmUNdeQ9P)
#
# BLE Tag:
# (EBay, Smart GPS Tracker Wireless Bluetooth Anti-Lost Alarm Finder Pet Locator Key W9J7, https://www.ebay.com/itm/Smart-GPS-Tracker-Wireless-Bluetooth-Anti-Lost-Alarm-Finder-Pet-Locator-Key-W9J7/233666540928)
#
# Pin Map
#
# Keypad C1 <---> GPIO32
# Keypad C2 <---> GPIO33
# Keypad C3 <---> GPIO25
# Keypad C4 <---> GPIO26
# Keypad R1 <---> GPIO23
# Keypad R2 <---> GPIO18
# Keypad R3 <---> GPIO19
# Keypad R4 <---> GPIO05
#
# PN532 SDA <---> GPIO21 (SDA)
# PN532 SCL <---> GPIO22 (SCL)
#
# WS2801 DIN <--> GPIO04
# WS2801 CLIN <-> GPIO15
#
# Buzzer <------> GPIO27
#
# Keypad:
# C1 C2 C3 C4
# +--------------------------------+
# | |
# R1| | 1 | | 2 | | 3 | | A | |
# | |
# R2| | 4 | | 5 | | 6 | | B | |
# | |
# R3| | 7 | | 8 | | 9 | | C | |
# | |
# R4| | * | | 0 | | # | | D | |
# | |
# +--|--|--|--|--|--|--|--|--|--|--+
# N C C C C R R R R N
# C 1 2 3 4 1 2 3 4 c
#
# Log:
# 2021-04-10
# Instead of fixed timeout for ble presence defined in substitutions (ble_presence_timeout: 2 min),
# added possibility to define this delay in HASS via input_number.ble_presence_timeout_value and sensor.ble_presence_timeout_int
# and internal esphome sensor "BLE presence timeout"
#
wifi:
networks:
- ssid: "IShome07"
password: !secret wifi_password
- ssid: "IShome04"
password: !secret wifi_password
- ssid: "IShome05"
password: !secret wifi_password
- ssid: "IShome06"
password: !secret wifi_password
domain: !secret domain
ap:
ssid: ${devicename}
password: !secret ap_password
# Enable the captive portal for inital WiFi setup
captive_portal:
substitutions:
devicename: tagreader
friendly_name: TagReader
#ble_presence_timeout: 2 min
esphome:
name: $devicename
platform: ESP32
board: esp32doit-devkit-v1
# Keypadtext begin
includes:
- keypad_sensor_textsensor.h
libraries:
- "Keypad"
# Keypadtext end
on_boot:
priority: -10
then:
- rtttl.play: "booted:d=64,o=7,b=120:c,p,p,c"
- wait_until:
api.connected:
- binary_sensor.template.publish:
id: itag_red
state: OFF
- binary_sensor.template.publish:
id: itag_blue
state: OFF
- binary_sensor.template.publish:
id: itag_black
state: OFF
- logger.log: API is connected!
- rtttl.play: "startup:d=32,o=5,b=120:c,d,e,f,g,a,h"
- light.turn_on:
id: group_led
#id: led_p1
brightness: 100%
red: 0%
green: 0%
blue: 100%
flash_length: 500ms
on_shutdown:
then:
- rtttl.play: "shutdown:d=32,o=5,b=120:h,a,g,f,e,d,c"
# Define switches to control LED and buzzer from HA
switch:
- platform: template
name: "${friendly_name} Buzzer Enabled"
id: buzzer_enabled
icon: mdi:volume-high
optimistic: true
restore_state: true
- platform: template
name: "${friendly_name} LED enabled"
id: led_enabled
icon: mdi:alarm-light-outline
optimistic: true
restore_state: true
# Restart switch
- platform: restart
name: "${friendly_name} Restart"
# Logging
logger:
#level: VERY_VERBOSE
# level: VERBOSE
#
level: INFO
baud_rate: 115200
# Enable Home Assistant API
api:
password: !secret api_password
services:
- service: rfidreader_tag_ok
then:
- rtttl.play: "beep:d=16,o=5,b=100:b"
- service: rfidreader_tag_ko
then:
- rtttl.play: "beep:d=8,o=5,b=100:b"
- service: play_rtttl
variables:
song: string
then:
- rtttl.play: !lambda 'return song;'
- service: play_auld_lang_syne
then:
- rtttl.play: "AuldLangSyne:d=4,o=5,b=100:4g,4c6,8c6,4c6,4e6,4d6,8c6,4d6,8e6,8d6,4c6,8c6,4e6,4g6,2a6,4a6,4g6,8e6,4e6,4c6,4d6,8c6,4d6,8e6,8d6,4c6,8a,4a,4g,2c6"
- service: write_tag_random
then:
- lambda: |-
static const char alphanum[] = "0123456789abcdef";
std::string uri = "https://www.home-assistant.io/tag/";
for (int i = 0; i < 8; i++)
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
uri += "-";
for (int j = 0; j < 3; j++) {
for (int i = 0; i < 4; i++)
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
uri += "-";
}
for (int i = 0; i < 12; i++)
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
auto message = new nfc::NdefMessage();
message->add_uri_record(uri);
ESP_LOGD("tagreader", "Writing payload: %s", uri.c_str());
id(pn532_board).write_mode(message);
- service: write_tag_id
variables:
tag_id: string
then:
- lambda: |-
auto message = new nfc::NdefMessage();
std::string uri = "https://www.home-assistant.io/tag/";
uri += tag_id;
message->add_uri_record(uri);
id(pn532_board).write_mode(message);
- service: clean_tag
then:
- lambda: 'id(pn532_board).clean_mode();'
- service: cancel_writing
then:
- lambda: 'id(pn532_board).read_mode();'
# Enable OTA upgrade
ota:
password: !secret ota_password
i2c:
sda: 21
scl: 22
scan: False
frequency: 400kHz
pn532_i2c:
id: pn532_board
update_interval: 1s
on_tag:
then:
- homeassistant.tag_scanned: !lambda |
if (!tag.has_ndef_message()) {
ESP_LOGD("tagreader", "No NDEF");
return x;
}
auto message = tag.get_ndef_message();
auto records = message->get_records();
for (auto &record : records) {
std::string payload = record->get_payload();
size_t pos = payload.find("https://www.home-assistant.io/tag/");
if (pos != std::string::npos) {
return payload.substr(pos + 34);
}
}
ESP_LOGD("tagreader", "Bad NDEF, fallback to uid");
return x;
- if:
condition:
switch.is_on: buzzer_enabled
then:
- rtttl.play: "success:d=24,o=5,b=100:c,g,b"
- if:
condition:
switch.is_on: led_enabled
then:
- light.turn_on:
id: led_p2
brightness: 100%
red: 0%
green: 100%
blue: 0%
flash_length: 500ms
# Define the buzzer output
output:
- platform: ledc
pin: GPIO27
id: buzzer
# Define buzzer as output for RTTTL
rtttl:
output: buzzer
# Define status LED
status_led:
pin:
number: GPIO2
inverted: True
# Configure RGB LEDs and partitions
light:
- platform: fastled_spi
chipset: WS2801
data_pin: GPIO4
clock_pin: GPIO15
default_transition_length: 10ms
num_leds: 2
rgb_order: RGB
id: group_led
name: "${friendly_name} LED base"
restore_mode: ALWAYS_OFF
internal: true
- platform: partition
name: "${friendly_name} LED P1"
id: "led_p1"
segments:
- id: group_led
from: 0
to: 0
effects: !include light_effects.yaml
- platform: partition
name: "${friendly_name} LED P2"
id: "led_p2"
segments:
- id: group_led
from: 1
to: 1
effects: !include light_effects.yaml
# RGB LED end
text_sensor:
- platform: version
name: "${friendly_name} ESPHome Version"
- platform: custom
lambda: |-
auto keytext_sensor = new KeypadTextSensor();
App.register_component(keytext_sensor);
return {keytext_sensor->phrase_sensor, keytext_sensor->char_sensor};
text_sensors:
# Line text, '#' is enter and send input to HA, '*' is clear, means clear all input, send empty string to HA
- name: "${friendly_name} Keypad TextSensor"
id: "keypad_textsensor"
# Pressed character as number, instantly sent to HA, valid for 500ms max.
# Characters are 0,1,2,3,4,5,6,7,8,9 for numerics
# 'A'=17, 'B'=18, 'C'=19, 'D'=20, '*'=-6, '#'=-13
- name: "${friendly_name} Keypad CharSensor"
id: "keypad_charsensor"
on_value:
- if:
condition:
lambda: 'return id(keypad_charsensor).state == "-13";'
then:
- if:
condition:
switch.is_on: buzzer_enabled
then:
- rtttl.play: "enter_beep:d=64,o=4,b=120:c,e,e"
- if:
condition:
switch.is_on: led_enabled
then:
- light.turn_on:
id: led_p2
brightness: 50%
red: 0%
green: 100%
blue: 0%
flash_length: 250ms
- if:
condition:
lambda: 'return id(keypad_charsensor).state == "-6";'
then:
- if:
condition:
switch.is_on: buzzer_enabled
then:
- rtttl.play: "clear_beep:d=32,o=4,b=120:e,c,c"
- if:
condition:
switch.is_on: led_enabled
then:
- light.turn_on:
id: led_p2
brightness: 50%
red: 100%
green: 0%
blue: 100%
flash_length: 250ms
- if:
condition:
and:
- lambda: 'return id(keypad_charsensor).state != "-6";'
- lambda: 'return id(keypad_charsensor).state != "-13";'
- lambda: 'return id(keypad_charsensor).state != "";'
then:
- if:
condition:
switch.is_on: buzzer_enabled
then:
- rtttl.play: "key_beep:d=32,o=7,b=120:g"
- platform: wifi_info
ip_address:
name: "${friendly_name} IP"
ssid:
name: "${friendly_name} Connected SSID"
bssid:
name: "${friendly_name} Connected BSSID"
mac_address:
name: "${friendly_name} Mac Wifi Address"
# To replace ${ble_presence_timeout} with from home assistant adjustable value. See log.
sensor:
- platform: homeassistant
name: "BLE presence timeout"
entity_id: sensor.ble_presence_timeout_int
id: ble_presence_timeout_int
internal: true
# To replace ${ble_presence_timeout} end
script:
- id: itag_red_timer
mode: restart
then:
- binary_sensor.template.publish:
id: itag_red
state: ON
#- delay: ${ble_presence_timeout} # Replaced with from home assistant adjustable value. See log.
- delay: !lambda 'return id(ble_presence_timeout_int).state * 1000;'
- binary_sensor.template.publish:
id: itag_red
state: OFF
- id: itag_blue_timer
mode: restart
then:
- binary_sensor.template.publish:
id: itag_blue
state: ON
#- delay: ${ble_presence_timeout} # Replaced with from home assistant adjustable value. See log.
- delay: !lambda 'return id(ble_presence_timeout_int).state * 1000;'
- binary_sensor.template.publish:
id: itag_blue
state: OFF
- id: itag_black_timer
mode: restart
then:
- binary_sensor.template.publish:
id: itag_black
state: ON
#- delay: ${ble_presence_timeout} # Replaced with from home assistant adjustable value. See log.
- delay: !lambda 'return id(ble_presence_timeout_int).state * 1000;'
- binary_sensor.template.publish:
id: itag_black
state: OFF
esp32_ble_tracker:
scan_parameters:
active: false
interval: 300ms
window: 200ms
duration: 5s
on_ble_advertise:
- mac_address: FF:FF:C0:05:D6:81
then:
- script.execute: itag_red_timer
- mac_address: FF:FF:50:07:75:44
then:
- script.execute: itag_blue_timer
- mac_address: FF:FF:BB:02:44:0C
then:
- script.execute: itag_black_timer
binary_sensor:
- platform: status
name: "${friendly_name} Status"
- platform: template
name: "iTag red"
id: "itag_red"
- platform: template
name: "iTag blue"
id: "itag_blue"
- platform: template
name: "iTag black"
id: "itag_black"
Arduino code keypad_sensor_textsensor.h:
#include "esphome.h"
#include "Keypad.h"
class KeypadTextSensor : public Component {
public:
static const byte n_rows = 4;
static const byte n_cols = 4;
bool keyPublished = false;
static const unsigned int resetTimeB = 500;
unsigned int lastPublish = 0;
char keys[n_rows][n_cols] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte colPins[n_cols] = {32, 33, 25, 26};
byte rowPins[n_rows] = {23, 18, 19, 5};
Keypad myKeypad = Keypad( makeKeymap(keys), rowPins, colPins, n_rows, n_cols);
std::string keysequenz;
static const unsigned int resetTimeA = 300;
unsigned int resetCounter = 0;
bool keyPressed = false;
TextSensor *phrase_sensor = new TextSensor();
TextSensor *char_sensor = new TextSensor();
void get_char(char ch) {
char myKey = ch;
if (myKey != NO_KEY){
if (myKey == '#'){
phrase_sensor->publish_state(keysequenz);
keysequenz.clear();
resetCounter = 0;
keyPressed = false;
}
else if (myKey == '*'){
keysequenz.clear();
phrase_sensor->publish_state(keysequenz);
resetCounter = 0;
keyPressed = false;
}
else {
keysequenz.push_back(myKey);
keyPressed = true;
}
}
if (keyPressed){
resetCounter++;
}
if (resetCounter >= resetTimeA){
keysequenz.clear();
phrase_sensor->publish_state(keysequenz);
resetCounter = 0;
keyPressed = false;
}
}
void loop() override {
// This will be called by App.loop()
char myKey = myKeypad.getKey();
//myKey = NO_KEY;
if (myKey != NO_KEY){
int key = myKey - 48;
char_sensor->publish_state(to_string(key));
keyPublished = true;
lastPublish = millis();
get_char(myKey);
}
else{
if (keyPublished && (millis() - lastPublish) >= resetTimeB){
char_sensor->publish_state("");
keyPublished = false;
}
}
}
};
light_effects.yaml:
# effects:
- strobe:
name: strobe_alarm_away_armed
colors:
- state: True
brightness: 50%
red: 100%
green: 0%
blue: 0%
duration: 500ms
- state: False
duration: 500ms
- strobe:
name: strobe_alarm_home_armed
colors:
- state: True
brightness: 50%
red: 0%
green: 0%
blue: 100%
duration: 500ms
- state: False
duration: 500ms
- strobe:
name: strobe_alarm_tripping
colors:
- state: True
brightness: 50%
red: 100%
green: 100%
blue: 0%
duration: 250ms
- state: True
brightness: 50%
red: 100%
green: 50%
blue: 100%
duration: 250ms
- strobe:
name: strobe_alarm_tripped
colors:
- state: True
brightness: 50%
red: 100%
green: 0%
blue: 0%
duration: 250ms
- state: True
brightness: 50%
red: 0%
green: 0%
blue: 100%
duration: 250ms
As I mention in comment, large parts of code are not mine, however I ‘glued’ it together to suit my needs.
Enjoy & best regards!