Lox Nexus

UniFi Network plugin

Connects a UniFi console (UniFi Network) to Nexus through Ubiquiti's official Network Integration API. Presence of phones and computers, device and port states come into Nexus as sensors. Device restarts, PoE power-cycles, guest access, Wi-Fi networks and hotspot vouchers go the other way.

What it does

  • Sensors: per client: connected (presence), the access point or switch it is on, its IP address. Per device: online, state, firmware update available, CPU, memory, uptime and uplink rates. Per switch port: link, speed and PoE state. Whether a Wi-Fi network is on, and the code of the newest voucher.
  • Actuators: restart a device, power-cycle a PoE port, authorize or unauthorize a guest, switch a Wi-Fi network on or off, create a voucher.
  • Catalog: the connected clients (by MAC address, and by name where the MAC is private), the devices with their ports and the statistics they report, the Wi-Fi networks and the vouchers, all ready to add. Each client, device, port and Wi-Fi network on the panel has a Signals button that offers just its signals.
  • Panel: whether the console can be read, its Network version and site, the number of clients and devices online, a switch per Wi-Fi network, the devices with a Restart button, the switch ports (no Power-cycle button: that is the actuator's), and the connected clients. It refreshes after every poll.
  • Actions: Create voucher (answers with the code and a QR code of it), List vouchers, Delete voucher.

Before you start

  • A UniFi console (a Cloud Gateway, a Dream Machine, or a server running UniFi Network) that Nexus can reach over HTTPS, and its IP address or host name.
  • An API key: on the console open Settings › Control Plane › Integrations and create one. Treat it like a password.
  • The console's certificate fingerprint (SHA-256). Consoles use a self-signed certificate, so pinning it is the safe way to connect. Test connection shows the fingerprint the console presents; see "Good to know".

Configuration

Field Description
Console address (host) required IP address or host name, e.g. 192.168.1.1. The API is then reached under /proxy/network/integration. A URL with a path, e.g. https://unifi.example:8443/integration, uses that path instead. https only.
API key (apiKey) required; secret The key from Settings › Control Plane › Integrations.
Site (site) optional; default empty Name or id of the UniFi site. Empty: the console's first site, which is Default on most consoles.
Poll every (pollInterval) optional; default 15 s Time between two reads of clients, devices and Wi-Fi networks, 5 to 3600 seconds.
Timeout (timeout) optional; default 10 s Time a single request may take, 1 to 20 seconds.
Certificate fingerprint (SHA-256) (fingerprint) optional, recommended The console's certificate, as 64 hex digits (AB:CD:…, with or without colons). Only this certificate is accepted.
Accept any certificate (insecure) (acceptAnyCertificate) optional; default off Skips the certificate check, for this console only. A pinned fingerprint wins over it.

Sensors and actuators

Kind Name (id) Value What it is
sensor Client connected (client-connected) boolean true while the client is connected (presence).
sensor Client connected to (client-uplink) text The access point or switch the client is on, VPN for VPN clients, empty while away.
sensor Client IP address (client-ip) text The client's IP address, empty while away.
sensor Device online (device-online) boolean true while the device's state is ONLINE.
sensor Device state (device-state) text ONLINE, OFFLINE, UPDATING, GETTING_READY, ADOPTING, …
sensor Firmware update available (device-update) boolean true while the console offers a firmware update.
sensor CPU load (device-cpu) number, % The device's CPU load.
sensor Memory use (device-memory) number, % The device's memory use.
sensor Uptime (device-uptime) number, s Seconds since the device started.
sensor Uplink download (device-uplink-rx) number, Mbit/s What the device receives over its uplink.
sensor Uplink upload (device-uplink-tx) number, Mbit/s What the device sends over its uplink.
sensor Port link (port-link) boolean true while the port's link is up.
sensor Port speed (port-speed) number, Mbit/s The link speed, 0 while nothing is connected.
sensor Port PoE state (port-poe) text UP, DOWN, LIMITED, UNKNOWN, or OFF when PoE is switched off.
sensor Wi-Fi network on (wifi-enabled) boolean true while the Wi-Fi network is switched on.
sensor Newest voucher code (voucher-code) text The code of the newest voucher this instance created, e.g. for a display.
actuator Restart device (device-restart) boolean Restarts the device on a pulse (a rising true).
actuator Power-cycle PoE port (port-power-cycle) boolean Cuts the port's PoE power briefly on a pulse, restarting what it powers.
actuator Guest access (guest-access) boolean true authorizes a connected guest client, false unauthorizes it.
actuator Wi-Fi network on/off (wifi-enabled) boolean Switches the Wi-Fi network (e.g. the guest network) on or off.
actuator Create voucher (create-voucher) boolean Creates a hotspot voucher on a pulse; its code goes to the code sensor.

Good to know

  • Presence flickers: phones leave Wi-Fi while they sleep. A debounce step of e.g. 10 minutes on the wire hides such short absences; it delays arrivals by as much, so for an instant "home" use Loxone's own off-delay on the raw value instead. The plugin doesn't smooth anything itself.
  • iPhones and rotating addresses: with "Private Wi-Fi Address" set to rotating, a phone's MAC address changes. Add its sensors by name (the catalog's "(by name)" entries) instead.
  • The fingerprint: with the field empty, Test connection fails and names the fingerprint the console presented. Compare it with the console's own certificate (your browser shows it when you open the console directly), then paste it. After a new certificate on the console, update it here.
  • Restart and power-cycle act on a new pulse only: a repeated true does nothing, and so does the value Nexus delivers again after a start, so restarting Nexus doesn't repeat a restart. Use short pulses: a true still held when Nexus starts may count as new.
  • What the API doesn't offer: no events (the plugin polls), no WAN up or down state, no per-client signal strength or traffic, no blocking of clients, no alarms or history.

More

The full reference (params of every sensor and actuator, the endpoints the plugin uses, polling and backoff, the catalog and panel, actions, security notes, limits and development notes) is in REFERENCE.md next to this file, and on the plugin's page in the Nexus documentation.

The API it uses

Only Ubiquiti's official, documented UniFi Network Integration API (/v1, the X-API-Key header), as described by its OpenAPI spec for UniFi Network 10.3.58. No classic endpoints (/api/s/…), no websockets, nothing reverse-engineered. UniFi Protect and Access are not covered; they would be separate plugins.

Endpoint Used for
GET /v1/info the Network version; the start and Test connection check the key with it
GET /v1/sites finding the site's id
GET /v1/sites/{siteId}/clients presence and the clients table, every poll
POST /v1/sites/{siteId}/clients/{clientId}/actions AUTHORIZE_GUEST_ACCESS (with timeLimitMinutes), UNAUTHORIZE_GUEST_ACCESS
GET /v1/sites/{siteId}/devices device state and firmware, every poll
GET /v1/sites/{siteId}/devices/{deviceId} ports: of switching devices every poll, of others while a port signal needs them, and for the catalog
GET /v1/sites/{siteId}/devices/{deviceId}/statistics/latest CPU, memory, uptime, uplink rates, only of online devices with such a signal
POST /v1/sites/{siteId}/devices/{deviceId}/actions RESTART
POST /v1/sites/{siteId}/devices/{deviceId}/interfaces/ports/{portIdx}/actions POWER_CYCLE
GET /v1/sites/{siteId}/wifi/broadcasts Wi-Fi networks, every poll
GET and PUT /v1/sites/{siteId}/wifi/broadcasts/{id} switching a Wi-Fi network on or off
GET, POST and DELETE /v1/sites/{siteId}/hotspot/vouchers listing, creating and deleting vouchers

Lists are read page by page (offset, limit=200) until totalCount, at most 50 pages. Every request has the configured timeout and uses its own connection, closed after the answer. Errors show the API's message (from its "Error Message" body) and the HTTP status.

Minimum version. The plugin is built and tested against the API description of UniFi Network 10.3.58; no real console was available to test against. Older Network versions with the Integration API may lack endpoints or fields. A missing Wi-Fi endpoint doesn't stop the rest: the panel then says it can't list the Wi-Fi networks.

What the API can't do

These are not in the official API, so the plugin doesn't offer them (and doesn't fake them): events or push updates (it polls), the WAN's up or down state (the WAN list has only ids and names), signal strength or traffic per client, blocking clients, alarms and history. Clients that aren't connected aren't listed at all.

Connection and certificate

The console's certificate is checked in one of three ways, for this plugin's connections only (nothing changes for the rest of Nexus):

Setting Check
fingerprint set only a certificate with this SHA-256 fingerprint is accepted (pinning; recommended)
neither the usual check against trusted authorities and the host name; a self-signed console fails it
Accept any certificate on, no fingerprint none; anyone in the network path could read the API key

The decision is made after the TLS handshake and before the request is written, so the API key never reaches a server whose certificate was refused. A refusal names the fingerprint the server presented.

To read the fingerprint: save the settings with the fingerprint empty and press Test connection; the message names it. Or ask the console yourself:

openssl s_client -connect 192.168.1.1:443 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256

Compare it with what your browser shows for the console's certificate before you trust it. Any spelling works: colons, spaces, dashes or none, upper or lower case.

Start and errors. At the start the plugin reads /v1/info once. A refused key (HTTP 401 or 403) fails the start, since a restart can't fix it. A console that doesn't answer doesn't: the instance starts degraded and keeps trying.

Polling

One loop reads clients, devices and Wi-Fi networks every Poll every seconds, plus ports and statistics as described above. A poll asked for while one runs joins it. After a failed poll the next attempt waits longer each time: one interval, then two, four, up to 5 minutes; the first good poll goes back to the normal pace. While polls fail the instance is degraded with the reason, and every sensor fed by the poll fails with "can't read the UniFi console: …", keeping its last value; the next good poll sends values again and ends the failure. A changing failure is logged once, not once per poll.

Sensors

All poll-fed sensors emit after every poll (the bus drops repeats) and right away when added to a running instance.

Client sensors

client-connected (boolean), client-uplink (text) and client-ip (text).

Param Form title Description
client Client required A MAC address (02:00:00:00:10:01, also with dashes or without separators), or with matchBy name the client's name as UniFi shows it. Groups the signals in lists.
matchBy Match by default mac mac or name. By name ignores case and outer spaces; if several connected clients have the name, the one connected last counts.

A client that isn't in the list of connected clients is away: false, and empty text. client-uplink names the uplink device (uplinkDeviceId), VPN or Teleport for those clients, and is empty for a client without one.

Device sensors

device-online, device-state, device-update, device-cpu, device-memory, device-uptime, device-uplink-rx, device-uplink-tx.

Param Form title Description
device Device required The device's MAC address. Stable across renames; groups the signals in lists.
  • A MAC the console doesn't list fails the signal ("no device … on the console").
  • The statistics sensors ask the console for that device's statistics/latest only while they exist. A device that isn't ONLINE has no statistics: the signal fails with its state until it is back.
  • device-uplink-rx and -tx read uplink.rxRateBps and txRateBps. The spec doesn't describe their unit. Read as bits per second, the values from a real console were implausibly low, so the plugin takes them as bytes per second, despite the name: it multiplies by 8 and divides by 1 000 000 for Mbit/s. If they look 8 times too high on yours, please report it.
  • device-cpu and device-memory are cpuUtilizationPct and memoryUtilizationPct; device-uptime is uptimeSec.

Port sensors

port-link, port-speed (Mbit/s) and port-poe (text).

Param Form title Description
device Device required The switch's MAC address.
port Port required The port's number (idx), from 1.

Signal lists group them as "device ▸ port". A port the device doesn't have, or port-poe on a port without PoE, fails the signal. port-poe is the API's PoE state (UP supplies power, DOWN, LIMITED, UNKNOWN), or OFF while PoE is switched off on the port.

wifi-enabled and voucher-code

wifi-enabled takes network, the Wi-Fi network's id as the catalog fills it in, so renaming the network in UniFi keeps the signal. Its name (SSID, ignoring case) works too, as signals added before ids name it; a name that matches none or several networks fails the signal.

voucher-code has no params. It shows the newest voucher created by this instance (the action or the actuator), also after a restart, as the code is kept in the instance's store.

Actuators

All accept a boolean.

Actuator Params On
device-restart device a new rising true
port-power-cycle device, port a new rising true
guest-access client, matchBy, minutes (optional, 1 to 1 000 000; empty: the site's default) true authorizes, false unauthorizes
wifi-enabled network true on, false off
create-voucher minutes (default 1440), guests (optional), note (default Loxone) a new rising true
  • Pulses. device-restart, port-power-cycle and create-voucher act on true after false only (the start counts as false), so a Loxone pulse acts once and a repeated true does nothing. A value Nexus replays after a start (a forced value) is ignored by these and by guest-access, so restarting Nexus doesn't repeat a restart or a voucher. That relies on Nexus flagging what it delivers again: a true the Miniserver still holds when Nexus starts, sent after Nexus last kept its values (every few seconds), counts as new, so use short pulses. Restart and power-cycle log who asked (the signal's name) at info level.
  • Guest access looks the client up in a fresh list of connected clients. It refuses clients that aren't connected (UniFi can't authorize them) or aren't guests. Authorizing again starts a new authorization with fresh counters.
  • Wi-Fi. The API has no partial update: the plugin reads the network's full object, changes enabled and sends everything back with PUT (without the read-only id and metadata). Nothing is sent when the network already is in that state, so a replay just confirms it. The object contains the passphrase: it is never logged, shown or returned.

Catalog

Node Entries
Clients (connected now) ▸ client connected, connected to, IP address, by MAC; the same by name for clients with a private (randomised) MAC or none; guest access for guests
Devices ▸ device online, state, firmware update, restart, and of CPU, memory, uptime and uplink rates those the device reports now, with their values (an offline device offers all of them)
Devices ▸ device ▸ Port n link, speed; PoE state and power-cycle on PoE ports
Wi-Fi networks ▸ network on (sensor), on/off (actuator)
Vouchers newest voucher code, create voucher (1 day)

Only connected clients appear: connect a device once to add it. The catalog reads the ports of devices the poll doesn't read anyway, once per browse.

Every row of the panel's clients, devices and ports tables, and every Wi-Fi network, has a Signals button: it opens the catalog at that client's, device's, port's or network's node (ids client:<id>, device:<id>, port:<device id>:<n>, wifi:<id>, the API's ids).

Panel

Pushed after every poll and after actions (panelUpdates: "push").

  • Console: a notice while the console can't be read; tiles with clients connected, devices online (with a bar) and guests, at the top of the page; reading state, Network version, address, certificate check, site, last successful poll and last error as "… ago" (the error greyed once recovered), newest voucher.
  • Wi-Fi networks: each with on or off and a switch (the set-wifi action, after a confirm).
  • Devices: name, model, state (green online; red offline, connection interrupted, isolated, incorrect topology; yellow updating, getting ready, adopting, pending adoption; grey deleting), IP and MAC address, firmware (yellow with an update), and an instant Restart button with a confirm.
  • Ports: ports of switching devices (and of devices with a port signal): link, speed and PoE. The page has no Power-cycle button: cutting a port's power is left to the port-power-cycle actuator, wired on purpose.
  • Ports not shown (a warning, only when needed): devices whose ports couldn't be read, with the reason, and devices with ports in a form the plugin doesn't read (left out one by one; debug logging names the port and the field). Missing values sent as null (e.g. an empty SFP cage's speed) are read as missing, not as an error.
  • Connected clients: name, IP and MAC address, where it is connected, type, access (guests with Authorize or Unauthorize, which opens the dialog for the minutes), connected since.

No secrets: neither the API key nor a Wi-Fi passphrase.

Actions

Action Where Input Answer
Create voucher (create-voucher) top minutes (default 1440), guests, note (default Nexus), dataLimitMB, downloadKbps, uploadKbps the code and a QR code of it
List vouchers (list-vouchers) top none a table of the vouchers that haven't expired
Delete voucher (delete-voucher) top code (letters, digits, dashes) how many were deleted; asks first
Switch Wi-Fi network (set-wifi) panel network (id), enabled the new state; asks first
Restart (restart-device) panel device (MAC) asks first
Guest access (guest-access) panel client, matchBy, authorize, minutes what happened

Voucher limits follow the API: minutes 1 to 1 000 000, data 1 to 1 048 576 MB, rates 2 to 100 000 kbit/s. The QR code holds the code itself (the API names no portal address to encode). Delete voucher uses the API's filter code.eq('…').

Security notes

  • The API key is a secret field: never shown again, never logged, sent only to the console over TLS after the certificate check.
  • Voucher codes are guest credentials. They appear in the action's answer, the panel and on the voucher-code sensor (and so on the bus and in history, if recorded), never in the log.
  • The Wi-Fi switch reads and writes the network's full settings including its passphrase, which stays inside the plugin.

Development

  • Tests run against a fake console in the test process: HTTPS on 127.0.0.1 (port 0) with a self-signed certificate generated at test time (no key in the repository), synthetic fixtures in test/fixtures/ shaped after the spec's schemas, and a TestClock for polls, backoff and timeouts. They never contact a real address.
  • Build: npm run build in plugins/unifi writes dist/plugin.js.