As an IoT enthusiast and a professional in the field, I’ve always been intrigued by the potential of smart home technology. One area that has particularly piqued my interest is home security. Over the years, I’ve added several cameras around my house, creating a robust surveillance system. However, one piece of the puzzle always seemed to be missing: a smart door lock for keyless entry.
In my quest for the perfect smart lock, I stumbled upon the Ultraloq UBolt Pro. This nifty gadget seemed to be the answer to my prayers. It offered a smorgasbord of unlocking methods, including fingerprint, RFID, and Bluetooth proximity. The cherry on top? It had an option for Z-Wave connectivity, which meant it could potentially operate without any cloud service. However, there was a catch. While the lock could be controlled via Z-Wave, it didn’t provide information on who unlocked the door. It was like being handed a beautifully wrapped gift box, only to find it empty.
The Ultraloq UBolt Pro was a tantalizing piece of tech, but it was missing the one feature I craved the most: user identification. I wanted my smart lock to do more than just unlock the door. I wanted it to tell me who was entering my house. This would allow me to run different automations in Home Assistant based on the person entering. But alas, the UBolt Pro fell short in this regard.
I scoured the market for other options, but none offered the unique combination of features I was looking for. It became clear that if I wanted a lock that met all my requirements, I would have to build it myself.
And so, my journey to create a smart lock that could identify its users began. It was a path filled with obstacles and challenges, but also one that offered immense learning opportunities. In the end, I was able to create a system that not only identified the user but also integrated seamlessly with Home Assistant. This is the story of that journey.
In my quest for the perfect smart lock, I knew I needed to start with the basics. I needed a lock that could simply unlock the door. The fancy features like keypads, fingerprint scanners, and RFID readers could come later. My search led me to two promising contenders: the LevelLock and the August Smart Lock.
The LevelLock, with its sleek, minimalist design, seemed to be the perfect candidate. It was the epitome of simplicity – a straightforward motor to unlock the door. However, my experience with it was a rollercoaster ride. While I admired its simplicity, I ran into a few roadblocks. The first LevelLock I installed met an untimely demise due to a rather weak motor. If the door jammed, the lock would break, a significant flaw in the design.
Another hiccup with the LevelLock was its mandatory cloud service connection. It was like a clingy friend who wouldn’t let go. I couldn’t find a way to set up the lock without connecting it to the cloud, and disconnecting it afterward was like trying to solve a Rubik’s cube blindfolded. Once I managed to connect it to Home Assistant, it was like a shaky long-distance relationship, with frequent disconnects due to its reliance on Bluetooth.
On the flip side, the August Smart Lock was like the reliable friend who always has your back. It boasted a stronger motor and has been a smooth sailing journey so far. Like the LevelLock, it also required cloud account setup. However, once it was all set up and connected to Home Assistant as a HomeKit lock, it was like breaking free from the chains. I could disconnect it from the cloud. An added feather in its cap was its WiFi connectivity, which offered a more stable connection compared to the fickle Bluetooth.
After choosing the August Smart Lock as my base, it was time to embark on the next leg of my journey: finding a keypad that could seamlessly integrate with Home Assistant to control the lock. My quest led me to a sturdy, generic keypad on Amazon (link). This keypad was like a Swiss Army knife – well-built with a full metal housing, capable of reading fingerprints and RFID tags. It could either be a lone wolf controlling a connected striker lock, or it could be a team player, configured to output its inputs through a Wiegand interface.
If you’re scratching your head wondering what a Wiegand interface is, let me enlighten you. The Wiegand interface is a wiring standard used in RFID readers and keypads for transmitting data. It’s like the reliable postman of the tech world, delivering data from your keypad or reader to your control system.
When the keypad dons its Wiegand mode, it’s a breeze to configure. All number inputs and RFID card scans are like letters sent directly to the Wiegand controller’s mailbox. In our case, this mailbox will be an ESPHome device. However, the keypad has a unique way of dealing with fingerprints. Each print is programmed on the keypad and is sent to the Wiegand controller as a tag, like a special delivery, with the number of the print you select when programming.
To bridge the gap between the Wiegand controller and Home Assistant, I added the ESPHome addon to my Home Assistant installation, which is currently running on a Raspberry Pi 4. Creating an ESPHome device is as easy as pie. I purchased these ESP32 boards with a USB-C interface (link), plugged one into the USB port on my laptop, navigated to the ESPHome tab in Home Assistant (which I had already installed), clicked on the ‘New Device’ button in the bottom corner, and followed the instructions to flash the board. The web-based flashing tool works with Chrome and other Chrome-based web browsers like a charm.
Now lets talk about the wiring, and setting up the keypad. As mentioned earlier, the keypad I chose can operate in standalone mode or with a Wiegand controller. By default, it comes preconfigured for standalone mode. These instructions apply specifically to the keypad I purchased, so if you choose a different one, you’ll need to figure out this part on your own.
The keypad was wired with the green and white wires connecting to my ESP GPIO pins 22 and 23, as Wiegand D0 and D1 respectively. I connected the red and black wires to a 12-volt power supply. The specific power supply I used was a Recom RAC05, which you can purchase from Mouser here. I used this because I have and use them at work all the time, so it was readily available. If you live in the US and run 120v, you could probably use something like this LED driver here. The Recom power supply works all the way up to 277v, which is nice. I then also wired the 12v output to a 3.3v (or it might have been 5v) Low Drop Out voltage regulator like these. I used this to power the ESP.
The wiring for the keypad runs from the outside wall into the light switch box just inside the house. It was originally a 2-gang box with 2 switches, which is quite common. I had to remove the faceplate, disconnect the switches, remove the box, cut some drywall, and install a new 3-gang old work box. After reconnecting the switches, I ran the wiring for the keypad into the box. I tapped into the power that was already running to the box for the light switches to power the Recom PSU, then connected everything together using Wago connectors, and finished by just stuffing the electronics into the extra space in the box. I bought a new faceplate for the box that simply has 2 holes for the light switches, and a blank.
With everything wired up and the power turned back on, the keypad came alive – a good sign! It was like Frankenstein’s monster, but less scary and more useful. I should also mention that I wired the yellow wire on the keypad to one of the other GPIO pins for later use. This allows me to force the keypad to ‘beep’ when I pull the pin high.
Now that the keypad is powered, it’s time to configure it. We can enter ‘programming mode’ by pressing the * key, followed by the admin code, then #. The default admin code is 999999.
The first order of business should always be to change the admin code to something that you will remember. This is done by pressing the following sequence: 00#[new code]#[new code]#. Next, we need to set the keypad into reader mode, which is done by pressing 03#0#. Once the keypad is in reader mode, accessing ‘programming mode’ will be done differently, since reader mode would normally pass through all button presses to the Wiegand controller. You can now exit programming mode by pressing #.
Now we need to configure the esphome device to listen to the keypad. I started with the basics:
esphome:
name: esphome-web-15fb74
esp32:
board: nodemcu-32s
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "random encryption key here"
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Esphome-Web-15Fb74"
password: "IbVQZDb4c4Ea"
captive_portal:
YAMLThe encryption key for the API can be generated randomly. You can find more information about this on the API page of ESPHome (link).
With the basic ESPHome configuration in place, the next step was to configure the Wiegand interface. This took some time to figure out, but I eventually found a solution that worked for me.
Firstly, I configured a key collector component that collects all of our number presses into a 4-digit code. Secondly, I set up the system to send this data to Home Assistant by emitting an event under the esphome.keypaddb
namespace. While we don’t have anything to listen to this event yet, we will set that up shortly.
Remember when I mentioned that the keypad requires you to program the fingerprints ahead of time and assign an ID number to them, and that it sends that ID number as a tag over Wiegand? I decided to encode each fingerprint with a 4-digit ID, starting at 1000. This allows me to detect whether the length of the tag is longer than 4 digits, and if it is, it must be an actual RFID tag. I chose to do it this way because all of the fobs that came with the reader have a minimum of 5 digits with leading zeros.
Here’s the configuration I used:
wiegand:
- id: mykeypad
d0: GPIO22
d1: GPIO23
on_key:
- lambda: ESP_LOGI("KEY", "received key %d", x);
on_tag:
- lambda: ESP_LOGD("TEST", "received tag %s", x.c_str());
- if:
condition:
lambda: |-
return x.length() > 4;
then:
- homeassistant.event:
event: esphome.keypaddb.getTag
data:
code: !lambda 'return x.c_str();'
else:
- homeassistant.event:
event: esphome.keypaddb.getPrint
data:
code: !lambda 'return x.c_str();'
on_raw:
- lambda: ESP_LOGI("RAW", "received raw %d bits, value %llx, tag %s", bits, value, to_string((value >> 1) & 0xffffff).c_str());
key_collector:
- id: pincode_reader
source_id: mykeypad
min_length: 4
max_length: 4
end_keys: "#"
end_key_required: false
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)" ]
on_result:
- logger.log:
format: "input result: '%s', started by '%c', ended by '%c'"
args: [ 'x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)" ]
- homeassistant.event:
event: esphome.keypaddb.getPin
data:
code: !lambda 'return x.c_str();'
on_timeout:
- logger.log:
format: "input timeout: '%s', started by '%c'"
args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]
YAMLTo verify that everything is working, I opened up the ESPHome dashboard in Home Assistant and checked the logs for my ESP device. I pressed a few buttons on the keypad and saw the corresponding keypresses appear in the logs. This confirmed that the keypad was correctly wired and configured.
Now that the keypad is in reader mode, we can enter ‘programming mode’ by holding down the * key and waiting for about 3 seconds. Once we hear a beep, we can enter the admin code followed by #.
We can add fingerprints by pressing the following sequence: 11#[the ID of the fingerprint you want to add]#. The keypad will then prompt you to place the finger you want to program on the scanner. You need to scan the same finger twice to ensure a good read. After the second successful scan, the keypad will beep to indicate that the fingerprint has been successfully programmed. Press the * key to finish programming the print.
Remember, each fingerprint is assigned a unique 4-digit ID number, starting at 1000. This ID number is sent to the Wiegand controller as a tag when the fingerprint is scanned. You can program as many fingerprints as you like, just make sure each one has a unique ID number.
We can delete fingerprints in two ways. The first method is by scanning the fingerprint you want to delete: press 21#, scan the finger once, then press *. The second method is by using the ID number: press 21#[ID number]#*.
Press # to exit programming mode.
There are numerous other options for programming the keypad, including adding an admin add/delete card for quickly adding or removing fingerprints. However, these operations are all detailed in the instruction manual included with the keypad. Deciphering the instructions is honestly the hardest part – a big thank you to the manufacturers in China for providing such a challenge!
Now that we have the keypad set up and programmed, we need a way to authenticate the users who are trying to use the keypad. If you’re not interested in the details of how this works, you can simply add this repository to your Home Assistant addons and install the keypaddb
addon.
However, if you’re interested in understanding how Home Assistant addons and repositories work, let’s delve into that!
Home Assistant addons are essentially Docker containers that run alongside the Home Assistant server. They are designed to provide a way to extend the functionality of Home Assistant without having to modify the core codebase. This makes it easy for developers to create new features and for users to install and use these features.
A Home Assistant repository is a collection of addons that have been grouped together. When you add a repository to your Home Assistant instance, you gain access to all of the addons within that repository.
Exposing User Interface with Home Assistant Addons: Docker Web Server Setup and UI Integration
One of the powerful features of Home Assistant addons is their ability to expose a user interface (UI) to the Home Assistant dashboard. This allows users to interact with the addon directly from the Home Assistant interface, providing a seamless user experience. Let’s delve into the specifics of how this is achieved.
Web Server Setup: The addon’s Docker container needs to run a web server. This could be any web server like Apache, Nginx, or even a lightweight server like Flask for Python applications. The web server should be configured to serve the UI’s static files (HTML, CSS, JavaScript, images, etc.) and handle any necessary dynamic content or API endpoints.
Addon Configuration: In the addon’s configuration file (config.json
), the ingress
option needs to be set to true
. This tells Home Assistant that the addon will expose a UI. The ingress_port
option should also be set to the port that the web server inside the Docker container is listening on.
{
"name": "My Addon",
"version": "1.0.0",
"slug": "my_addon",
"description": "My Home Assistant Addon",
"ingress": true,
"ingress_port": 8080,
...
}
JSONIngress Handling: When Home Assistant sees that an addon has ingress
set to true
, it sets up a reverse proxy from the Home Assistant frontend to the addon’s web server. This allows HTTP requests from the frontend to be forwarded to the addon’s web server, and responses from the addon’s web server to be forwarded back to the frontend.
URL Generation: Home Assistant automatically generates a URL for each addon that has Ingress enabled. This URL is relative to the Home Assistant’s base URL and includes a unique path for the addon. For example, if your Home Assistant is accessible at http://homeassistant.local:8123
, an addon might be accessible at http://homeassistant.local:8123/api/hassio_ingress/unique-addon-path
.
Dashboard Integration: In the Home Assistant dashboard, addons with Ingress enabled will have an “OPEN WEB UI” button on their detail page. When you click this button, Home Assistant opens a new panel that loads the Ingress URL for the addon. This panel displays the web interface provided by the addon’s web server, allowing you to interact with the addon directly from the Home Assistant dashboard.
The content displayed in this panel is entirely provided by the addon’s web server. It could be a full web application, a simple form for configuring the addon, a status page, or any other web content. The addon’s web server is responsible for handling requests from the dashboard and returning the appropriate responses.
As a web developer, I’ve spent a significant amount of time working with Vue. It’s a framework I’ve come to know and love, and it’s been my go-to for many projects. But as much as I enjoy working with Vue, I’ve always been eager to explore new technologies and broaden my skill set.
When I started this project, I saw an opportunity to do just that. I hadn’t yet had the chance to work with Nuxt 3, and I knew that this project would require both a front-end and a back-end server. It seemed like the perfect time to dive into Nuxt 3 and see what it had to offer.
So, I decided to take the plunge. I was excited to explore the capabilities of Nuxt 3 and see how it could enhance this project.
Navigating New Terrain: Nuxt 3, SQLite, Prisma, and More
As I embarked on this project, I knew I was in for a unique challenge. I needed a database to store all the user and keypad data, but this wasn’t just any database. It needed to be provisioned anew with each installation of the addon on a different Home Assistant system. It had to be transferable, resilient against overwrites from reinstalls, and capable of running on devices with limited resources, like Raspberry Pis.
Enter SQLite, a lightweight, file-based database that seemed to fit the bill perfectly. But I didn’t stop there. I wanted to push the envelope further, to explore new tools and technologies. So, I decided to use Prisma, a powerful database ORM tool that focuses on creating TypeScript definitions for your database. Prisma allowed me to define my database schema, create a new database from that schema or link to an existing one, and create migrations for the database whenever a change was needed. It also provided type checking during development, a feature that proved invaluable for this project. And while I’m quite pleased with Prisma, I must admit, I’ve got my eye on a new competitor for future projects.
But the journey didn’t end there. I had to find a way to facilitate communication between the front and back ends of the application. While I initially thought Nuxt would provide a straightforward solution, I found that it wasn’t as simple as I’d hoped. So, I turned to tRPC, a project that promises to provide type safety between client and server code, given both are written in TypeScript. Despite some initial challenges due to its focus on React and Next.js, I managed to get it up and running with Nuxt. However, I must confess, the lack of documentation for Nuxt integration was a bit of a hurdle, and I’m still on the lookout for a more intuitive solution.
Of course, this application also needed to interact with Home Assistant, listening and firing events as required. This was a unique challenge specific to this project, and while I didn’t have an immediate solution, I knew it was a hurdle I would eventually overcome.
For the front end, I aimed to mirror the aesthetic of the default Home Assistant dashboard, following the principles of Material Design. I used Vuetify 3 to expedite the process and Vuelidate for form building. Initially, I started the project using Vee-Validate, but due to some issues with validating an array of fingerprint fields, I switched to Vuelidate. Despite this minor setback, the project was shaping up nicely, and I was excited to see it come to life. Although, I do lament the loss of using Zod for validation, which Vee-Validate supported, as it would have allowed me to maintain a single set of validation rules for both the front and back ends. But such is the nature of development – sometimes, you have to make tough choices.
The web app, at its core, is quite simple. It’s essentially a table that lists all entries in the database. Each entry comprises a name, a pin code, an RFID tag, and a list of fingerprints. I chose to structure the data relationally, splitting the codes, tags, and prints into separate tables and linking them to the users table. This approach ensured the uniqueness of each ID code for the various authentication methods, a task that would have been more challenging had I tried to include all the data directly in the users table. It also provided flexibility for future enhancements, such as tracking which finger each fingerprint belongs to or allowing users to have multiple ID cards. While I haven’t yet implemented the ability to have multiple keypads, when I do, they’ll be a separate table, allowing for unique ID codes for different keypads.
The app itself consists of two pages: one displaying a table of all users, and another for editing a selected user. Submitting the form triggers data validation, after which the data is sent to the back end via tRPC for further validation. Provided the various codes don’t already exist, the user is added to the table. And because I’m all for efficiency, I handle user edits by simply deleting the old entry and creating a new one.
From Blueprint to Functionality: Building the HA Addon
Once I had the basics of the app sorted out, which didn’t take long, it was time to integrate the app into Home Assistant (HA). This proved to be a bit more challenging, primarily due to figuring out how to load the assets correctly within the HA window’s iframe. As I mentioned earlier, HA creates a unique URL for the addon. However, most front-end frameworks compile your HTML so that your JavaScript code is loaded from a basepath defined at compile time. Since we don’t know the basepath ahead of time, we needed a workaround.
My initial solution was to create a reverse proxy using Nginx that would perform a sub_filter to rewrite the links inside the HTML. This, unfortunately, didn’t pan out in the long run. My second solution was to define some environment variables that would change the basepath when the container is started. This approach proved to be more successful, and it’s a testament to the iterative and problem-solving nature of development.
My blueprint for structuring the Home Assistant (HA) addon was the HA AdGuardHome addon. It was up-to-date and featured an ingress-enabled web app UI. However, replicating their sub_filter implementation wasn’t as straightforward as I’d hoped. The unique URL problem was a tough nut to crack, but I discovered that the unique URL only changes when the plugin restarts. Nuxt provides an environment variable ‘NUXT_APP_BASE_URL’ that can be set at runtime. I was able to obtain the addon URL through a utility included in the Hass.io base Docker image called Bashio.
With the web app displayed, the next step was to add the ability to respond to events. This was accomplished by setting hassio_api: true
in the addon config, obtaining the SUPERVISOR_TOKEN environment var
iable, and sending REST requests to http://supervisor/ from within the container. Authentication was handled by including an Authorization: Bearer SUPERVISOR_TOKEN
header. If you’ve worked with any API that uses bearer tokens for authentication, this is the same concept.
Events can be subscribed to using a WebSocket connection provided by the HA supervisor. I modified the Nuxt app to create a new plugin in the server code that establishes the WebSocket connection when the server starts, and I subscribed to several events from HA. Home Assistant maintains a JavaScript library for managing communication with the supervisor. I copied it, made some minor modifications, and began translating it to TypeScript.
ESPHome devices can only send events under the ESPHome namespace, so for every event I want to listen to, I create two listeners, one with and one without the ESPHome namespace. These are simply getPin, getPrint, and getTag, all of which return the user tied to the relevant code. These event names correspond to the events I defined earlier in the ESPHome device when using the keypad. This is where those events finally end up. Once a user is found, the addon fires an event called keypaddb.userFound
with the name and ID of the user as a JSON object. I plan to add more details about the user in the event later.
So, to recap: we have a door lock connected to HA that can lock and unlock the door. The lock also has a door sensor that informs HA whether the door is open or closed. We have a keypad that sends pins, tags, or prints through an HA event. We have an addon with a reverse proxy that exposes a web app inside HA. The addon displays a table of all users and facilitates editing or adding new users, as well as listens to these events. It looks up users based on these events and fires a new event containing the user’s details. HA is now listening to this event. The final piece of the puzzle? Deciding what to do with it.
With the hardware and software components of the project in place, it’s time to bring it all together. The final step in this journey is to create an automation in the Home Assistant dashboard that listens to the event and triggers the lock to unlock. This is where the magic happens, where the individual parts of the project come together to form a cohesive whole.
The beauty of this setup is that you can make the automation as complex or as simple as you want. You could check if the door is open, relock after a set amount of time, or even set up notifications to let you know who’s just walked through the door. The possibilities are endless.
Here’s the code for my configuration. It includes a persistent_notification service that sends notifications containing the name of the person who opened the door. Please note that you can’t just copy and paste this code and expect it to work, as all the device IDs are specific to my Home Assistant installation.
alias: Backdoor Keypad
description: ""
trigger:
- platform: event
event_type: keypaddb.userFound
id: unlockDoor
- type: not_opened
platform: device
device_id: 567235b0f6858689032c091a9db5dfb0
entity_id: binary_sensor.front_door_open
domain: binary_sensor
id: doorClosed
- platform: event
event_type: timer.finished
id: autoLockDoor
event_data:
entity_id: timer.backdoor_auto_lock
- type: opened
platform: device
device_id: 567235b0f6858689032c091a9db5dfb0
entity_id: binary_sensor.front_door_open
domain: binary_sensor
id: doorOpened
condition: []
action:
- choose:
- conditions:
- condition: trigger
id: unlockDoor
- condition: device
device_id: 567235b0f6858689032c091a9db5dfb0
domain: lock
entity_id: lock.front_door
type: is_locked
sequence:
- device_id: 567235b0f6858689032c091a9db5dfb0
domain: lock
entity_id: lock.front_door
type: unlock
- service: timer.start
data:
duration: "15"
target:
entity_id: timer.backdoor_auto_lock
- service: persistent_notification.create
data:
message: "{{ trigger.event.data.name }} has unlocked the back door."
- conditions:
- condition: trigger
id: doorClosed
sequence:
- service: timer.cancel
data: {}
target:
entity_id: timer.backdoor_auto_lock
- service: timer.start
data:
duration: "5"
target:
entity_id: timer.backdoor_auto_lock
- conditions:
- condition: trigger
id: doorOpened
sequence:
- service: timer.cancel
data: {}
target:
entity_id: timer.backdoor_auto_lock
- conditions:
- condition: trigger
id: autoLockDoor
- type: is_not_open
condition: device
device_id: 567235b0f6858689032c091a9db5dfb0
entity_id: binary_sensor.front_door_open
domain: binary_sensor
sequence:
- device_id: 567235b0f6858689032c091a9db5dfb0
domain: lock
entity_id: lock.front_door
type: lock
- if:
- condition: trigger
id: unlockDoor
- condition: template
value_template: "{% if trigger.event.data.name == \"Dalen\" %}true{% endif %}"
then:
- service: persistent_notification.create
data:
message: THIS IS A TEST
mode: single
YAMLCustomizing Code Lengths: The Finishing Touch
To add a final touch of customization, I revisited the app’s source code and introduced new environment variables for setting the length of all three codes: pin, tag, and print. This allowed me to add configuration options within the addon config, empowering each user to determine their preferred code lengths. Want a six-digit code instead of a four-digit one? No problem. Prefer to program fingerprints with three-digit IDs? You got it. This level of customization ensures that the system can be tailored to each user’s unique preferences, enhancing both security and usability.
And with that, the journey from concept to reality was complete. A door lock connected to Home Assistant, a keypad sending unique user codes, an addon with a reverse proxy exposing a web app inside HA, and a system listening and responding to events. All wrapped up with the ability to customize code lengths. A testament to the power of problem-solving, creativity, and a dash of tech wizardry.
With the technical aspects of the project wrapped up, there’s one last crucial step to tackle: updating the documentation. As any seasoned developer knows, comprehensive and accurate documentation is the backbone of any successful project. It’s the roadmap that guides users through the software, explaining how to install, configure, and use it effectively.
In my case, there’s a fair amount of legacy content left over from the AdGuard blueprint I used as a template. While it served as a fantastic starting point, it’s now time to tailor the documentation to accurately reflect this unique project. This involves detailing the installation process, explaining the configuration options, and providing a clear guide on how to use the keypad, the addon, and the Home Assistant integration.
This process might seem tedious, especially after the excitement of building and testing the project, but it’s an essential step. After all, what good is a brilliant solution if no one knows how to use it? So, armed with a fresh pot of coffee and a clear understanding of the project, it’s time to dive into the world of documentation. It’s the final frontier in this journey, and a vital one at that. One which I will get to right…. eventually!
As the sun sets on this project, I find myself standing at the crossroads of completion and curiosity. The web app, in its current form, is a testament to my journey of exploration and learning. It’s a product of my passion for IoT, my love for customization, and my desire to create something that truly meets my needs. But as I look at it, I can’t help but see the potential for more.
Currently, I’m in the process of rewriting the web app in React. There’s nothing inherently wrong with the app being written in Nuxt, but the siren call of React is too strong to ignore. I’ve dabbled in it occasionally, but now, without the pressure of a ticking clock, I have the perfect excuse to dive deeper.
Part of this rewrite involves adding the ability to control multiple keypads independently. Imagine being able to allow some users into one area but not another, or having different codes for each keypad. It’s like having multiple layers of security, each with its own set of rules and restrictions.
Moreover, I’m also planning to add the ability to set schedules for users based on Home Assistant automations. This would allow for even more customization and control, tailoring access to specific times and conditions.
So, while this chapter may be coming to a close, the story is far from over. The journey continues, and I can’t wait to see where it leads me next. Stay tuned!