Skip to content

Instantly share code, notes, and snippets.

@alexberry
Last active March 28, 2025 23:17
Show Gist options
  • Save alexberry/f9b4fa9e335970afbb53075bd1e7852e to your computer and use it in GitHub Desktop.
Save alexberry/f9b4fa9e335970afbb53075bd1e7852e to your computer and use it in GitHub Desktop.
Automating Ubiquity AP LEDs with Home Assistant & Unifi Network

This is a small guide to set up Unifi AP LED Control using Home Assistant.

Known Supported Hardware

These scripts have been tested as working on the following hardware:

Home Assistant Hosting

My Home Assistant setup runs in docker compose (see setup guide) and so this doc is written with that in mind.

I have stored my home assistant config in /opt/homeassistant, and so my docker compose file is at /opt/homeassistant/docker-compose.yaml. All paths mentioned in this tutorial assume this setup, but you should alter it to match your filesystem configuration.

SSH config

These commands use ssh to connect to each AP in order to configure them. With this in mind, we must do a little bit of legwork to get our ssh configured.

Generate SSH keys & config for use by home assistant

My approach to this has been to generate keys outside of home assistant and mount them inside, this ensures that the keys survive a rebuild of the home assistant container.

First, we must create a directory and generate ssh keys in said folder. Perform this command on the container host itself:

mkdir /opt/homeassistant/dotssh
ssh-keygen -t rsa -f /opt/homeassistant/dotssh/id_rsa

Next, we must set up the config for each access point, referencing the below ssh_config file, create a file at /opt/homeassistant/dotssh/config with each Access Point that you want to connect to, including the SSH user defined in the Unifi Network Application (see Unifi Network Application settings for further details).

You should now have a folder with content as follows:

dotssh
|-- config
|-- id_rsa
`-- id_rsa.pub

Next we must mount this directory in the home assistant container, see Mounting SSH Directory.

Home Assistant Config

Mounting SSH Directory

Now an SSH key has been generated, we must mount the ssh config in home assistant such that it uses these credentials to connect to access points. See the volumes key in docker-compose.yaml. The folder should be mounted within the home assistant container at /root/.ssh.

shell_command settings

See the shell_command key in configuration.yaml. Two shell commands are included, one to control LEDs, the other to reboot the access point, both use ssh to connect.

script settings

See scripts.yaml. These scripts use the shell_command keys defined in configuration.yaml to manage the LEDs on your AP. You should adjust this to your liking by replacing the names of my access points (e.g. garage.mydomain.mytld) with your own hostname or IP address.

automation settings

See automations.yaml. These automations depend on the sun & meteoalarm home assistant integrations for LED scheduling & Weather Warning alerts respectively, you must install and configure these integrations for your location before copying any of these automations.

I do not recommend blindly merging these automations with your own, as I cannot guarantee the IDs wont collide. Instead, I recommend using the Automation Editor, then at the top-left click the three-dot menu & choose 'Edit in YAML'. You can then copy each automation you would like by copying one, removing it's ID & de-indenting, e.g.:

- id: '1742025250691'
  alias: Wifi pre-dawn
  description: ''
  triggers:
  - event: sunrise
    offset: -01:00:00
    trigger: sun
  conditions: []
  actions:
  - action: script.unifi_led_white
    data: {}

Becomes:

alias: Wifi pre-dawn
description: ''
triggers:
- event: sunrise
  offset: -01:00:00
  trigger: sun
conditions: []
actions:
- action: script.unifi_led_white
  data: {}

Unifi Network Application settings

Finally we need to add the public key from the SSH Config section to our Unifi Application. Copy the contents of dotssh/id_rsa.pub to your clipboard, and follow this UI Knowledgebase article to trust this new key. Additionally, ensure the ssh user is configured to match the usernames defined in dotssh/config.

Validating SSH Config

Before testing any automations, we should make sure that home assistant can successfully connect to our access point. Referencing a Host defined in dotssh/config, we can get a shell in our container and attempt to ssh to one of our access points:

docker exec -it homeassistant bash
ssh garage.mydomain.mytld

This should then launch a shell on your access point like so:

BusyBox v1.25.1 () built-in shell (ash)


  ___ ___      .__________.__
 |   |   |____ |__\_  ____/__|
 |   |   /    \|  ||  __) |  |   (c) 2010-2023
 |   |  |   |  \  ||  \   |  |   Ubiquiti Inc.
 |______|___|  /__||__/   |__|
            |_/                  https://www.ui.com

      Welcome to UniFi UAP-AC-Mesh!

********************************* NOTICE **********************************
* By logging in to, accessing, or using any Ubiquiti product, you are     *
* signifying that you have read our Terms of Service (ToS) and End User   *
* License Agreement (EULA), understand their terms, and agree to be       *
* fully bound to them. The use of SSH (Secure Shell) can potentially      *
* harm Ubiquiti devices and result in lost access to them and their data. *
* By proceeding, you acknowledge that the use of SSH to modify device(s)  *
* outside of their normal operational scope, or in any manner             *
* inconsistent with the ToS or EULA, will permanently and irrevocably     *
* void any applicable warranty.                                           *
***************************************************************************

Garage-BZ.6.6.77#
# This is a copy of the automations I used to control my Unifi AP LEDs,
# they are dependent on both the shell_command & script entries defined in configuration.yaml & scripts.yaml respectively.
- id: '1742021322634'
alias: Internet Down
description: ''
triggers:
- trigger: state
entity_id:
- device_tracker.google_dns
from: home
to: not_home
conditions: []
actions:
- action: script.unifi_led_flash
metadata: {}
data: {}
mode: single
- id: '1742021469389'
alias: Internet Recovered - Day
description: ''
triggers:
- trigger: state
entity_id:
- device_tracker.google_dns
from: not_home
to: home
conditions:
- condition: sun
before: sunset
after: sunrise
actions:
- action: script.unifi_led_blue
metadata: {}
data: {}
mode: single
- id: '1742021652745'
alias: Internet Recovered - Night
description: ''
triggers:
- trigger: state
entity_id:
- device_tracker.google_dns
from: not_home
to: home
conditions:
- condition: or
conditions:
- condition: sun
after: sunset
- condition: sun
before: sunrise
actions:
- action: script.unifi_led_off
metadata: {}
data: {}
mode: single
- id: '1742022139515'
alias: Weather Warning Night
description: ''
triggers:
- entity_id: binary_sensor.meteoalarm
from: 'off'
trigger: state
conditions:
- condition: or
conditions:
- condition: sun
before: sunrise
- condition: time
after: '23:00'
actions:
- wait_for_trigger:
- event: sunrise
offset: 00:02:00
trigger: sun
- action: script.unifi_led_flash_alternate_slow
data: {}
- id: '1742022210534'
alias: Weather Warning
description: ''
triggers:
- entity_id: binary_sensor.meteoalarm
from: 'off'
trigger: state
conditions:
- condition: and
conditions:
- condition: sun
after: sunrise
- condition: time
before: '23:00'
actions:
- action: script.unifi_led_flash_alternate_slow
data: {}
- id: '1742025250691'
alias: Wifi pre-dawn
description: ''
triggers:
- event: sunrise
offset: -01:00:00
trigger: sun
conditions: []
actions:
- action: script.unifi_led_white
data: {}
- id: '1742025276513'
alias: Wifi post-dawn
description: ''
triggers:
- event: sunrise
offset: 01:00:00
trigger: sun
conditions: []
actions:
- action: script.unifi_led_blue
- id: '1742025307592'
alias: Wifi dusk
description: ''
triggers:
- event: sunset
offset: -01:00:00
trigger: sun
conditions: []
actions:
- action: script.unifi_led_light_blue
- id: '1742025347680'
alias: Wifi twilight
description: ''
triggers:
- event: sunset
trigger: sun
conditions: []
actions:
- action: script.unifi_led_white
- id: '1742025368327'
alias: Wifi dawn
description: ''
triggers:
- event: sunrise
trigger: sun
conditions: []
actions:
- action: script.unifi_led_light_blue
- id: '1742025413492'
alias: Wifi night
description: ''
triggers:
- trigger: time
at: '22:15:00'
conditions: []
actions:
- action: script.unifi_led_off
data: {}
# Home Assistant configuration file
# Add these lines to any existing config to split out your scripts & automations to their own files
# This is optional, but easier to manage in my opinion.
scripts: !include scripts.yaml
automations: !include automations.yaml
shell_command:
# Shell command used to connect to individual APs, it is reused by each script in scripts.yaml.
# In order for this script to work, home assistant must have valid ssh credential & config for each AP in the ~/.ssh folder.
# It takes the following parameters:
# connection_string: the IP or hostname of your access point
# pattern: the colour(s) of the LED, including blue, white, light blue, alternating white/blue & flashing single colours. See scripts.yaml for the settings required for a given colour.
# tempo: the tempo, in beats per minute, that any flashing will follow. Has no effect on static colours.
# It performs the following steps on each AP it connects to:
# 1. Deletes certain config keys from the persistent management file, including previously set overrides.
# 2. Creates override config settings and writes these to the persistent management file. If this step is missed the GPIO settings revert nearly immediately to the contents of the file.
# 3. Writes these settings to the GPIO pins responsible for adjusting LED settings.
# All settings applied by this script are reset upon rebooting the access point.
unifi_led: ssh {{ connection_string }} "sed -i '/^mgmt.led_enabled/d; /^mgmt.led_tempo_override/d; /^mgmt.led_pattern_override/d' /var/etc/persistent/cfg/mgmt; echo -e 'mgmt.led_pattern_override={{ pattern }}\nmgmt.led_tempo_override={{ tempo }}'>> /var/etc/persistent/cfg/mgmt && echo {{ tempo }} > /proc/gpio/led_tempo && echo {{ pattern }} > /proc/gpio/led_pattern"
# Basic reboot command over ssh
unifi_reboot: ssh {{ connection_string }} "reboot"
# This is my docker compose file, though all but the salient information has been stripped
# Particularly, notice that I have mounted a folder 'dotssh' at '/root/.ssh'
# these are standard ssh config files & private/public key pairs
services:
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:stable
volumes:
- /opt/homeassistant/dotssh:/root/.ssh
- /opt/homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
network_mode: host
# This is a custom path for scripts, as defined in configuration.yaml
# Add the following lines to scripts.yaml, or under the scripts key in configuration.
unifi_reboot:
sequence:
- service: shell_command.unifi_reboot
data_template:
connection_string: study.mydomain.mytld
- service: shell_command.unifi_reboot
data_template:
connection_string: garage.mydomain.mytld
- service: shell_command.unifi_reboot
data_template:
connection_string: livingroom.mydomain.mytld
unifi_led_off:
sequence:
- service: shell_command.unifi_led
data_template:
connection_string: study.mydomain.mytld
pattern: 0
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: livingroom.mydomain.mytld
pattern: 0
- service: shell_command.unifi_led
data_template:
connection_string: garage.mydomain.mytld
pattern: 0
unifi_led_white:
sequence:
- service: shell_command.unifi_led
data_template:
connection_string: study.mydomain.mytld
pattern: 2
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: livingroom.mydomain.mytld
pattern: 2
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: garage.mydomain.mytld
pattern: 2
tempo: 120
unifi_led_blue:
sequence:
- service: shell_command.unifi_led
data_template:
connection_string: study.mydomain.mytld
pattern: 1
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: livingroom.mydomain.mytld
pattern: 1
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: garage.mydomain.mytld
pattern: 1
tempo: 120
unifi_led_light_blue:
sequence:
- service: shell_command.unifi_led
data_template:
connection_string: study.mydomain.mytld
pattern: 3
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: livingroom.mydomain.mytld
pattern: 3
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: garage.mydomain.mytld
pattern: 3
tempo: 120
unifi_led_on:
sequence:
- service: shell_command.unifi_led
data_template:
connection_string: study.mydomain.mytld
pattern: 1
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: livingroom.mydomain.mytld
pattern: 1
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: garage.mydomain.mytld
pattern: 1
tempo: 120
unifi_led_flash:
sequence:
- service: shell_command.unifi_led
data_template:
connection_string: study.mydomain.mytld
pattern: 10
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: livingroom.mydomain.mytld
pattern: 10
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: garage.mydomain.mytld
pattern: 10
tempo: 120
unifi_led_flash_alternate:
sequence:
- service: shell_command.unifi_led
data_template:
connection_string: study.mydomain.mytld
pattern: 12
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: livingroom.mydomain.mytld
pattern: 12
tempo: 120
- service: shell_command.unifi_led
data_template:
connection_string: garage.mydomain.mytld
pattern: 12
tempo: 120
unifi_led_flash_alternate_slow:
sequence:
- service: shell_command.unifi_led
data_template:
connection_string: study.mydomain.mytld
pattern: 12
tempo: 20
- service: shell_command.unifi_led
data_template:
connection_string: livingroom.mydomain.mytld
pattern: 12
tempo: 20
- service: shell_command.unifi_led
data_template:
connection_string: garage.mydomain.mytld
pattern: 12
tempo: 20
# This is the ssh config file for home assistant
# It should be stored within the container at /root/.ssh/config
# The Host must match the values used in scripts.yaml, either hostname or IP address
# You must specify the user as defined in the unifi network application for ssh
# You must set StrictHostKeyChecking to false, as on modern APs this key is generated on each boot, breaking ssh after reboot.
Host study.mydomain.mytld
User myuser
StrictHostKeyChecking no
Host livingroom.mydomain.mytld
User myuser
StrictHostKeyChecking no
Host garage.mydomain.mytld
User myuser
StrictHostKeyChecking no
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment