UniFi WiFi Presence
This driver turns your Ubiquiti UniFi network into a presence sensor. It polls the UniFi Network Controller (UDM, UDM Pro, UDR, Cloud Key Gen2+, or a self-hosted controller) for the list of connected clients and flips a person tracker zone to present the moment that person's phone associates with the wifi — and to away once the phone has been off the network for a grace period. No app to install on the phones, no GPS, no cloud: if the phone joins the wifi, they're home.
Trackers can be tied directly to GEM user accounts: give a user a wifi_macs attribute holding their phone's MAC and the driver auto-creates a tracker zone for them under the Location subsystem. People without GEM accounts (kids, guests, housekeeper) get hand-made zones with the MAC as the zone address.
The OwnTracks driver gives you named geofence regions ("at work", "at school") from a phone app publishing GPS. This driver answers only one question — on the home network or not — but needs nothing installed on the phone and detects arrival within seconds of the phone joining wifi. They coexist happily; both feed the same person-tracker conventions.
Prerequisites
- A UniFi Network Controller managing the wifi the phones join, reachable from the GEM host over HTTPS.
- Preferred — an API key minted on the controller (UniFi UI → Settings → Control Plane → Integrations, or Admins & Users → API keys). This uses Ubiquiti's official, versioned Network Integration API and requires Network App 9.3 or newer. The key is shown once — copy it when you mint it.
- Fallback — a local admin account, for controllers too old for the Integration API. Ubiquiti SSO / remote accounts will not work with the legacy local API — create a dedicated local account. The driver only ever reads, so a restricted/read-only local admin is ideal.
- Each phone's wifi MAC address as your network sees it — see the private-address note below.
Modern iOS and Android connect with a per-network "private" MAC, not the hardware MAC printed in the phone's About screen. The per-network address is stable for your SSID by default, so it works fine — but you must use that address. Read it from the phone's wifi settings while connected to your network, or run the device's get_clients command and pick the phone out of the list. On iOS 18+ make sure Private Wi-Fi Address is set to Fixed (not Rotating) for the home network, or the address changes periodically and presence breaks.
Setup steps
- Add the device. In the GEM admin console open Devices → Add Device and choose UniFi WiFi Presence.
- Fill in the connection fields:
- Controller IP / Host — the UniFi controller, not an access point.
- Controller Port —
443for UniFi OS consoles,8443for standalone self-hosted controllers. - API Key (preferred) — paste the key minted on the controller. Username/password can stay empty; they are ignored when a key is set. Stored encrypted.
- Admin Username / Admin Password (legacy fallback) — only for controllers without the Integration API (Network App older than 9.3). The password is stored encrypted. In this mode also check UniFi OS Console: on for UDM / UDM Pro / UDR / Cloud Key G2+, off for a standalone self-hosted controller.
- Site — leave
defaultunless the controller is multi-site. With an API key the driver matches this against the controller's site names automatically (single-site controllers just work).
- Save and enable. The driver validates the key (or logs in) and starts polling the client list (every 15 s by default).
- Find the phone MACs. Run the get_clients command on the device (Commands tab) while the phones are on wifi — it lists every client with MAC, hostname, and network.
- Associate MACs with people — either way works, and they can be mixed:
- Linked to a GEM user (recommended): add a
wifi_macsattribute to the user (targetauth_user) containing their phone's MAC. Comma-separate several MACs to also count a watch or tablet. Within one sync pass the driver creates a tracker zone for that user under Location, labeled with their name and carrying auser_idattribute pointing back at the account. The zone's tracked MACs follow the user'swifi_macsfrom then on — edit the attribute, never the zone address. - Stand-alone person: create a zone by hand on this device with address = the phone MAC (
aa:bb:cc:dd:ee:ff), under the Location subsystem. Extra devices for the same person go in the zone'smac_addressesattribute.
- Linked to a GEM user (recommended): add a
- Verify. Toggle wifi off on a phone, run get_tracked, and watch
seen_seconds_agoclimb; turn wifi back on and the zone flips topresentwithin one poll.
Like the OwnTracks driver, auto-created tracker zones live under a Location subsystem that the driver creates the first time it needs it. A new subsystem appearing after setup is expected.
How presence is decided
- Arrival is immediate. A tracked MAC appearing in the controller's client list flips the zone to
presenton the next poll — at the default 15 s interval, effectively as the phone walks in the door. - Departure is debounced. Phones nap off wifi to save power, so a missing MAC does not mean gone. The zone flips to
awayonly after every tracked MAC has been off the network for Away Timeout (default 300 s). Don't set it below ~120 s or sleeping phones will flap. - Wired clients don't count unless Count Wired Clients is enabled — a desktop that stays on all night is not a person.
- VPN / Teleport clients never count (API-key mode) — a phone connected remotely over VPN is away, not home.
- On each transition the driver stamps
last_arrivalorlast_departure(the departure time is backdated to the final sighting, not the moment the grace timer expired).
Attribute reference
Device attributes
| Attribute | Required | Default | Purpose |
|---|---|---|---|
ip | ✓ | — | Controller IP or hostname. |
api_key | ✓* | — | Official Integration API key (Network App 9.3+). Preferred. Stored encrypted. |
username / password | ✓* | — | Legacy local admin credentials — the fallback when no API key is set. Password stored encrypted. |
port | 443 | 443 UniFi OS, 8443 standalone. | |
unifi_os | true | Legacy mode only: off for standalone self-hosted controllers. Ignored with an API key. | |
site | default | Controller site — short-name in legacy mode, matched against site names in API-key mode. | |
status_interval | 15000 | Milliseconds between client-list polls. Arrival latency is at most one interval. | |
away_timeout | 300 | Seconds off the network before a person flips to away. | |
include_wired | false | Count ethernet clients as presence too. | |
sync_users | true | Auto-create tracker zones from GEM users carrying wifi_macs. |
* One of the two auth modes is required: either api_key, or username + password.
User attribute
| Attribute | Target | Purpose |
|---|---|---|
wifi_macs | auth_user | Comma-separated MACs of this user's personal devices. Setting it is what links a person to a tracker zone. |
Zone attributes
| Attribute | Written when | Meaning |
|---|---|---|
state / presence | On transition | present or away. state is what triggers and UIs read; presence mirrors it for person-tracker consumers like voice "who's home?" queries. |
last_arrival | Going present | ISO timestamp of the arrival. |
last_departure | Going away | ISO timestamp of the final wifi sighting. |
ssid | On change while present | The wifi network the phone was last seen on. |
mac_addresses | You set it | Extra MACs that also count as this person. |
user_id | Zone auto-creation | The linked GEM user account. |
Commands
| Command | Purpose |
|---|---|
get_clients | List everything currently on the network (MAC, hostname, IP, SSID, signal) — the tool for finding a phone's MAC. |
get_tracked | Every tracked person with their MACs, current state, and seconds since last sighting. |
sync_users | Re-read wifi_macs from GEM users now and create any missing tracker zones (otherwise runs automatically every few minutes). |
login | Force a fresh controller login (legacy mode) or re-validate the API key and site (API-key mode). |
Using presence in automation
Tracker zones are ordinary zones, so the standard surfaces apply:
- "They just got home" — an Attribute Trigger on the person's zone, firing when
statebecomespresent, running a welcome-home macro (lights, climate, disarm prompt). Trigger debounce, day, and hour masks all apply. - "Everyone left" — a trigger on
statebecomingaway, gated on the other trackers' zones also readingaway, arming security and setting back climate. - Voice — the AI assistant's "who's home?" / "is Nathan home?" answers read these zones automatically; no extra wiring.
- History — presence transitions land in attribute history like any other state change, so arrivals and departures are queryable in History.
What this driver does not do
- No location beyond home/away. There are no named regions — for "at work" / "at school" answers, pair it with OwnTracks.
- No writes to the controller. It never changes network configuration; the account can be read-only.
- No zone cleanup. A person whose
wifi_macsis cleared keeps their zone (it drops to disconnected as a flag). Retired trackers are deleted by hand.
Troubleshooting
- API key rejected (401/403). Re-mint the key on the controller and confirm the Network App is 9.3 or newer — older controllers have no Integration API, so clear
api_keyand use a local admin username/password instead. - Login fails with 400/401 (no API key set). The account is a Ubiquiti SSO / remote account — the legacy local API requires a local admin account on the controller. Or skip the problem entirely and mint an API key.
- "Site not found" in API-key mode. The driver matches the Site field against the controller's site names; the error message lists the names it found. Single-site controllers resolve automatically.
- A phone never shows present. Compare
get_clientsoutput against the MAC you configured while the phone is on wifi. Nine times out of ten it's the private-address mismatch described above. - Presence flaps between present and away. The phone is aggressively power-napping off wifi, or its private address is rotating. Raise
away_timeoutto 600+, and pin the phone's private address to Fixed for this SSID. - No zone appears for a user. The user needs a
wifi_macsattribute with at least one valid MAC, the account must be enabled, and sync_users must be on. Run thesync_userscommand and watch the log for the create. get_clientsis empty. Wrong Site short-name (it is literallydefaulton single-site controllers), or the standalone/UniFi OS toggle doesn't match the controller type.