Keycard door access in Home Assistant

I first started fantasizing about keycard access to my home when Ubiquiti announced the G4 Doorbell Pro. I’ve had the original G4 Doorbell for a few years now, and it’s been great. However, when the G4 Doorbell Pro finally came out of Early Access, it lacked the RFID feature that was originally advertised.

Based on community consensus, it doesn’t seem like we’ll be getting that feature anytime soon, so I decided to build my own RFID reader that would semi-integrate with the G4 Doorbell I already had.

Using ESPHome, an inexpensive NFC module, and a 3D printed enclosure, I sort of got what I wanted.

Home Assistant has built-in support for RFID tags, which I could leverage to trigger an automation. All I really needed to do was figure out how to feed it tags, and find a lock that would integrate.

I ended up using a Kwikset Convert to add Zigbee control to the back of my Schlage deadbolt, since it was less ugly than Schlage’s own options and I didn’t want to rekey everything again.

I borrowed the BOM from an existing Home Assistant tag reader project and added some components to power it. I changed the WS2812 for an SK9822, since I already had some on the shelf. I also removed the buzzer since I wouldn’t really want to use it anyway.

My original plan was to make this device wireless, but the ESP8266 makes deep-sleep hard to work with, so the 820 mAh battery I originally speced only lasted 6 hours. Thus, I resorted to hardwiring.

Using the 1N4001 as a half-wave rectifier with some buck converters I already had, I was able to get a usable 5V DC from the doorbell’s 18V AC circuit. The converter’s built-in 100uF capacitor was sufficient to smooth the rectified voltage.

I forgot to take a picture of the hardwired version, so here’s the guts from the battery-powered iteration:

After assembly, I had to figure out the correct incantations to get everything working in ESPHome. I modified the yaml from the above project, added some other stuff from the internet, and ended up with this:

esphome:
  name: door_card_reader
  on_boot:
    priority: -10
    then:
    - wait_until:
        api.connected:
    - light.turn_on:
        id: status_led
        brightness: 100%
        red: 0%
        green: 0%
        blue: 100%
        flash_length: 2000ms

esp8266:
  board: esp01_1m
  framework:
    version: 2.7.4 # required for fastled on this platform

logger:

api:
  encryption:
    key: (removed)

ota:
  password: (removed)

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

captive_portal:

i2c:
  scan: False
  frequency: 400kHz

time:
- platform: sntp
  on_time:
  - seconds: /10
    then:
      if:
        condition:
          api.connected:
        then:
        - light.turn_on:
            id: status_led
            brightness: 50%
            red: 0%
            green: 0%
            blue: 100%
            flash_length: 250ms
        else:
        - light.turn_on:
            id: status_led
            brightness: 50%
            red: 100%
            green: 0%
            blue: 0%
            flash_length: 250ms

pn532_i2c:
  id: pn532_board
  on_tag:
    then:
    - if:
        condition:
          api.connected:
        then:
        - light.turn_on:
            id: status_led
            brightness: 100%
            red: 0%
            green: 100%
            blue: 0%
            flash_length: 500ms
        - delay: 0.15s
        - homeassistant.tag_scanned: !lambda |-
            return x;
        else:
        - light.turn_on:
            id: status_led
            brightness: 100%
            red: 100%
            green: 0%
            blue: 0%
            flash_length: 500ms
        - delay: 0.15s
  on_tag_removed:
    then:
    - homeassistant.event:
        event: esphome.tag_removed

light:
  - platform: fastled_spi
    id: status_led
    internal: true
    chipset: APA102
    clock_pin: 14
    data_pin: 13
    num_leds: 1
    rgb_order: BGR
    name: "Door Card Reader Status LED"

sensor:
  - platform: wifi_signal
    name: "Door Card Reader Wifi Signal dB"
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"

  - platform: copy
    source_id: wifi_signal_db
    name: "Door Card Reader Wifi Signal Percent"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "Signal %"
    entity_category: "diagnostic"

It’ll do nothing special with tags, and just report them directly to Home Assistant. It also has a nice status LED functionality.

To avoid drilling a second hole in my door trim, I designed a bracket which would mount behind the Unifi Doorbell and give me some space to splice the wires. The enclosure for the card reader would essentially just be a box that I’d shove everything into, because I was too lazy to design any mounting features.

The model is available on Thingiverse.

This is what it looks like installed:

Ignore the doorbell’s complaint about Wifi - it had just booted up and apparently hadn’t found the AP yet.

So far it is serving me well. It gives me the ability to hand out dirt-cheap tags instead of get traditional keys copied every time I need someone to be able to get into my house. This is great for guests and petsitters who you’d otherwise have to trust to return the spare key before you need to give it to the next person.

These tags also have the benefit of being easily revocable credentials. It’s a lot harder to rekey all my locks than it is to remove the automation for a tag in Home Assistant. This would mean a cloned credential also has less impact than a cloned key.