A while back, I set up keyfob access using Home Assistant and ESPHome. It required bodging an RFID reader onto my doorbell, and after some time its 3D printed enclosure has yellowed. Today, out of the blue, an idea: ditch the keyfobs and scanner for QR codes.
Scroll to the bottom if you just want the GitHub link.
The plan was simple. First, eat the doorbell’s RTSP stream. Feed it into OpenCV or something and detect QR codes. Then, throw the codes at the Home Assistant API.
It ended up being easier than I thought.
Here’s the code for the initial proof of concept. Yep, that’s all of it. It took about 15 minutes to write and test.
import sys import re from datetime import datetime, timedelta import cv2 import homeassistant_api as ha import yaml try: from yaml import CLoader as Loader, CDumper as Dumper except ImportError: from yaml import Loader, Dumper DEBOUNCE_PERIOD = timedelta(seconds=5) TAG_ID_PATTERN = re.compile('https://www.home-assistant.io/tag/([0-9a-f-]+)') def main(config): with open(config, 'r') as fh: config = yaml.load(fh, Loader=Loader)['ha-cam-tag'] stream = cv2.VideoCapture(config['camera']['stream']) detector = cv2.QRCodeDetector() last_time, last_tag = None, None with ha.Client(config['homeassistant']['uri'], config['homeassistant']['auth-token']) as client: while stream.isOpened(): _, frame = stream.read() if data := detector.detectAndDecode(frame): if m := TAG_ID_PATTERN.match(data): cur_time, tag_id = datetime.now(), m.group(1) if last_tag != tag_id or last_time < cur_time - DEBOUNCE_PERIOD: client.fire_event("tag_scanned", tag_id=tag_id, device_id=config['camera']['device-id']) last_time, last_tag = cur_time, tag_id stream.release() cv2.destroyAllWindows() return 0 if __name__ == "__main__": sys.exit(main(*sys.argv[1:]))
The final version has the detector running in a separate thread (this helps with latency), and some boilerplate to make it easier to run, but the core concept is the same.
This requires just three packages: pyyaml, opencv-python, and homeassistant_api.
It eats a config file that looks like this:
ha-cam-tag: homeassistant: uri: https://homeassistant.example.com/api/ auth-token: "secret-token" camera: device-id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa stream: rtsp://10.1.20.xxx:7447/xxxxxxxxxxxxxxxx
I’m using the high resolution RTSP stream on my Unifi doorbell. The medium level worked too, but was a bit less reliable.
I’ve got this running in a separate container from Home Assistant on my Proxmox box for now, but I want to port it to a proper extension eventually.
Here’s the repo: https://github.com/tmick0/ha-cam-tag