Control UniFi Access Point LEDs via the internal REST API.
Project description
Control UniFi Access Point LEDs via internal API — perfect for night mode scheduling and automation.
UniFi has never implemented a proper night mode scheduler for Access Points — despite users requesting it for years. This project fills that gap with a lightweight, reliable Python-based API that sends PUT requests to the internal REST API of the AP. Tested on U7 Pro and U6+.
[!NOTE]
Modern UniFi APs (U6/U7 series) support LED override ON/OFF only.
Color and brightness overrides are ignored by firmware.
Features
- Turn device LEDs on or off programmatically
- Auto-Discovery — finds all UniFi APs and checks LED toggle compatibility easily
- Webhook API Server — built-in HTTP server explicitly designed to integrate with Node-RED, Home Assistant, etc.
- Device Groups — create custom zones like
upstairsordownstairsusinggroups.json - CLI Color & Brightness — supported on older models via
--colorand--brightness - Auto-config — device config is fetched live on every run, so payloads are never stale
- Per-device payloads — each AP gets its own config files (
led_on_{id}.json/led_off_{id}.json) - Multiple device support — control several APs with one command or webhook
- LED status — check the current LED state without making changes
- Dry-run mode — preview payloads before sending
- Cross-platform config —
.envfile works on Linux, macOS, and Windows - Works with UniFi OS and legacy controllers
- Simple command-line interface with
--helpsupport - Suitable for cron jobs, automation, and Docker
- Published Docker image on GitHub Container Registry
Why this does NOT use SSH
Many existing UniFi LED control solutions rely on SSH access to the access point and manual modification of LED-related settings or scripts.
⚠️ SSH-based approaches have been observed to cause firmware corruption and device instability, sometimes requiring recovery via TFTP.
UniFi access points are not designed for persistent or repeated SSH access. Running SSH-based automation can:
- Increase CPU and memory usage on the AP
- Interfere with UniFi's internal configuration management
- Cause device instability, reprovisioning loops, or unexpected reboots
- Break silently after firmware updates
This project avoids SSH entirely and instead uses the same internal REST API mechanism the UniFi controller itself uses to manage device configuration. As a result, it is significantly more stable, safer, and closer to how UniFi intended devices to be managed. This makes it suitable for long-term automation, cron jobs, and unattended operation.
Requirements
- Python 3.9+
aiohttplibrary (installed automatically)- A UniFi Controller with API access
- A local UniFi user account (no 2FA)
Installation
With PyPI (Recommended)
pip install unifi-led-api
This gives you access to the global unifi-led command.
From Source / Development
-
Clone this repository:
git clone https://github.com/elNino0916/unifi-led-api.git cd unifi-led-api
-
Install dependencies:
pip install -e ".[dev]"
With Docker
docker pull ghcr.io/elnino0916/unifi-led-api:main
Or build locally:
docker build -t unifi-led-api .
Configuration
- Copy the example env file and fill in your values:
cp .env.example .env
- Edit
.envwith your controller IP, credentials, and device ID(s).
That's it — you're ready to go. The tool fetches your device config automatically on every run.
Finding your device ID
Your device ID is in the UniFi controller URL when viewing a device:
https://<controller-ip>/network/default/devices/properties/device/<DEVICE_ID>/general
[!TIP] If you enter a wrong device ID, the error message will list all available devices with their names and IDs.
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
UNIFI_USER |
Yes | — | Local UniFi username (no 2FA) |
UNIFI_PASS |
Yes | — | UniFi password |
UNIFI_CONTROLLER |
Yes | — | Controller URL (e.g. https://192.168.1.1) |
UNIFI_DEVICE_ID |
Yes | — | Device ID (comma-separated for multiple) |
UNIFI_SITE |
No | default |
UniFi site name |
UNIFI_VERIFY_SSL |
No | false |
Set to true for valid SSL certs |
UNIFI_TIMEOUT |
No | 10 |
HTTP request timeout in seconds |
Targeting Devices and Groups
You can define devices in the .env file via a comma-separated list:
UNIFI_DEVICE_ID=device_id_1,device_id_2,device_id_3
Each device gets its own per-device config files and all are updated in a single run.
Using Device Groups:
Create a groups.json file in the root directory (see groups.json.example) to group IDs by name:
{
"upstairs": ["device_id_1", "device_id_2"],
"downstairs": ["device_id_3"]
}
You can then run commands targeting only that group by placing the global --group argument before the sub-command:
unifi-led --group upstairs led on
Specific Device Overrides:
Similarly, you can override the target device list entirely by passing the global --device argument before the sub-command:
unifi-led --device device_id_3 led off
Usage
Interactive Setup
The easiest way to configure the project is with the built-in interactive wizard. It will prompt for your credentials, discover all connected devices, and let you select which ones to control.
unifi-led setup
Once completed, a .env file will be generated automatically in your current directory.
Commands Overview
unifi-led --help
unifi-led led --help
unifi-led status --help
unifi-led discover --help
unifi-led serve --help
unifi-led setup --help
unifi-led fetch-config --help
Turn LED On
unifi-led led on
For older supported access points, you can also pass a hex color and brightness percentage:
unifi-led led on --color "#0000ff" --brightness 50
Turn LED Off
unifi-led led off
[!NOTE] Every
led on/offcommand automatically fetches the latest device config before pushing. This means config files are always up-to-date — no manual regeneration needed after firmware updates or config changes.
Preview payload (dry-run)
unifi-led led on --dry-run
Shows the JSON payload that would be sent, without actually making any changes. Useful for debugging and first-time verification.
Auto-Discovery
unifi-led discover
Finds all devices connected to the UniFi controller and lists their names, MAC addresses, models, and device IDs. It also includes a compatibility check table to show you if your firmware natively supports LED toggling.
Check LED status
unifi-led status
Displays the current led_override state for each targeted device. No changes are made.
Webhook API Server
Start a lightweight HTTP server to listen for webhooks. This is perfect for integrating UniFi LEDs with Node-RED, Home Assistant, or other platforms without invoking the CLI every time.
unifi-led serve --port 8080
You can then control devices with HTTP GET or POST requests:
- Turns on LEDs for
.envconfigured devices:http://localhost:8080/led/on - Using query parameters for groups and overrides:
http://localhost:8080/led/off?group=upstairsorhttp://localhost:8080/led/on?device=id1,id2 - Using color and brightness:
http://localhost:8080/led/on?color=%23ff0000&brightness=100
Preview config without changing LEDs
unifi-led fetch-config
This fetches the device config and generates the JSON payload files without making any changes. Useful for inspecting what will be sent.
Cron Example
Turn off LEDs every night at 10 PM:
0 22 * * * cd /path/to/my/env_folder && unifi-led led off
Turn on LEDs every morning at 7 AM:
0 7 * * * cd /path/to/my/env_folder && unifi-led led on
[!TIP] The
.envfile is loaded automatically — no extra setup needed in cron.
Docker
# Turn LEDs off
docker run --rm --env-file .env ghcr.io/elnino0916/unifi-led-api:main led off
# Turn LEDs on
docker run --rm --env-file .env ghcr.io/elnino0916/unifi-led-api:main led on
# Check LED status
docker run --rm --env-file .env ghcr.io/elnino0916/unifi-led-api:main status
# Preview payload (dry-run)
docker run --rm --env-file .env ghcr.io/elnino0916/unifi-led-api:main led on --dry-run
# Preview config
docker run --rm --env-file .env -v $(pwd):/app ghcr.io/elnino0916/unifi-led-api:main fetch-config
Docker cron example:
0 22 * * * docker run --rm --env-file /path/to/.env ghcr.io/elnino0916/unifi-led-api:main led off
0 7 * * * docker run --rm --env-file /path/to/.env ghcr.io/elnino0916/unifi-led-api:main led on
Docker Compose (automated scheduling)
A docker-compose.yml is included that uses ofelia for cron-based scheduling:
docker compose up -d
By default, LEDs turn off at 22:00 and on at 07:00. Customize the schedules via environment variables:
CRON_LED_OFF="0 0 23 * * *" CRON_LED_ON="0 0 6 * * *" docker compose up -d
LED Payload Files
Config files are generated per-device as led_on_{device_id}.json and led_off_{device_id}.json.
These are auto-generated on every run from your live device config. You generally don't need to touch them, but they're written to disk so you can inspect exactly what gets sent.
If customizing manually:
led_override:"on"or"off"led_override_color: LED color in hex format (e.g.,"#0000ff") (Only older APs)led_override_color_brightness: Brightness percentage (e.g.,"100") (Only on some newer APs if it even works in the first place.)
[!CAUTION]
Do NOT modify anything in the JSON files except the LED fields.
Changing other values can break your device configuration.
Troubleshooting
Authentication Issues
- Ensure you're using a local UniFi account, not a Ubiquiti cloud account
- The account should not have 2FA enabled
- Check that the controller URL is correct and accessible
SSL Certificate Errors
If you're using a self-signed certificate, set UNIFI_VERIFY_SSL=false
CSRF Token Errors
The API automatically handles CSRF tokens. If you encounter issues, ensure your controller is running a compatible version.
License
This project is provided under the MIT License.
Disclaimer
This project is an unofficial tool and is not affiliated with, endorsed by, or supported by Ubiquiti Inc. or any of its subsidiaries in any way. This software interacts with internal and undocumented UniFi controller and access point APIs, which are subject to change without notice. As a result, functionality may break at any time due to firmware updates, controller updates, configuration changes, or other modifications made by Ubiquiti. Use this tool only if you understand what it does and have verified it in a safe or non-production environment first. If you are unsure whether this tool is appropriate for your setup, do not use it.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unifi_led_api-0.2.2.tar.gz.
File metadata
- Download URL: unifi_led_api-0.2.2.tar.gz
- Upload date:
- Size: 22.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a75c157bb2bf78ea41c4f4207bb0c9f80b1680be9ef2ff48922a9e1a4f029062
|
|
| MD5 |
e96e08e0735301236cd2aaf48f511f7c
|
|
| BLAKE2b-256 |
ce480596ef6725f2a4592364cbaff6c7688581b64d90637134c7c30fa91eec0c
|
Provenance
The following attestation bundles were made for unifi_led_api-0.2.2.tar.gz:
Publisher:
publish.yml on elNino0916/unifi-led-api
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
unifi_led_api-0.2.2.tar.gz -
Subject digest:
a75c157bb2bf78ea41c4f4207bb0c9f80b1680be9ef2ff48922a9e1a4f029062 - Sigstore transparency entry: 1154472740
- Sigstore integration time:
-
Permalink:
elNino0916/unifi-led-api@1eac6d2cd3c9405a36604de7c81a99d37b8f3d9d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/elNino0916
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1eac6d2cd3c9405a36604de7c81a99d37b8f3d9d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file unifi_led_api-0.2.2-py3-none-any.whl.
File metadata
- Download URL: unifi_led_api-0.2.2-py3-none-any.whl
- Upload date:
- Size: 18.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c8bc11a41c52ee121abca73ec3dcb077d92a39045449e50202dd06a8f1f6caa
|
|
| MD5 |
08b9955a3b02fa59c149090e25f4ae40
|
|
| BLAKE2b-256 |
02d674dd4d408a9c963180ccd9e468dcb99356eb6b97e5b726b6952571296e70
|
Provenance
The following attestation bundles were made for unifi_led_api-0.2.2-py3-none-any.whl:
Publisher:
publish.yml on elNino0916/unifi-led-api
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
unifi_led_api-0.2.2-py3-none-any.whl -
Subject digest:
1c8bc11a41c52ee121abca73ec3dcb077d92a39045449e50202dd06a8f1f6caa - Sigstore transparency entry: 1154472741
- Sigstore integration time:
-
Permalink:
elNino0916/unifi-led-api@1eac6d2cd3c9405a36604de7c81a99d37b8f3d9d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/elNino0916
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1eac6d2cd3c9405a36604de7c81a99d37b8f3d9d -
Trigger Event:
workflow_dispatch
-
Statement type: