Skip to main content

Unifi Dhcp Reservation

@sntxrr/unifi-dhcp-reservationv2026.08.20.1· 26d agoMODELSWORKFLOWS
01README

Declarative DHCP fixed-IP reservations on a local UniFi controller (UDM / UDM Pro / UDM SE). sync reads every reservation the controller holds; drift compares a desired set against it without writing, reporting missing, mismatched, unmanaged, duplicate and DHCP-pool-overlapping entries; apply reconciles the controller to the desired set and supports dryRun. device_drift does the same read-only comparison for adopted hardware, which cannot hold reservations at all and is addressed through device config instead — the one surface drift is blind to. Reservations live on the legacy Network API as user objects carrying use_fixedip/fixed_ip. Adds TOTP/MFA login support, which the upstream @mgreten/unifi auth flow lacks — UniFi SSO accounts with MFA reject password-only logins with MFA_AUTH_REQUIRED. Catches the failure mode where a reservation silently never takes effect because its address is already claimed by a statically-configured host. forget_client prunes stale client records the controller still remembers, refusing any MAC that holds a reservation or a live lease unless forced.

02Models1
@sntxrr/unifi/dhcp_reservationv2026.08.20.1unifi_dhcp_reservation.ts

Global Arguments

ArgumentTypeDescription
hoststringUDM IP address or hostname, e.g. 192.0.2.1
usernamestringUniFi admin username
passwordstringUniFi admin password (use a vault reference)
totpSecret?stringBase32 TOTP secret for MFA-enabled accounts (use a vault reference).
sitestringUniFi site name
fn sync()
Read every fixed-IP reservation from the controller and store one
fn drift(desired: array)
Compare a desired reservation set against the controller without
ArgumentTypeDescription
desiredarrayThe reservation set that should exist.
fn inventory(macs?: array, ips?: array, unreservedOnly: boolean)
List every host the controller knows about — clients and adopted
ArgumentTypeDescription
macs?arrayOnly these MACs (any separator style).
ips?arrayOnly these addresses.
unreservedOnlybooleanOnly hosts without a fixed-IP reservation.
fn verify(desired: array)
Pre-flight a desired reservation set against live DHCP leases: where
ArgumentTypeDescription
desiredarrayThe reservation set that should exist.
fn set_pool(start: string, stop: string, dryRun: boolean)
Change the DHCP range of the LAN network. Reports which hosts hold a
ArgumentTypeDescription
startstringFirst address of the new DHCP range.
stopstringLast address of the new DHCP range.
dryRunbooleanReport what the change would displace without writing.
fn device_pin(devices: array, netmask: string, gateway: string, dns1?: string, dns2?: string, dryRun: boolean)
Set a static address in device config on adopted UniFi hardware
ArgumentTypeDescription
devicesarrayThe devices to pin.
netmaskstring
gatewaystringDefault gateway for the fabric.
dns1?string
dns2?string
dryRunboolean
fn apply(desired: array, dryRun: boolean)
Reconcile the controller to a desired reservation set. Creates
ArgumentTypeDescription
desiredarrayThe reservation set that should exist.
dryRunbooleanReport the actions that would be taken without writing.
fn forget_client(macs: array, force: boolean, dryRun: boolean)
Forget stale client records the controller still remembers — the
ArgumentTypeDescription
macsarrayMACs to forget. Normalized before use, so any separator works.
forcebooleanForget even a MAC that holds a reservation or a live lease.
dryRunbooleanReport the actions that would be taken without writing.

Resources

reservation(infinite)— A DHCP fixed-IP reservation known to the controller.
drift(infinite)— Comparison of a desired reservation set against the controller.
verification(infinite)— Pre-flight of a desired reservation set against live DHCP leases.
inventory(infinite)— Every host the controller knows about, clients and adopted hardware.
pool_change(infinite)— Outcome of changing the DHCP range, and what it displaces.
device_pin_result(infinite)— Outcome of pinning one adopted device to a static address.
apply_result(infinite)— Outcome of reconciling one reservation.
forget_result(infinite)— Outcome of forgetting one stale client record.
03Workflows2
@sntxrr/unifi-drift-watch60d31d68-3677-4d22-b9c0-30a27bb54dbe

Compare the controller's DHCP reservations against the desired set, raise an Apprise notification when they diverge, and record a full host inventory. Static addressing drifts silently across three places at once -- in-guest config, controller reservations, and device-local settings -- and nothing announces it. This is what makes the controller half of that noticed without being asked. Read-only. Every method used here (drift, inventory) is non-writing; the reconciling methods (apply, set_pool,

compareCompare the desired reservation set against the controller
1.detect-drifthome-udm.drift— Report missing, mismatched, unmanaged, conflicting entries
alertNotify when the controller diverges from the desired set
1.notify-driftapprise.notify— Gated by a step `guard` -- a CEL predicate evaluated before the step, where TRUTHY MEANS SKIP. Read it as "skip when notification is off, or when the controller already matches the desired set". This used Apprise's own `when` argument until 2026-08-04, on the belief that swamp had no step-level way to branch on step output. That is true of `dependsOn` conditions, which are status-based, but not of `guard`. The guard is the better home for it because the gate belongs to the decision rather than t
snapshotRecord every host the controller currently knows
1.take-inventoryhome-udm.inventory— Full host list, clients and adopted hardware -- a dated record of what was on the network, which is what you want when working out *when* something changed.
@sntxrr/unifi-device-drift-watchf4905195-d596-4cf3-bd52-641ffadf6cb7

Compare the static addressing on adopted UniFi hardware against fabric-devices.json and raise an Apprise notification when it diverges. Companion to @sntxrr/unifi-drift-watch, which covers DHCP reservations and is blind to this. Adopted devices cannot hold reservations at all -- the controller rejects them with api.err.FixedIpAlreadyUsedByDevice -- so APs and switches are addressed through device config instead, and nothing was watching it. A switch was once found carrying a stale config_network

compareCompare desired device pins against the controller
1.detect-device-drifthome-udm.device_drift— Report mismatched, unpinned (back on DHCP), unadopted and undeclared devices. Reads /stat/device config_network; never writes.
alertNotify when the fabric diverges from the desired pins
1.notify-device-driftapprise.notify— Gated by a step `guard` -- a CEL predicate evaluated before the step, where TRUTHY MEANS SKIP. Read it as "skip when notification is off, or when the fabric already matches the desired pins". This used Apprise's own `when` argument until 2026-08-04, on the belief that swamp had no step-level way to branch on step output. That is true of `dependsOn` conditions, which are status-based, but not of `guard`. The gate belongs to the decision rather than to the transport, so it now reads the same whate
04Previous Versions5
2026.08.13.2
2026.08.13.1

Modified 1 models

2026.08.04.1

Added 2 workflows

2026.07.31.1

Modified 1 models

2026.07.21.2
05Stats
A
100 / 100
Downloads
2
Archive size
60.7 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License declared1/1earned
  • Verified public repository2/2earned
06Platforms
07Labels