Porter: A Smart Garage Door Controller
Welcome to Porter: A simple garage door controller written in Go.
Check it out on GitHub.
This project is a ground-up rebuild of my previous doorMan project, with a few goals in mind:
Easy configuration: Porter includes hardware, HTTP, and security configuration into an easy-to-read JSON file. The hardware component is highly configurable, so it’s very easy to wire in one or several garage doors and should be highly compatible with different systems. You can even work around wiring mistakes.
Security: Apart from the security benefits of Go itself, porter allows users to define scoped API keys with varying permissions for different clients. By specifying certificate file locations in the configuration, the REST API can be served over HTTPS.
Clean and Simple Interface: Everything is exposed behind an easy-to-use HTTP REST API, and a fully-functional client library (
porter/client) is included for use in building integrations (such as the HomeKit integration, web interface, and Twilio-based status monitor).
Easy Portability: The base Porter package supports the Raspberry Pi by way of the
go-rpiopackage. However, I’ve done some work to improve the ease with which support can be added for additional boards. The
porter/hwpackage serves as a wrapper around platform-specific libraries, so you could (in theory) update those bindings to your library of choice and be on your way with your favorite board.
Secure, Sane, and Simple API
The Porter API is quite simple to use and should be easy to integrate into other projects and solutions like Homeassistant.
Included in this repository is a fully-functional API client library,
porter/client. You can use this library to add Porter support to your other Go projects.
I’ve built multiple integrations using the Porter API. Here are a few:
The HKPorter HomeKit daemon,
The porter-web web UI, and
Reporter, a Twilio-based SMS notifier that tells you if the door has been left open/ajar for too long.
Be sure to check out those projects if you’re interested in their functionality, or are seeking more examples of how to use the API and client library.
Before you begin, you’ll need to build porter. Fetch the dependencies using
go get, and if necessary cross compile porter for the correct
GOARCH for your target platform.
For the Raspberry Pi, your build command would look like this:
GOOS=linux GOARCH=arm go build -o porter main.go
Next, you’ll need to copy the compiled porter binary to the appropriate location on your host. I recommend using
The following instructions walk you through the process of setting up a Porter instance on a Raspberry Pi. These instructions assume that you’re using either Raspbian or Ubuntu Server.
This brief guide is intended for advanced users who require only minimal guidance to configure hardware and system services.
At some point in time, I’ll probably include releases of Porter in this repository in .deb/.rpm format. This will make installation significantly easier.
Hardware configuration is straightforward, although hardware fundamentals are outside of the scope of this document. Instead, an example wiring scheme that will work with a provided default configuration is presented here.
Note: The Raspberry Pi unfortunately uses several pin numbering schemes simultaneously, which can be confusing. For physical wiring, I’ll specify the physical pin number on the GPIO header. In the software configuration later on, we’ll have to use the BCM pin numbers. Resources like pinout.xyz are an excellent reference if you’re trying to follow along.
Additional Note: The
gpio command (install with
apt -y install rpi.gpio) is extremely useful for troubleshooting. Specifically,
gpio readall will print out the current state of all pins in a very readable format.
On the hardware side, you’re going to need the following components:
- A 5v Relay module, such as this one to control your garage door opener.
- A magnetic reed switch (such as this) to determine whether the door is open or closed.
- Some length of wiring to reach from your garage door lift and the reed switch to the pi (this will vary depending on your particular setup)
Your final pinout should look like this:
Pi Pin | Device
2 | Relay Vcc In
6 | Relay Gnd
11 | Relay In1
14 | Reed Switch
16 | Reed Switch
Note the active and inactive state of your relays and switches- these will need to be reflected in the Porter configuration file if state information and commands are to be read/sent properly. For example, a magnetic reed switch in a normally-closed configuration would read 1 when inactive (meaning that no magnets are nearby). Similarly, a relay module (such as the Sainsmart) is active when its input signal is low.
Another important thing to keep in mind is that some pins on the Pi might be in a high or low state when the system starts up. Keep this in mind, as choosing the wrong pin could, for example, cause your door to open each time the system reboots. I cannot guarantee that the example pins provided above will be compatible with the hardware you are using, so be sure to perform some testing on your own.
For the lift, make sure that your relay is in a normally open state on the control lines. These lines are the same wires leading to physical switches you might have on the wall that allow you to open and close the door. Generally, you can tap directly into a wiring block on the back of your lift in order to attach the relay as a controller. Consult the manual for your lift and perform some testing before proceeding.
Creating the Porter Service Account
For security reasons, I highly recommend running the Porter service as its own dedicated, unprivileged user account. You can create this account using the commands below:
Note: Adding the
porter user to the GPIO group is an important step, as it provides the necessary permissions for Porter to modify the Pi’s GPIO pins. At the time of writing Porter, certain elements of this were still under development. If you’re still getting permissions errors about accessing
/dev/gpiomem after adding the porter user to the
gpio group, you may need to copy the udev rules in this repository (in
/etc/udev/rules.d/gpio.rules and reboot for the below to work.
useradd -M -N -r -s /bin/false -c "porter service account" porter
adduser porter porter
Adding Porter to Systemd
Like many services on a Linux system, the Porter service will be managed by Systemd. To enable this, we’ll need to create a couple of small configuration files so that Systemd knows how to start and manage the service.
First, we’ll need to create an environment file, which contains the command line arguments that Systemd should start the Porter service with. Porter takes exactly one command-line argument,
-config, which specifies the location of your configuration file. Create the file
/etc/default/porter with the following contents:
Next, copy the contents of
porter.service (located in the root of this repository) to
Finally, we can tell Systemd to load our new Porter unit file, and to start it at boot time:
systemctl enable porter
Configuring the Porter Service
Now it’s time to configure Porter.
First, create a directory in
/etc/ to hold Porter’s configuration file. We’ll need to set the appropriate permissions to make sure that the Porter service can read (but not overwrite) the file’s contents:
chown root:porter -R /etc/porter
chmod 644 -R /etc/porter
Here’s what a basic Porter configuration file would look like for the wiring guide provided earlier in this tutorial.
This configuration provides the following:
One door definition.
Two API keys, one with full administrative privileges and another that can only read (but not modify) the door state.
The API server is configured to listen on all addresses at port 8080 with SSL disabled.
Edit the example to taste and place the completed file in
To start Porter, run:
service porter start
To stop the server, run:
service porter stop
To view logs and error messages, view the service’s status like so:
service porter status
Now that you’ve installed Porter, its Systemd unit, and configuration file, start the service by running
service porter start.
If you encounter errors these will be logged. Use
service porter status to diagnose and correct them.
You are now ready to test the API. Here are some example cURL commands.
Set variables for testing
Run this command first. If you’ve changed your Porter API token or door name from the example provided above, make sure you reflect that in the below commands.
Assuming you’re running these commands on the Pi that’s hosting porter, you can reach the API server locally at
127.0.0.1:8080. However, if you are running these commands on a different system, be sure to update the IP/port information as well.
/api/v1/list: Lists the status of all doors Porter is configured to manage.
/api/v1/open/<door name>: Opens the specified door. The command will succeed only if the door is currently closed.
/api/v1/close/<door name>: Closes the specified door. The command will succeed only if the door is currently open.
/api/v1/trip/<door name>: Activates the lift controller for the specified door. The command will succeed regardless of the door’s current state. This is useful in cases where the door might be stuck, or its state is not being read correctly.
/api/v1/lock/<door name>: This enables a soft lock on the specified door, preventing any open, close, or trip commands from acting on it. This is a useful way to disable Porter if necessary.
/api/v1/unlock/<door name>: Removes a lock from the specified door
Read door state
curl -H "Authorization: Bearer $PORTER_TOKEN" http://$PORTER_IP/api/v1/list
Open the door
curl -H "Authorization: Bearer $PORTER_TOKEN" http://$PORTER_IP/api/v1/open/$PORTER_DOOR
Close the door
curl -H "Authorization: Bearer $PORTER_TOKEN" http://$PORTER_IP/api/v1/close/$PORTER_DOOR