Wiegand Code Writing ESPHome + Home Assistant

Ok I’m very new to programming, ESPHome, HA, all of that. So please bear with me.

I bought a Wiegand 26 door code input that I’m trying to set up. Having troubles with “how” to write the code. Yeah, I’ll probably need help with the exact later, but more structurally, I’m trying to figure out how I want to set it up (and be somewhat safe).

Here’s what I want to do. I want to have a list of “permenant codes”, family members and such that really won’t get deleted. But I also want to be able to add and remove temporary codes fairly easily (without necessarily having to do an OTA update to the ESP32 every time). So, I just had this idea, “what if I made the lists of codes in the !secret.yaml file”.
In the secret.yaml, I would have

doorcodes:
  - "1111"
  - "2222"
  - "3333"

dogwalkercodes:
  - "8888"
  - "9999"

Would this work and how can I have ESP verify a code entered into the pad against either of these lists? And, I would like to have an input boolean or something that can “shut off” the dogwalkercodes. This will probably happen in automations, but basically the ability to instantly freeze the dogwalker codes, or not used at certain times (at night), etc.
I originally thought about making input_booleans for every code, but that’s like 10 codes, and I really want to be able to go into the secret.yaml file from my phone and add a new code if the wife texts me that a new person is coming over.

Ultimately, I want to set up sensors (or something) so that when the door is activated by a dogwalker or non-immediate family member, that the cameras will start recording, maybe alert my phone. I can handle the automations later, but just don’t know how to set up the structure and get started.

hell, I don’t even really know how I’m setting up the Wiegand, I’m just copying/pasting code until something works. Some user codes have if statements built into the template sensors, others have it built into the Key Collector on_value command.

Thanks for any insight!

No. The secret.yaml file is only read at compile time.

Maybe create a Home Assistant sensor on your device that points to a helper in HA containing the codes.

Thanks for the quick reply. Again, apologies for being elementary.
So what you’re saying I do, is create an input_text helper that lists all the codes (maybe 2, one for family, the other for dogwalkers), then create a sensor to reference the list of codes? Can you help me on the sensor part?

or how about this? basically checking if the returned string keyCode is within one of those two databases?

sensor:
  - platform: template
    name: Keypad Code
    id: keyCode    
    on_value:
      - if:
          condition:
            - lambda: |
                {{ 'return id(keyCode).state' in ['states('input_text.codes')', 'states('input_text.dogcodes') ] }}
          then:  
            - homeassistant.service: 
                service: cover.toggle
                data: 
                 entity_id: cover.garage_door   

Except lambda in ESPHome is C++, not Jinja like it is in HA.

But yes that’s what I had in mind.

EDIT: longer version of this worked - tried shortening and it broke…

Here is some lambda that does what you were trying to do.

csl is a comma separated list containing all the valid pins. x always contains the state of the sensor you are triggering on.

You will need to create a Template Text Sensor with id: csl that loads the values from your HA input _texts into it.

sensor:
  - platform: template
    name: Keypad Code
    id: keycode    
    on_value:
      then:
        - if:
            condition:
              lambda: |-
                return (id(csl).state.find(x) >= 0);
            then:
              - homeassistant.service: 
                  service: cover.toggle
                  data: 
                    entity_id: cover.garage_door   

Ok - trying to keep this as short as possible, but this one works. Adjust for the length of whatever PIN you use.

  - platform: template
    name: Keypad Code
    id: keycode    
    on_value:
      then:
        - if:
            condition:
              lambda: |-
                if (x.length() == 4) {
                  return (id(csl).state.find(x) != std::string::npos));
                } else {
                  return false;
                }
            then:
              - homeassistant.service: 
                  service: cover.toggle
                  data: 
                    entity_id: cover.garage_door   
1 Like

Thanks a million, I’ll give it a shot tomorrow when I get a bit of time and see! Thanks!

I found this while setting up mine. He has created input selects to toggle user access from the HA front end as well as create new users on the fly and set their code or tag id number. Everything you are wanting to do can be found in this program. If you have a keypad w/tag scanner you could just use his program in it’s entirety. Mine is similar to what you want but i didn’t set up a way to grant or disable access to certain codes/users but you could easily do this by creating an input select for each user/code and then use it as a condition in the lambda so it checks to see if the code matches AND if the input select for the user is ON/OFF.

sensor:

  - platform: template
    name: Keypad Code
    id: keyCode    
    on_value:
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 6722842;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Mike's Tag"
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 1050;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Justin's Code"          

      - if:
          condition:
            - lambda: 'return id(keyCode).state == 6422842;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Paula's Tag"
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 1955;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Paula's Code"    

      - if:
          condition:
            - lambda: 'return id(keyCode).state == 5553549;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Golf Cart Tag"                

      - if:
          condition:
            - lambda: 'return id(keyCode).state == 6491970;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Mike's Tag"
         
      - if:
          condition:
            - lambda: 'return id(keyCode).state != 5553549;' 
            - lambda: 'return id(keyCode).state != 6422842;' 
            - lambda: 'return id(keyCode).state != 6722842;'
            - lambda: 'return id(keyCode).state != 6491970;'
            - lambda: 'return id(keyCode).state != 6753957;'
            - lambda: 'return id(keyCode).state != 1050;'
            - lambda: 'return id(keyCode).state != 1955;'
          then:
            - text_sensor.template.publish:
                id: last_user
                state: "Invalid User Access"

wiegand:
  - id: mykeypad
    d0: D7
    d1: D6
    on_key:
      - lambda: ESP_LOGI("KEY", "received key %d", x);
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - sensor.template.publish:
         id: keyCode
         state: !lambda "return parse_number<float>(x).value();"   
     

     
          
    on_raw:
      - lambda: ESP_LOGI("RAW", "received raw %d bits, value %llx", bits, value);

key_collector:
  - id: pincode_reader
    source_id: mykeypad
    min_length: 4
    max_length: 5
    end_keys: "#"
    end_key_required: true
    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)" ]
    on_result:      
      then:
        - sensor.template.publish:
            id: keyCode
            state: !lambda "return parse_number<float>(x).value();"         
                
    on_timeout:
      - logger.log:
          format: "input timeout: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]    

And then a text sensor that displays the last user to use the keypad/tag reader

text_sensor:   

  - platform: template
    name: Last User
    id: last_user
    icon: mdi:clock-start    

Thanks for your reply! I was looking over your code yesterday and tried to use it as a template and couldn’t get it working on my module, I was getting frustrated. But then I later learned that I have to connect a common ground to the module along with d0 and D1 so I think that’s why I couldn’t get anything working so when I get home later today I’m going to mess with it. I appreciate the help!!

oh ya. Just about anytime you have a device with an esp board they need a common ground, 12v, 24, 5v, it doesn’t matter.

makes complete sense. and I knew that as a general principle lol, but none of any documentation mentioned it and my instruction book that came with my keypad is garbage at best! busy busy weekend, hopefully I can have a couple hours freetime this weekend and I’ll report back!

I have everything mostly working now! Thank you!

Question though, I really want to be able to change codes from within HA on the fly. The list option that zoogara gave me didn’t work unfortunately, but no biggie.

But this is more of a general lambda question:
How can I compare the returned string to a sensor value? Because I can make an input_text sensor and report the value to ESPHome?

Example: I have/can make a text sensor

  - platform: homeassistant
    name: personscode
    id: personscode

So where you have:

I want to do something like this:

- lambda: 'return id(keyCode).state == id(personscode).state;'

It gives me this error:

/config/esphome/garage-keypad.yaml: In lambda function:
/config/esphome/garage-keypad.yaml:181:29: error: no match for ‘operator==’ (operand types are ‘float’ and ‘std::__cxx11::string’ {aka ‘std::__cxx11::basic_string’})
- lambda: ‘return id(keyCode).state == id(dogwalkercode).state;’

One more thing, is there a way for the lambda to check multiple numbers on a single line (to reduce code lines)…

ie:

- lambda: 'return id(keyCode).state == 1111, 2222, 3333 ;

I feel your pain, my manual was written in broken english and the directions for putting it into wiegand26 output were actually backwards, it was a nightmare. You’d have to post more of your code, it looks like the error is because your trying to compare a float and a string but i can’t tell because i don’t know what the type is for dogwalkercode.state. Is this is a sensor? an integer? a string? like it returns a person’s name? I think there needs to be an “if” statement to compare those two as well like this.

- if:                            
                condition:
                    - lambda: 'return id(Keypad_code).state == id(user_1_code);'
                then: .....

as far as comma seperated values like that, i’m not sure. I personally find it easier to read and keep organized if they are seperated statements instead of seperated by commas. I’m sure you can do that, i just don’t know how.

post your code, i’ll try to help you out if you want.

I think I’m going to make a binary sensor that reports “true” when x string and the input_text matches, then make a conditional statement that “if binary_sensor = true”. I can’t find it, but I read about doing that. Messing with it now.

esphome:
  name: garage-keypad
  friendly_name: Garage Keypad


esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

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

ota:
  password: "xxxxxxx"

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

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

captive_portal:

binary_sensor:
###-----the next 3 are the input boolean toggle switches that I can control if certain groups are active or not----------------------------
  - platform: homeassistant
    name: "Immediate Family Entry"
    id: immediatefamilyentry
    entity_id: input_boolean.garage_code_immediate_family

  - platform: homeassistant
    name: "Guests Family Entry"
    id: guestsentry
    entity_id: input_boolean.garage_code_guests

  - platform: homeassistant
    name: "Dog Walkers Entry"
    id: dogwalkersentry
    entity_id: input_boolean.garage_code_dog_walkers

  - platform: status
    id: keypad_status
    name: Garage Keypad Status 

#----------------change this to garage door when working
switch:
  - platform: template  
    id: OvrHead
    optimistic: true 
    on_turn_on:
      - homeassistant.service:
          service: cover.toggle
          data: 
            entity_id: cover.garage_door
      - delay: 1s
      - switch.turn_off: OvrHead

button:
  - platform: restart
    name: "Keypad Restart"
  - platform: safe_mode
    name: "Keypad (Safe Mode)"  
 

text_sensor:  

  - platform: template
    name: Uptime Garage Keypad
    id: uptime_human_wg26
    icon: mdi:clock-start

  - platform: template
    name: Last User
    id: last_user
    icon: mdi:clock-start   

  - platform: homeassistant
    name: "Dog Walker's Code"  
    id: dogwalkercode
    entity_id: input_text.dog_walker_code
    
sensor:

  - platform: template
    name: Keypad Code
    id: keyCode    
    on_value:
   #------------------------Justin Brittany codes--------------------------------   
      - if:
          condition:
            - binary_sensor.is_on: immediatefamilyentry
            - lambda: 'return id(keyCode).state == 9012;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Brittany's Code"
      - if:
          condition:
            - binary_sensor.is_on: immediatefamilyentry
            - lambda: 'return id(keyCode).state == 1948;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Justin's Code"          

#--------------------------Jack Simon Caiti Donna Hunter Lindsey codes-------------------------------
      - if:
          condition:
            - binary_sensor.is_on: guestsentry
            - lambda: 'return id(keyCode).state == 1224;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Jack's Code"
      - if:
          condition:
            - binary_sensor.is_on: guestsentry
            - lambda: 'return id(keyCode).state == 3025;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Simons's Code"    

      - if:
          condition:
            - binary_sensor.is_on: guestsentry
            - lambda: 'return id(keyCode).state == 1017;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Caiti's Code"

      - if:
          condition:
            - binary_sensor.is_on: guestsentry
            - lambda: 'return id(keyCode).state == 5028;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Donnas's Code"

      - if:
          condition:
            - binary_sensor.is_on: guestsentry
            - lambda: 'return id(keyCode).state == 1006;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Hunter's Code"   

      - if:
          condition:
            - binary_sensor.is_on: guestsentry
            - lambda: 'return id(keyCode).state == 2020;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Lindsey's Code"         

#-------------------Dog Walker Codes ---------------------------------


#### ----------------- this is where I want to have it check the input passcode against an input text from home assistant----------
      - if:
          condition:
            - binary_sensor.is_on: dogwalkersentry
            - lambda: 'return id(keyCode).state == 4157;' ### basically change 4157 to whatever the input text 'dogwalkercode' is
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Dog 4157 Code"

      - if:
          condition:
            - binary_sensor.is_on: dogwalkersentry
            - lambda: 'return id(keyCode).state == 9255;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Walker 9255 Code"     

      - if:
          condition:
            - binary_sensor.is_on: dogwalkersentry
            - lambda: 'return id(keyCode).state == 4335;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Walker 4335 Code"                           
         
      - if:
          condition:
            - lambda: 'return id(keyCode).state != 9012;' 
            - lambda: 'return id(keyCode).state != 1948;' 
            - lambda: 'return id(keyCode).state != 1224;'
            - lambda: 'return id(keyCode).state != 3025;'
            - lambda: 'return id(keyCode).state != 1017;'
            - lambda: 'return id(keyCode).state != 5028;'
            - lambda: 'return id(keyCode).state != 1006;'
            - lambda: 'return id(keyCode).state != 2020;'   
            - lambda: 'return id(keyCode).state != 4157;'
            - lambda: 'return id(keyCode).state != 9255;'
            - lambda: 'return id(keyCode).state != 4335;'                                             
          then:
            - text_sensor.template.publish:
                id: last_user
                state: "Invalid User Access"



  - platform: wifi_signal    
    id: wifi_signal_db
    update_interval: 300s
    entity_category: "diagnostic"

  - platform: uptime #Uptime in Seconds
    name: Garage Keypad Uptime
    id: uptime_sensor_wiegand
    update_interval: 240s
    internal: True
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human_wg26
            state: !lambda |-
              int seconds = round(id(uptime_sensor_wiegand).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? String(days) + "d " : "") +
                (hours ? String(hours) + "h " : "") +
                (minutes ? String(minutes) + "m " : "") +
                (String(seconds) + "s")
              ).c_str();

wiegand:
  - id: mykeypad
    d0: GPIO33
    d1: GPIO32
    on_key:
      - lambda: ESP_LOGI("KEY", "received key %d", x);
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - sensor.template.publish:
         id: keyCode
         state: !lambda "return parse_number<float>(x).value();"  
      - homeassistant.tag_scanned: !lambda 'return x.c_str();'
          
    on_raw:
      - lambda: ESP_LOGI("RAW", "received raw %d bits, value %llx", bits, value);

key_collector:
  - id: pincode_reader
    source_id: mykeypad
    min_length: 4
    max_length: 4
    end_keys: "#"
    end_key_required: false
    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)" ]
    on_result:      
      then:
        - sensor.template.publish:
            id: keyCode
            state: !lambda "return parse_number<float>(x).value();"         
                
    on_timeout:
      - logger.log:
          format: "input timeout: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]  

You should just need an input select and then add the users name’s you want to toggle on/off and for each name you will need a binary sensor. When a name is selected the binary sensor is toggled ON. When a code is entered you then make 2 conditional checks, is it a valid code and does that code owner have access(from the input select/binary sensor)

I’m not sure why you did it this way, making your sensors in HA but you’d be better off making them in your esphome config. If you keep it all on the esp, it can work independently of HA if for some reason HA or the network was down you would be able to toggle user access from the fallback api. It’s really not any different, you’re still going to have the same binary sensors except instead of the states being read from HA by the esp board, HA is instead reading the sensor states that are being sent from the esp. keeping it all on the esp gives you redundancy control options. You do it however you want, that’s just my 2 cents…

binary_sensor:
###-----the next 3 are the input boolean toggle switches that I can control if certain groups are active or not----------------------------
  - platform: homeassistant
    name: "Immediate Family Entry"
    id: immediatefamilyentry
    entity_id: input_boolean.garage_code_immediate_family

  - platform: homeassistant
    name: "Guests Family Entry"
    id: guestsentry
    entity_id: input_boolean.garage_code_guests

  - platform: homeassistant
    name: "Dog Walkers Entry"
    id: dogwalkersentry
    entity_id: input_boolean.garage_code_dog_walkers

  - platform: status
    id: keypad_status
    name: Garage Keypad Status 

i’d do something like this personally.

sensor:
  - platform: template
    name: "Guests Family Entry"
    id: guestsentry

  - platform: template
    name: "Immediate Family Entry"
    id: immediatefamilyentry

and then the select.

select:
  - platform: template
    name: "Template select"
    id: select
    optimistic: true
    options:
      - " "
      - one
      - two
      - three
    initial_option: " "
    on_value:
      then:
        - lambda: |-
            if (id(select).state == "one") {
              id(guestsentry).turn_on();
            } else if (id(select).state == "two") {
              id(immediatefamilyentry).turn_on();
            }
1 Like

I got it working finally with a series of template switches and sensors. Probably not the most efficient way, but it’s working finally!!

sensor:
  - platform: homeassistant
    name: "Dog Walker's Code 1"  
    id: dogwalkercode1
    entity_id: input_text.dog_walker_code_1
binary_sensor:
  - platform: homeassistant
    name: "Dog Walkers Entry"
    id: dogwalkersentry
    entity_id: input_boolean.garage_code_dog_walkers
switch:
  - platform: template  
    id: garagedoor
    optimistic: true 
    on_turn_on:
      - homeassistant.service:
          service: cover.toggle
          data: 
            entity_id: cover.garage_door
      - delay: 1s
      - switch.turn_off: garagedoor
      
  - platform: template
    id: dog1match
    optimistic: true
    name: "Dog Walker Code 1 Match"
    lambda: |-
      if (id(keyCode).state == id(dogwalkercode1).state) {
        return true; }
        else {
          return false;
      }  
    on_turn_on:
      - if:
          condition:
            - binary_sensor.is_on: dogwalkersentry
          then:
            - switch.turn_on: garagedoor
            - delay: 1s   
            - text_sensor.template.publish:
                id: last_user
                state: "Dog Walker Custom Code" 

Basically I have an 3 input_texts in HA that I can put in a specific, changeable 4-digit codes. I also have 3 input_booleans, that I can turn on and off for each category (household members, guests, and dog walkers). When an input is made, it runs the Wiegand code through the ‘keyCode’ sensor, then if the input_text and keyCode sensor match (and the dogwalkerentry boolean is “on”), it will fire the ‘garagedoor’ actions.

It ain’t pretty but it’s working. Thanks for everyone’s guidance!