This is admittedly something I did not learn today but rather learned and adapted a couple years ago from this post on the Home Assistant forum, but I just had to use it again today and so I figured I’d write it down with all the bells and whistles just in case I ever need this information again - or anyone else does.
First of all, in your unifi controller you should create a new user that Home Assistant will act as to manage your port forward(s) for you. So, log into the controller, go into Settings > Administrators and add a new Administrator user1.
Then create your port forward in Settings > Routing & Firewall > Port Forwarding. Take note of the id of the port forward you have created - you can find it by clicking edit on it again, it will be the number at the end of the URL of the edit page. E.g. if the URL looks like this: https://my.unifi.controller/manage/site/default/settings/portforward/edit/1234567890
then this is the id of the port forward: 1234567890
.
Next, copy this shell script to /config/scripts/unifi.sh
in your Home Assistant. Make sure to adjust https://my.unifi.controller
(and, if necessary, the site default
) to your own values.
#!/bin/sh
set -e
# based on https://community.home-assistant.io/t/automating-unifi-port-forwarding-based-upon-presence-detection/168185
cookie=$(mktemp)
headers=$(mktemp)
curl_cmd="curl --silent --cookie ${cookie} --cookie-jar ${cookie} -D ${headers} --insecure"
BASEURL="https://my.unifi.controller"
SITE="default"
auth() {
USERNAME=$1
PASSWORD=$2
# authenticate against unifi controller
${curl_cmd} --output /dev/null -d "{\"username\":\"$USERNAME\", \"password\":\"$PASSWORD\"}" $BASEURL/api/login
# grab the `x-csrf-token` and strip the newline (added when upgraded to controller 6.1.26)
csrf="$(awk -v FS=': ' '/^x-csrf-token/{print $2}' "${headers}" | tr -d '\r')"
echo $csrf
}
portfwd() {
USERNAME=$1
PASSWORD=$2
FORWARD_ID=$3
FORWARD_ENABLED=$4
# authenticate against unifi controller
csrf=$(auth $USERNAME $PASSWORD)
# enable/disable firewall rule
body=$(${curl_cmd} -X GET $BASEURL/api/s/default/rest/portforward/$FORWARD_ID | jq '.data[0] | .enabled='$FORWARD_ENABLED'')
${curl_cmd} -X PUT $BASEURL/api/s/default/rest/portforward/$FORWARD_ID -H "Content-Type: application/json" -H "x-csrf-token: ${csrf}" -d @<(echo "$body")
}
isportfwd() {
USERNAME=$1
PASSWORD=$2
FORWARD_ID=$3
# authenticate against unifi controller
csrf=$(auth $USERNAME $PASSWORD)
${curl_cmd} -X GET $BASEURL/api/s/default/rest/portforward/$FORWARD_ID | jq '.data[0].enabled'
}
"$@"
Now, let’s imagine you want to add a switch for an SFTP port forward that you’ve just created. Then, in your secrets.yaml
file, add the following:
unifi_forward_sftp_check: '/bin/bash /config/scripts/unifi.sh isportfwd <user> <password> <forward_id>'
unifi_forward_sftp_enable: '/bin/bash /config/scripts/unifi.sh portfwd <user> <password> <forward_id> true'
unifi_forward_sftp_disable: '/bin/bash /config/scripts/unifi.sh portfwd <user> <password> <forward_id> false'
Replace <user>
, <password>
and <forward_id>
with the login credentials and id of the forward you just created.
Next, add a command line switch definition to your configuration.yaml
(or in my case to my packages/network.yaml
file):
switch:
- platform: command_line
switches:
sftp_port_forward:
friendly_name: "SFTP Port Forward"
command_state: !secret unifi_forward_sftp_check
command_on: !secret unifi_forward_sftp_enable
command_off: !secret unifi_forward_sftp_disable
value_template: '{{ bool(value, false) }}'
Throw that somewhere on your dashboard, or alternatively tie it into some automation, and you’re good to go!
Maybe a regular user suffices as well, I honestly can’t remember, but I’m using an admin user here. ↩︎