2026-08-09 16:32:42 +02:00
|
|
|
|
# FastHue
|
|
|
|
|
|
|
|
|
|
|
|
A local, iPhone-first Philips Hue dashboard and command-line client. It works
|
|
|
|
|
|
with a Hue Bridge or Hue Bridge Pro on the same network.
|
|
|
|
|
|
|
|
|
|
|
|
## Dashboard
|
|
|
|
|
|
|
|
|
|
|
|
The dashboard fills an iPhone screen with eight dark controls in a 2 × 4 grid.
|
|
|
|
|
|
|
|
|
|
|
|
- A blue edge means at least one light in that room or zone is on.
|
|
|
|
|
|
- The subtitle is the active Hue scene, when one is active.
|
|
|
|
|
|
- Tap a button to activate the next configured scene.
|
|
|
|
|
|
- Press and hold a button for about 0.65 seconds to turn its lights off.
|
|
|
|
|
|
|
|
|
|
|
|
### Start it
|
|
|
|
|
|
|
|
|
|
|
|
First register the app with the bridge (you only need to do this once):
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
python3 hue.py register
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Press the physical button on the bridge when prompted. Then install the small
|
|
|
|
|
|
web app dependencies and run it:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
python3 -m venv .venv
|
|
|
|
|
|
.venv/bin/pip install -r requirements.txt
|
|
|
|
|
|
.venv/bin/python app.py
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
On the iPhone, open `http://YOUR-COMPUTER-IP:5000/?key=YOUR_API_KEY` in Safari.
|
|
|
|
|
|
The key is saved only for that browser session and removed from the displayed
|
|
|
|
|
|
URL. The iPhone and computer must be on the same local network.
|
|
|
|
|
|
|
|
|
|
|
|
## Configure buttons and scenes
|
|
|
|
|
|
|
|
|
|
|
|
`hue.yaml` controls the dashboard. The `buttons` entries define button order,
|
|
|
|
|
|
the target room/zone, and the ordered scenes to cycle through:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
buttons:
|
|
|
|
|
|
1:
|
|
|
|
|
|
name: Ground floor
|
|
|
|
|
|
scenes:
|
|
|
|
|
|
- Sunset allure
|
|
|
|
|
|
- Relax
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Each button name must match a `rooms` or `zones` entry. Scene names must match
|
|
|
|
|
|
scenes for that same group in the main `scenes` catalog. The app expects exactly
|
|
|
|
|
|
eight buttons.
|
|
|
|
|
|
|
|
|
|
|
|
## Command line client
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
# Find local bridges
|
|
|
|
|
|
python3 hue.py discover
|
|
|
|
|
|
|
|
|
|
|
|
# List rooms, zones, and scenes
|
|
|
|
|
|
python3 hue.py list
|
|
|
|
|
|
|
|
|
|
|
|
# Activate a scene or control a group directly
|
|
|
|
|
|
python3 hue.py scene "Evening relax"
|
|
|
|
|
|
python3 hue.py on "Living room"
|
|
|
|
|
|
python3 hue.py off "zone:Downstairs"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Credentials are saved with owner-only permissions in
|
|
|
|
|
|
`secrets.yaml`:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
bridge: 192.168.1.10
|
|
|
|
|
|
app_key: your-hue-application-key. # the hue bride api key
|
|
|
|
|
|
api_key: your-dashboard-api-key # the key for this services api
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The file is ignored by Git and should not be shared. `hue.py register` creates
|
|
|
|
|
|
or updates it, including a random dashboard API key. You can override values
|
|
|
|
|
|
for either tool with environment variables:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
FASTHUE_API_KEY=YOUR_DASHBOARD_KEY HUE_BRIDGE=192.168.1.10 HUE_APP_KEY=YOUR_HUE_KEY .venv/bin/python app.py
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The reverse-proxy path is configured separately in `config.yaml`:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
url_prefix: /hue
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Set it to an empty value when FastHue is served at `/`.
|
|
|
|
|
|
|
|
|
|
|
|
## HTTP API
|
|
|
|
|
|
|
|
|
|
|
|
Every API request needs the single `api_key` from `secrets.yaml` as a Bearer
|
|
|
|
|
|
token. Override it with `FASTHUE_API_KEY` or `--api-key` when necessary.
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
export FASTHUE_API_KEY='the-api_key-from-secrets.yaml'
|
|
|
|
|
|
curl -H "Authorization: Bearer $FASTHUE_API_KEY" http://localhost:5000/api/groups
|
|
|
|
|
|
curl -X POST -H "Authorization: Bearer $FASTHUE_API_KEY" \
|
|
|
|
|
|
http://localhost:5000/api/groups/GROUP_UUID/next-scene
|
|
|
|
|
|
curl -X POST -H "Authorization: Bearer $FASTHUE_API_KEY" \
|
|
|
|
|
|
http://localhost:5000/api/groups/GROUP_UUID/off
|
|
|
|
|
|
curl -X POST -H "Authorization: Bearer $FASTHUE_API_KEY" \
|
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
|
-d '{"name":"Relax"}' \
|
|
|
|
|
|
'http://localhost:5000/api/groups/Ground%20floor/scene'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`GET /api/groups` returns each configured group ID, its power state, and its
|
|
|
|
|
|
active scene. Use those IDs for the `next-scene` and `off` endpoints. The
|
|
|
|
|
|
`scene` endpoint uses the URL-encoded room or zone name (not its UUID), and
|
|
|
|
|
|
activates the named scene only when it belongs to that group. Keep this service on
|
|
|
|
|
|
a trusted network; the included Flask server uses HTTP, so place it behind an
|
|
|
|
|
|
HTTPS reverse proxy if it needs to cross an untrusted network.
|
|
|
|
|
|
|
|
|
|
|
|
## Deploy with Gunicorn and Nginx
|
|
|
|
|
|
|
|
|
|
|
|
These instructions target a Linux host running systemd. Gunicorn listens on a
|
|
|
|
|
|
Unix socket; Nginx is the only public-facing process.
|
|
|
|
|
|
|
|
|
|
|
|
Install the project and dependencies under a service account, then place its
|
|
|
|
|
|
credentials outside the web root:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
sudo useradd --system --create-home --shell /usr/sbin/nologin fasthue
|
2026-08-09 16:38:29 +02:00
|
|
|
|
sudo mkdir -p /srv/fasthue /etc/fasthue
|
|
|
|
|
|
sudo chown fasthue:fasthue /srv/fasthue /etc/fasthue
|
|
|
|
|
|
# Copy this project into /srv/fasthue, then:
|
|
|
|
|
|
sudo -u fasthue python3 -m venv /srv/fasthue/.venv
|
|
|
|
|
|
sudo -u fasthue /srv/fasthue/.venv/bin/pip install -r /srv/fasthue/requirements.txt
|
2026-08-09 16:32:42 +02:00
|
|
|
|
sudo install -o fasthue -g fasthue -m 600 secrets.yaml /etc/fasthue/secrets.yaml
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Create `/etc/systemd/system/fasthue.service`:
|
|
|
|
|
|
|
|
|
|
|
|
```ini
|
|
|
|
|
|
[Unit]
|
|
|
|
|
|
Description=FastHue dashboard
|
|
|
|
|
|
After=network-online.target
|
|
|
|
|
|
Wants=network-online.target
|
|
|
|
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
|
|
Type=exec
|
|
|
|
|
|
User=fasthue
|
|
|
|
|
|
# Use your Nginx worker group (commonly www-data; use nginx on some distros).
|
|
|
|
|
|
Group=www-data
|
2026-08-09 16:38:29 +02:00
|
|
|
|
WorkingDirectory=/srv/fasthue
|
2026-08-09 16:32:42 +02:00
|
|
|
|
Environment=FASTHUE_SECRETS_FILE=/etc/fasthue/secrets.yaml
|
|
|
|
|
|
RuntimeDirectory=fasthue
|
|
|
|
|
|
RuntimeDirectoryMode=0750
|
2026-08-09 17:07:38 +02:00
|
|
|
|
ExecStart=/srv/fasthue/.venv/bin/gunicorn --workers 1 --umask 007 --bind unix:/run/fasthue/fasthue.sock app:gunicorn_app
|
2026-08-09 16:32:42 +02:00
|
|
|
|
Restart=on-failure
|
|
|
|
|
|
RestartSec=3
|
|
|
|
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
|
|
WantedBy=multi-user.target
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Enable it:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
sudo systemctl daemon-reload
|
|
|
|
|
|
sudo systemctl enable --now fasthue
|
|
|
|
|
|
sudo systemctl status fasthue
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Configure Nginx with a TLS-enabled virtual host (replace the hostname and
|
|
|
|
|
|
certificate paths). This example publishes FastHue at `/hue`:
|
|
|
|
|
|
|
|
|
|
|
|
```nginx
|
|
|
|
|
|
server {
|
|
|
|
|
|
listen 443 ssl http2;
|
|
|
|
|
|
server_name hue.example.com;
|
|
|
|
|
|
|
|
|
|
|
|
ssl_certificate /etc/letsencrypt/live/hue.example.com/fullchain.pem;
|
|
|
|
|
|
ssl_certificate_key /etc/letsencrypt/live/hue.example.com/privkey.pem;
|
|
|
|
|
|
|
|
|
|
|
|
location = /hue {
|
|
|
|
|
|
return 302 /hue/;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
location /hue/ {
|
|
|
|
|
|
# The trailing slash strips /hue before proxying to Flask.
|
|
|
|
|
|
proxy_pass http://unix:/run/fasthue/fasthue.sock:/;
|
|
|
|
|
|
proxy_set_header Host $host;
|
|
|
|
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
|
|
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
|
|
proxy_set_header X-Forwarded-Prefix /hue;
|
|
|
|
|
|
proxy_set_header Authorization $http_authorization;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The `www-data` group in the systemd unit and `--umask 007` make the socket
|
|
|
|
|
|
readable by Nginx. If your Nginx workers use another group, replace `www-data`
|
|
|
|
|
|
in the unit with that group. Test and reload Nginx with
|
|
|
|
|
|
`sudo nginx -t && sudo systemctl reload nginx`.
|
|
|
|
|
|
Use the HTTPS URL on the iPhone with `/hue/?key=YOUR_API_KEY` once per browser
|
|
|
|
|
|
session. Set `url_prefix` in `config.yaml` to another leading-slash prefix when
|
|
|
|
|
|
needed, and use the same value in Nginx's `location` and
|
|
|
|
|
|
`X-Forwarded-Prefix`. Keep
|
|
|
|
|
|
`secrets.yaml` readable only by the service account.
|
|
|
|
|
|
|
|
|
|
|
|
The bridge typically has a locally issued TLS certificate, so certificate
|
|
|
|
|
|
verification is disabled by default. Add `--verify-tls` only when your computer
|
|
|
|
|
|
trusts that certificate.
|
|
|
|
|
|
|
|
|
|
|
|
## Files
|
|
|
|
|
|
|
|
|
|
|
|
- `app.py` — Flask dashboard and Hue API integration
|
|
|
|
|
|
- `hue.py` — dependency-free command-line client and registration tool
|
|
|
|
|
|
- `config.yaml` — reverse-proxy settings
|
|
|
|
|
|
- `hue.yaml` — button, group, and scene configuration
|
|
|
|
|
|
- `static/` and `templates/` — phone interface
|