Matrix keypad pin code

Hi, I am using the new matrix keypad functionality fine but I am such a newbie, I can’t figure out how to check for a valid pincode.

My attempt so far, which obivously doesnt work:

matrix_keypad:
  id: mykeypad
  rows:
    - pin: 33 #0
    - pin: 32 #1
    - pin: 23 #2
    - pin: 22 #3
  columns:
    - pin: 16  #0
    - pin: 5 #1
    - pin: 17 #2
    - pin: 4  #3
  keys: "123A456B789C*0#D"
  has_diodes: false

key_collector:
  - id: pincode_reader
    source_id: mykeypad
    min_length: 4
    max_length: 4
    end_keys: "#"
    end_key_required: true
    back_keys: "*"
    clear_keys: "C"
    allowed_keys: "0123456789"
    timeout: 5s
    on_result:
      - if:
          condition:
            lambda: 'return(x) = 1234'
          then:
            - logger.log: 'Success!'

Just guessing as I have not used this yet but if you require the end key does that mean your length must be at least 5 to match “1234”?

Don’t think so, the official example throws “on_result” when length reaches 4:

    on_result:
      - logger.log:
          format: "input result: '%s', started by '%c', ended by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)" ]

I’m not strong on esphome lambdas or c++, but this looks wrong to me:

lambda: 'return(x) = 1234'

A single equal sign is an assignment, not a comparison. The paranthesis around x doesn’t seem to match other lambda examples I’ve seen either.

You’re also missing “then:” at the beginning of the on_result action block. Looking at the docs for actions in automations, I think that may be a mistake in the key collector docs. Also in the key_collector docs, the x variable in the lambda (the collected result of the keys pressed) is described as of type vector<uint8_t>, which would need to be converted to a string if you want to compare it to another string (I think…this is where my c++ weakness shows).

So going from the examples on the esphome docs on lambdas and key collector, and my own admittedly minimal understanding of c++, I think this is closer to what you’re looking for:

  on_result:
    then:
    - if: 
      condition: 
        lambda: 'return x.c_str() == "1234"'
      then:
        - logger.log: 'Success!'

Pessimistically assuming this also won’t work, please post the compile time or runtime error if possible. It might give a hint what else might be wrong.

This compiles fine, but doesn’t give Success when entering 1234 and confirmed with “#”.

    on_result:
      - if:
          condition:
            lambda: 'return x.c_str() == "1234";'
          then:
            - logger.log: 'Success!'

Fair warning, I can’t test any of this myself currently, and I’m making a lot of assumptions here :smile:

What’s in the log if you log the result with the c_str conversion as shown in the example?

on_result:
  then:
      - logger.log:
          format: "input result: '%s', started by '%c', ended by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)" ]
      - if:
          condition:
            lambda: 'return x.c_str() == "1234";'
          then:
            - logger.log: 'Success!'

I’d say there’s a good chance that the x string includes the #

Slightly off topic but I made a 3d printed case for a matrix keypad alarm keypad you may find it useful: ESP32 Alarm Keypad

My code is arduino so I’m not sure about the esphome setup

1 Like

@gonzotek did you make any progress here? I’m looking for the same sollution, for making a geocache with keypad…

Sorry, I don’t have a matrix keypad to test on myself and @criuz hasn’t replied, was relying on them to provide feedback. I may pick one up to play with sometime and if I do, I’ll update with results.

I managed to display a page when the right code is entered. It is not quite a clean code but it works.

    on_result:
      then:
        - text_sensor.template.publish:
            id: keypad
            state: !lambda "return x.c_str();"  
        - if:
            condition:
              lambda: 'return id(keypad).state == "1234";'

            then:
             - globals.set:
                id: current_page_num
                value: '2'
             - delay: 5s
             - globals.set:
                id: current_page_num
                value: '1'

I’m now working on ‘live’ displaying the input… This thing is quite bad documented…

I’m a fast learning person :wink:

it.printf(3, 1, "%s", id(keypadp).state.c_str());  

Is the code to display live input… And don’t forget to make template text sensors…

text_sensor:
  - platform: template
    name: "Keypad code"
    id: keypad

  - platform: template
    name: "Keypad code"
    id: keypadp

Why there are 2 text sensors? Is it possible to share your complete esphome yaml code?

Here you go :slight_smile:
If you have any additions to make the code better, i’d like to hear :wink:

esphome:
  name: esphome-web-30dbbc
  friendly_name: Masterpiece

esp32:
  board: esp32dev
  framework:
    type: arduino


# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "************************************"

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "*********************"
    password: "***************"

captive_portal:

i2c:
  sda: 23
  scl: 21

globals:
   - id: current_page_num
     type: int
     restore_value: no
     initial_value: '1'

display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x27
    id: lcd1
    update_interval : 0.5 s
#it.strftime(1, 1, "%d-%m-%y %H:%M", id(my_time).now());
    lambda: |-
      if (id(current_page_num) == 1) {
          it.print(2, 0, "Masterpiece");
          it.print(1,1, "Voer code in!");
        }
      if (id(current_page_num) == 2) {
          it.print(2, 0, "Masterpiece!");
          it.print(1, 1, "You Found It!");
        }
      if (id(current_page_num) == 3) {
          it.print(2, 0, "Masterpiece!");
          it.print(3, 1, "Foute Code");      
        } 
      if (id(current_page_num) == 4) {
          it.print(2, 0, "Uw invoer is:");
          it.printf(4, 1, "%s", id(keypadp).state.c_str());      
        }
      if (id(current_page_num) == 5) {
          it.print(2, 0, "Masterpiece!");
          it.print(4, 1, "See ya!");
        }
       
time:
  - platform: homeassistant
    id: my_time

pcf8574:
  - id: 'pcf8574_hub'
    address: 0x20
    pcf8575: false

matrix_keypad:
  id: mykeypad
  rows:
    - pin:
        pcf8574: pcf8574_hub
        number: 0
        mode: OUTPUT
        inverted: False
    - pin:
        pcf8574: pcf8574_hub
        number: 1
        mode: OUTPUT
        inverted: False        
    - pin:
        pcf8574: pcf8574_hub
        number: 2
        mode: OUTPUT
        inverted: False
    - pin:
        pcf8574: pcf8574_hub
        number: 3
        mode: OUTPUT
        inverted: False
  columns:
    - pin:
        pcf8574: pcf8574_hub
        number: 4
        mode: INPUT
        inverted: False
    - pin:
        pcf8574: pcf8574_hub
        number: 5
        mode: INPUT
        inverted: False
    - pin:
        pcf8574: pcf8574_hub
        number: 6
        mode: INPUT
        inverted: False
    - pin:
        pcf8574: pcf8574_hub
        number: 7
        mode: INPUT
        inverted: False
  keys: "123A456B789C*0#D"

key_collector:
  - id: pincode_reader
    source_id: mykeypad
    min_length: 8
    max_length: 8
    end_keys: "#"
    end_key_required: true
    back_keys: "*"
    #clear_keys: "*"
    allowed_keys: "0123456789"
    timeout: 5s
    on_progress:
      - logger.log:
          format: "input progress: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]
      - text_sensor.template.publish:
          id: keypadp
          state: !lambda "return x.c_str();"  
      - globals.set:
          id: current_page_num
          value: '4'
      
    on_result:
      then:
        - logger.log:
            format: "input result: '%s', started by '%c', ended by '%c''x.c_str()'"
            args: [ 'x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)" ]
        - text_sensor.template.publish:
            id: keypad
            state: !lambda "return x.c_str();"  
        - if:
            condition:
              lambda: 'return id(keypad).state == "*********";'

            then:
             - delay: 1s
             - switch.turn_on: cache_relay_1
             - delay: 100ms
             - globals.set:
                id: current_page_num
                value: '2'             
             - delay: 5s
             - switch.turn_off: cache_relay_1

             - delay: 10s
             - globals.set:
                id: current_page_num
                value: '5' 
             - delay: 30s            
             - globals.set:
                id: current_page_num
                value: '1'
            else:
             - delay: 1s
             - globals.set:
                id: current_page_num
                value: '3'
             - delay: 5s
             - globals.set:
                id: current_page_num
                value: '1'   

    on_timeout:
      - logger.log:
          format: "input timeout: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]


text_sensor:
  - platform: template
    name: "Keypad code"
    id: keypad

  - platform: template
    name: "Keypad code"
    id: keypadp

switch:
  - platform: gpio
    pin: 25
    name: "Masterpiece switch 1"
    id: cache_relay_1
2 Likes

Excellent, it’s working, thanks!!!

I can see something that it’s not correct. This code will constantly and periodically will send the secret code. Is there a way to stop from doing this?
The following picture is showing the secret code that it’s being sending continuously.

  name: keypad
  platform: ESP32
  board: esp32dev
  
    

esp32_ble_tracker:
  scan_parameters:
    interval: 1100ms
    window: 1100ms
    active: true

bluetooth_proxy:
  active: true

wifi:
  ssid: "XXXXXXXX"
  password: "XXXXXXX"
  manual_ip:
    static_ip: 192.168.3.69
    gateway: 192.168.3.1
    subnet: 255.255.255.0

# Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Keypad Fallback"
    password: "XXXXXXXX" 

    

captive_portal:

# Enable logging
logger:
  
# Enable Home Assistant API
api:
  

ota:
  password: "liakjim"

sensor:
  - platform: wifi_signal
    name: keypad_node WiFi Signal
    update_interval: 15s
    filters:
      - sliding_window_moving_average:
          window_size: 15
          send_every: 15
          send_first_at: 15
  - platform: uptime
    id: sensor_uptime

globals:
  - id: global_livingroom
    type: int
    restore_value: no
    initial_value: '0'
  - id: current_page_num
    type: int
    restore_value: no
    initial_value: '1'   

font:
  - file: "fonts/arial.ttf"
    id: my_font
    size: 20 

i2c:
  sda: GPIO23
  scl: GPIO22

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    lambda: |-
      if (id(current_page_num) == 1) {
          it.printf(10, 20, id(my_font), "%s", id(alarmo).state.c_str());
          it.print(10, 40, id(my_font), "Enter code");
        }
      if (id(current_page_num) == 2) {
          it.printf(10, 20, id(my_font), "%s", id(alarmo).state.c_str());
          it.print(1, 40, id(my_font), "Correct code");
        }
      if (id(current_page_num) == 3) {
          it.printf(10, 20, id(my_font), "%s", id(alarmo).state.c_str());
          it.print(10, 40, id(my_font), "Wrong code");      
        } 
      if (id(current_page_num) == 4) {
          it.printf(10, 20, id(my_font), "%s", id(alarmo).state.c_str());
          it.printf(10, 40, id(my_font), "%s", id(keypadp).state.c_str());      
        }
      if (id(current_page_num) == 5) {
          it.printf(10, 20, id(my_font), "%s", id(alarmo).state.c_str());
          it.print(30, 40, id(my_font), "Bye");
        }
         
matrix_keypad:
  id: mykeypad
  rows:
    - pin: 21
    - pin: 19
    - pin: 18
    - pin: 5
  columns:
    - pin: 17
    - pin: 16
    - pin: 4
  keys: "123456789*0#"
  has_diodes: false
  
key_collector:
  - id: pincode_reader
    source_id: mykeypad
    min_length: 4
    max_length: 4
    end_keys: "#"
    end_key_required: true
    back_keys: "*"
    clear_keys: "C"
    allowed_keys: "0123456789"
    timeout: 5s
    on_progress:
      - logger.log:
          format: "input progress: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]
      - text_sensor.template.publish:
          id: keypadp
          state: !lambda "return x.c_str();" 
      - globals.set:
          id: current_page_num
          value: '4'    
    on_timeout:
      - logger.log:
          format: "input timeout: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ] 
    on_result:
      then:
        - logger.log:
            format: "input result: '%s', started by '%c', ended by '%c''x.c_str()'"
            args: [ 'x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)" ]
        - text_sensor.template.publish:
            id: keypad
            state: !lambda "return x.c_str();"  
        - if:
            condition:
              lambda: 'return id(keypad).state == "XXXX";'

            then:
             - delay: 1s
             - globals.set:
                id: current_page_num
                value: '2'             
             - delay: 10s
             - globals.set:
                id: current_page_num
                value: '5' 
             - delay: 10s            
             - globals.set:
                id: current_page_num
                value: '1'
            else:
             - delay: 1s
             - globals.set:
                id: current_page_num
                value: '3'
             - delay: 3s
             - globals.set:
                id: current_page_num
                value: '1'            

binary_sensor:
  - platform: status
    name: keypad_node Status
    
  
  - platform: template
    name: "Living VPIR Sensor"
    device_class: motion
    id: vpir_livingroom  
    
  - platform: gpio
    pin: 26
    name: "Living room PIR Sensor"
    device_class: motion      
    internal: false
    on_multi_click:
      - timing:
        - ON for 0.1s to 6s
        then:
        - lambda: |-
            id(global_livingroom) += 1;
            if (id(global_livingroom) >= 2) {
              id(vpir_livingroom).publish_state(true);
            }             
      - timing:
        - OFF for at least 10s
        then:
        - lambda: |-
            id(global_livingroom) = 0;
            id(vpir_livingroom).publish_state(false);
  
switch:
  - platform: restart
    name: "Restart keypad_node"
    id: restart_switch

text_sensor:
  # Expose WiFi information as sensors.
  - platform: wifi_info
    ip_address:
      name: keypad_node  IP 
  - platform: template
    name: "Keypad code"
    id: keypadp
  - platform: template
    name: "Keypad code"
    id: keypad 
  - platform: homeassistant
    name: "Ntina's alarmo state"
    entity_id: alarm_control_panel.alarmo
    id: alarmo        
    internal: true  

You want to do it a different way IMO publishing mqtt data to topics instead of updating text sensors. I’m working on my own project at the moment, I’ll post the esphome code soon.

@liakjim I’ve just published my updated esphome version: ESP32 Alarm Keypad

1 Like

Thanks for this post!!! really saved my day.