Your Router’s DHCP Lease Table Is a Starting Point, Not an Asset Inventory
The failure mode: you can’t tell what broke because you never wrote down what the device was
A Zigbee contact sensor stops reporting. The dashboard shows it unavailable. You have three plausible causes and no way to separate them: the battery died, the device fell off the mesh, or the network path between it and the coordinator changed. If you had a labeled list — this MAC is the back-door sensor, it lives on the north wall, it was installed in March — the diagnosis collapses to a battery swap or a re-pair. Without that list, you spend twenty minutes on a ladder with a phone flashlight.
The router’s DHCP lease table is the cheapest place to start building that list. It is not an asset inventory in any accounting sense, and it will not tell you whether a device is healthy. But it is a read-only record of which clients currently hold an address, and that is enough to seed a labeled inventory you maintain yourself.
What a lease table actually contains
DHCP dynamic allocation assigns an IP address to a client for a limited period of time, or until the client explicitly relinquishes it. Dynamic allocation is also the only one of DHCP’s three allocation mechanisms that allows automatic reuse of an address no longer needed by its prior client. A binding is a collection of configuration parameters, including at least an IP address, bound to a client and managed by the DHCP server. (RFC 2131, sections 1 and 1.5.)
That has a direct consequence for inventory work: the table is a snapshot of who currently holds an address, not a registry of what you own. A device that has been unplugged for a month may still appear if its lease has not expired. A device that rebooted and got a new address may appear twice if the old lease is still in the pool. A device with a static IP will never appear at all.
In practice, consumer routers expose some subset of these fields per row:
- IP address
- MAC address
- Hostname, if the client sent one
- Lease expiry or remaining time, if the firmware shows it
Which of those appear, and whether you can export them as CSV, depends on the router. Treat the router UI as a place to read and copy, not as a system of record.
The local-first equivalent: Home Assistant’s DHCP Browser
If you already run Home Assistant, you have a second view of the same data that does not depend on the router’s UI. The DHCP Browser displays devices discovered by Home Assistant using various network methods, such as DHCP, ARP + PTR lookups, and router-based device trackers. It is opened from Settings > System > Network > DHCP Browser. (Home Assistant DHCP integration documentation.)
The DHCP discovery integration is enabled by default unless you have removed the default_config: line from your configuration. That matters for the audit: if someone trimmed default_config to speed up startup, the browser may be empty and the cause is configuration, not a quiet network.
Under the hood, Home Assistant exposes DHCP cache entries as DhcpServiceInfo objects with ip, hostname, and macaddress fields. The hostname is lowercase, and the MAC address is lowercase without colons — AA:BB:CC:12:34:56 becomes aabbcc123456. (Home Assistant developer documentation on network discovery.)
That normalization detail is worth internalizing. If you export a lease table from the router and match it against Home Assistant’s cache by hand, you will get duplicate rows unless you normalize both sides to the same form. Do it once at export time and the rest of the procedure gets simpler.
Build the inventory: a repeatable procedure
This is the part that turns a lease table into something useful. It takes about an hour the first time and fifteen minutes per quarter after that.
- Export or copy the current lease table. If the router offers CSV, take it. If not, copy the rows into a spreadsheet. Record the date of the export at the top; a lease table without a timestamp is hard to reason about later.
- Normalize the MAC column. Lowercase, strip colons and hyphens. This matches the form Home Assistant uses and prevents the same device appearing as two rows across exports.
- Add three columns you own: a human label (“back-door contact sensor”), a physical location (“north wall, kitchen”), and a protocol or integration (“Zigbee2MQTT”, “ESPHome”, “Z-Wave JS”). The lease table cannot supply any of these; you supply them once and they persist.
- Mark static-IP devices separately. Anything you configured with a fixed address will not appear in the lease table. Add it by hand from your own records, or accept that the inventory covers only DHCP clients and say so in the header.
- Cross-check against Home Assistant’s DHCP Browser. Rows in the browser but not in your export are usually devices that renewed between the export and the check, or devices on a segment the router’s lease view does not show. Rows in your export but not in the browser are worth a second look — the device may hold a lease but not have announced itself in a way Home Assistant saw.
- Store the finished file outside the router. A local spreadsheet, a Home Assistant helper, or a text file in your notes. The point is that a router replacement or a vendor cloud outage does not erase your labels.
If you want a broader audit habit around the small systems that run your week, the procedure in How to Audit the Small Systems That Quietly Run Your Week covers the surrounding maintenance cadence; this article is the network-address slice of it.
Cross-check reachability: a lease is not a heartbeat
The inventory tells you a device exists and holds an address. It does not tell you the device is working. For that you need a second signal, and for Zigbee devices Zigbee2MQTT provides one — with caveats worth understanding before you enable it.
Zigbee2MQTT’s device availability feature is disabled by default. When enabled, it publishes availability to zigbee2mqtt/[FRIENDLY_NAME]/availability with a retained payload of {"state":"online"} or {"state":"offline"}. (Zigbee2MQTT device availability documentation.)
The timeouts differ by device class, and the difference matters for how you read the inventory:
- Active devices (non-battery-powered) must check in every 10 minutes by default. If they do not, they are pinged; if the ping fails, they are marked offline.
- Passive devices (battery-powered) must check in every 25 hours by default and cannot be pinged. If they miss that window, they are marked offline.
Two operational notes from the same documentation. First, the timeout is persisted between Zigbee2MQTT restarts, so if you stop Zigbee2MQTT for longer than 10 minutes, all active devices will be marked offline initially until they check in again. Do not read that as a network fault. Second, the pinging can be heavy on the coordinator, especially with CC2530 or CC2531 adapters. If you have a large mesh on an older coordinator, raising the active timeout is a reasonable tradeoff; the documentation exposes it as a configuration option.
With availability enabled, your inventory gains a second column: reachable. A device can be in the inventory and unreachable — that is the interesting state, and it is the one a lease table alone cannot show you.
Where the lease table lies to you
Every inventory method has blind spots. These are the ones that show up most often in a residential or small-office install.
Battery devices look stale by design
A battery sensor that reports every few hours will not renew its DHCP lease on any schedule you can rely on. Do not treat a missing recent lease as a fault for those devices. Use the protocol’s own availability timeout instead — 25 hours for Zigbee2MQTT passive devices, per the documentation above.
Static IPs never appear
Anything you configured with a fixed address is invisible to the lease table. If you have a mix, decide up front whether the inventory covers DHCP clients only or both, and label the file accordingly.
Separate VLANs and guest networks
If your smart devices sit on an IoT VLAN, the lease table you are looking at may be the main LAN’s. Check which interface or pool the router is showing before you conclude a device is missing.
MAC randomization and firmware changes
Some devices change their MAC address after a firmware update or when a privacy feature is enabled. A row that disappears and a new row that appears in the same export is a hint, not proof. The inventory’s human label is what survives; the MAC is the join key, and join keys change.
Hostnames are not labels
A hostname is set by firmware or by the user, and nothing guarantees it describes the device’s function. ESP-A4F2 tells you it is an ESP-based device; it does not tell you it is the garage door controller. That is what your label column is for.
ESPHome’s own naming rules are a useful reminder of how little the hostname carries: the node name must be unique in the ESPHome network, may only contain lowercase characters, digits, and hyphens, and is limited to 24 characters by default. (ESPHome core configuration documentation.) That is an identifier, not a description.
What this procedure is not
It is not a compliance-grade asset register, and it is not a substitute for walking the house once a year and confirming that the thing labeled “north wall sensor” is still on the north wall. The lease table gives you the network-address half of the picture cheaply. The physical half still requires eyes.
It is also not always the right tool. If you have four smart devices and a mechanical timer on the porch light, a plain switch and a written note on the breaker panel is the smarter choice. The lease-table procedure earns its keep when you have enough devices that “which one is this?” is a recurring question — roughly the point where you stop recognizing MAC addresses on sight.
Quarterly re-export and diff
A one-time cleanup decays. The useful habit is a quarterly re-export and a diff against the previous file:
- New rows are devices you forgot you added, or a device that changed its MAC. Either way, they need a label.
- Disappeared rows are devices that were removed, changed identity, or moved to a static address. Confirm which before deleting the label.
- Rows that persist but are unreachable are the ones worth investigating — that is the state where a lease exists but the device is not reporting.
Keep the previous export. The diff is the value; a single snapshot tells you less than two snapshots side by side.
Graceful degradation
The reason to keep the finished inventory in a local file or a Home Assistant helper rather than only in the router UI is that the router is a single point of failure for the record. If the router dies and is replaced, the new one starts with an empty lease table and your labels are gone unless you stored them elsewhere. If the vendor’s cloud portal is the only place the lease table is visible and that portal is down, you cannot audit. A local copy survives both.
The same logic applies to the reachability signal. Zigbee2MQTT’s availability topics are retained MQTT messages, which means a subscriber that reconnects gets the last known state without waiting for the next check-in. That is useful during a broker restart, but it also means a stale “online” can persist until the device’s next check-in window. Read the timestamp, not just the state.
FAQ
Can I use the DHCP Browser as my only inventory?
No. It shows devices discovered through the methods listed in its documentation — DHCP, ARP + PTR lookups, and router-based device trackers — which is narrower than “everything on the network.” Static-IP devices and devices on segments the router does not expose will be missing. Use it as a cross-check against your own file, not as the file.
How often should I re-export?
Quarterly is a reasonable default for a residential install. The right interval is the one where the diff is small enough to review in a few minutes; if you are adding devices weekly, monthly is better. There is no documented standard here — this is a judgment call, not a finding.
Why does my Zigbee device show offline right after a Zigbee2MQTT restart?
Because the availability timeout is persisted between restarts. If Zigbee2MQTT was stopped for longer than the active-device timeout (10 minutes by default), active devices are marked offline until they check in again. Wait one check-in window before treating it as a fault.
Does enabling availability reduce outages?
The documentation describes the mechanism and its coordinator load; it does not claim a reliability outcome. What availability gives you is a signal you can act on. Whether that reduces downtime depends on whether you respond to it.
Sources
- RFC 2131, Dynamic Host Configuration Protocol: https://www.rfc-editor.org/rfc/rfc2131.txt
- Home Assistant DHCP integration: https://www.home-assistant.io/integrations/dhcp/
- Home Assistant network discovery (developer docs): https://developers.home-assistant.io/docs/network_discovery/
- Zigbee2MQTT device availability: https://www.zigbee2mqtt.io/guide/configuration/device-availability.html
- ESPHome core configuration: https://esphome.io/components/esphome.html