# DD-WRT Binding
This binding monitors and manages DD-WRT, OpenWrt, Tomato, and other Linux-based routers and access points via SSH.
It auto-detects the wireless chipset driver (Broadcom wl, Atheros wl_atheros, iwinfo, iw) and adapts its commands accordingly.
Features include:
- Device telemetry — CPU load, CPU temperature, uptime, WAN IP, interface traffic counters
- Wireless radio monitoring — SSID, channel, mode, associated client list
- Client tracking — wireless, wired, and VPN client presence, signal strength, roaming between APs, MAC randomization support
- Syslog monitoring — real-time DHCP, wireless association, warning, and error events via
logread -fortail -F - Firewall rule control — enable/disable DD-WRT GUI-configured filter rules via nvram
- Device reboot — remote reboot via SSH
# Supported Things
| ThingTypeUID | Label | Description |
|---|---|---|
network | DD-WRT Network | Bridge representing the network of managed devices |
device | DD-WRT Device | A DD-WRT, OpenWrt, Tomato, or compatible Linux device managed via SSH |
radio | Wireless Radio | A wireless radio interface on a device (e.g. wl0, wlan0) |
client | Client | A client endpoint on the network (wireless, wired, or VPN) |
firewall-rule | Firewall Rule | A GUI-configured firewall filter rule from DD-WRT nvram |
The binding auto-detects the chipset and firmware variant during the first SSH connection.
Tested firmware includes DD-WRT, OpenWrt, FreshTomato, and generic Linux with iw/iwconfig.
# Discovery
After adding and configuring the network bridge, the binding automatically discovers:
- Devices — from the hostnames list configured on the bridge
- Radios — by probing wireless interfaces on each device
- Clients — from radio association lists, DHCP lease tables, and ARP/neighbor caches on each device
- Firewall rules — from DD-WRT nvram
filter_ruleentries (DD-WRT gateway devices only)
Discovery results appear in the openHAB inbox after each device refresh cycle.
# Quick Start
- Enable SSH on your router (see Firmware SSH Setup below)
- Set up SSH key authentication (see SSH Key Setup below)
- Test from the openHAB host:
ssh root@routershould log in without a password prompt - Add the bridge in openHAB with your device hostnames:
Bridge ddwrt:network:home "Home Network" [ hostnames="router,office-ap,garage-ap" ]
- Wait for discovery — devices, radios, clients, and firewall rules appear in the inbox
The hostnames parameter is a comma-separated list of hostnames or IP addresses.
Each hostname is connected via SSH and auto-detected during the first refresh cycle.
Devices that fail to connect are retried every refresh interval until they come online.
You do not need to manually add device things — they are discovered automatically from this list.
# SSH Authentication
The binding connects to devices using Apache MINA SSHD. Authentication is attempted in this order:
- SSH keys from
$OPENHAB_USERDATA/ddwrt/keys/(any files in this directory) - SSH keys from
$HOME/.ssh/(standard OpenSSH key files likeid_ed25519,id_rsa) - Password from thing configuration (least secure, not recommended)
On openhabian, $HOME for the openHAB service is /var/lib/openhab, so keys should be placed in /var/lib/openhab/.ssh/.
The binding also respects $HOME/.ssh/config for per-host settings including HostName, User, Port, ProxyJump, and IdentityFile.
This means you can use jump hosts (ProxyJump) to reach devices behind NAT.
If no user is configured on the thing, the binding defers to $HOME/.ssh/config or the system username.
If no port is configured (port = 0), the binding defers to $HOME/.ssh/config or port 22.
# SSH Key Setup
SSH key authentication is strongly recommended over password authentication.
# 1. Generate an SSH key pair (if you don't have one)
ssh-keygen -t ed25519 -C "openhab"
This creates $HOME/.ssh/id_ed25519 (private key) and $HOME/.ssh/id_ed25519.pub (public key).
The binding automatically loads keys from $HOME/.ssh/.
# 2. Copy the public key to each device
ssh-copy-id root@router
ssh-copy-id root@office-ap
Or manually append the public key to the device's authorized keys file (see firmware-specific instructions below).
# 3. Verify passwordless login
ssh root@router
You should be logged in without a password prompt.
# Key directories
The binding loads private keys from two directories:
| Directory | Description |
|---|---|
$HOME/.ssh/ | Standard OpenSSH key directory (recommended) |
$OPENHAB_USERDATA/ddwrt/keys/ | Binding-specific key directory for dedicated keys |
Files named id_ed25519, id_rsa, id_ecdsa, or any file not ending in .pub are loaded as private keys.
Files named known_hosts, config, authorized_keys, and backup files (~) are skipped.
If openHAB runs as a different user (e.g. openhab), place keys in that user's $HOME/.ssh/ directory or in the $OPENHAB_USERDATA/ddwrt/keys/ directory.
Note: SSH keys are loaded once when the binding starts. If you add or change key files, restart openHAB for the binding to pick them up.
# SSH config (optional)
You can use $HOME/.ssh/config to set per-host defaults:
Host router
HostName 192.168.1.1
User root
Host office-ap
HostName 192.168.1.10
User root
Port 2222
Host remote-ap
HostName 10.0.0.1
User root
ProxyJump router
This lets you use short hostnames in the binding configuration and reach devices behind NAT via ProxyJump.
# Host Key Verification
The binding uses Trust On First Use (TOFU) host key verification with $HOME/.ssh/known_hosts:
- First connection — the host key is automatically accepted and saved to
$HOME/.ssh/known_hosts - Subsequent connections — the saved key is verified; if it matches, the connection proceeds
- Changed key — the connection is rejected and a warning is logged with instructions to fix it
If a device's host key changes (e.g. after a firmware reflash), you will see:
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
The host key for 192.168.1.1:22 has changed.
Add correct host key in /home/openhab/.ssh/known_hosts to get rid of this message.
Offending key in /home/openhab/.ssh/known_hosts:1
To fix this, remove the old key and restart openHAB so the binding re-reads known_hosts and re-learns the new key on the next connection:
ssh-keygen -R 192.168.1.1
Note: The known_hosts file is read once when the binding starts and cached in memory. Changes to this file (including new keys accepted via TOFU) are written back automatically, but manual edits require an openHAB restart to take effect.
# Firmware SSH Setup
Each firmware has its own way to enable SSH and install public keys:
- DD-WRT — Services → Secure Shell → Enable SSHd, paste public key into "Authorized Keys". See DD-WRT SSH documentation (opens new window).
- OpenWrt — System → Administration → SSH Access is enabled by default on port 22. Paste public key into System → Administration → SSH-Keys. See OpenWrt Dropbear key-based authentication (opens new window).
- FreshTomato — Administration → Admin Access → Enable SSH Daemon, paste public key into "Authorized Keys". See FreshTomato documentation (opens new window).
- Standard OpenSSH — For generic Linux devices, use
ssh-copy-idor append the public key to$HOME/.ssh/authorized_keys. See OpenSSH manual (opens new window).
# Thing Configuration
# network Bridge Configuration
| Name | Type | Description | Default | Required | Advanced |
|---|---|---|---|---|---|
| hostnames | text | Comma-separated list of hostnames or IP addresses for device discovery | N/A | no | no |
| user | text | Default SSH username for all devices (if omitted, uses SSH config) | N/A | no | no |
| password | text | Default SSH password for all devices (if omitted, uses key auth) | N/A | no | no |
| port | integer | Default SSH port for all devices (0 = use SSH config or 22) | 0 | no | yes |
| refreshInterval | integer | Polling interval in seconds | 3 | no | yes |
# device Thing Configuration
| Name | Type | Description | Default | Required | Advanced |
|---|---|---|---|---|---|
| hostname | text | Hostname or IP address of the device | N/A | yes | no |
| user | text | SSH username (overrides bridge default; if omitted, uses SSH config) | N/A | no | no |
| password | text | SSH password (overrides bridge default) | N/A | no | no |
| port | integer | SSH port (0 = use SSH config or 22) | 0 | no | yes |
| refreshInterval | integer | Polling interval in seconds (overrides bridge default) | 3 | no | yes |
| syslogPriority | text | Minimum syslog level to capture (debug/info/notice/warning/error/critical) | warning | no | yes |
# radio Thing Configuration
| Name | Type | Description | Default | Required | Advanced |
|---|---|---|---|---|---|
| interfaceId | text | Wireless interface identifier (e.g. wl0, wlan0) | N/A | yes | no |
| parentDeviceMac | text | MAC address of the parent device | N/A | no | yes |
# client Thing Configuration
| Name | Type | Description | Default | Required | Advanced |
|---|---|---|---|---|---|
| hostname | text | Hostname of the client (stable identifier with MAC randomization) | N/A | yes | no |
| mac | text | MAC address of the client (may change due to MAC randomization) | N/A | no | yes |
The thing ID is derived from the client's sanitized hostname (e.g. Joes-Phone → joesphone).
If the client uses MAC randomization, the binding tracks it by hostname rather than MAC address.
When a new randomized MAC appears with the same DHCP hostname, the binding merges it with the existing client.
# firewall-rule Thing Configuration
| Name | Type | Description | Default | Required | Advanced |
|---|---|---|---|---|---|
| ruleId | text | The nvram filter_rule key (e.g. filter_rule3) | N/A | yes | no |
# Channels
# Network Bridge Channels
| Channel | Type | Read/Write | Description |
|---|---|---|---|
| total-clients | Number | RO | Total clients connected across all devices |
| wireless-clients | Number | RO | Wireless clients connected across all devices |
| wired-clients | Number | RO | Wired clients connected across all devices |
| wan-ip | String | RO | External WAN IP address (gateway devices only) |
| wan-in | Number:DataAmount | RO | Total bytes received on WAN (gateway devices only) |
| wan-out | Number:DataAmount | RO | Total bytes sent on WAN (gateway devices only) |
| dhcp-leases | Number | RO | Number of active DHCP leases (gateway devices only) |
| dhcp-remaining | Number | RO | Available IP addresses in the DHCP pool (gateway devices only) |
| last-dhcp-event | String | RO | Last DHCP lease/renewal/release event |
# Device Channels
| Channel | Type | Read/Write | Description |
|---|---|---|---|
| online | Switch | RO | Whether the device is reachable via SSH |
| uptime | DateTime | RO | System boot time (updates only on reboot) |
| cpu-load | Number | RO | 1-minute load average |
| cpu-temp | Number:Temperature | RO | CPU temperature |
| if-in | Number:DataAmount | RO | Total bytes received on LAN bridge (br0) |
| if-out | Number:DataAmount | RO | Total bytes sent on LAN bridge (br0) |
| reboot | Switch | RW | Turn ON to reboot the device; automatically resets to OFF |
| device-wireless-clients | Number | RO | Wireless clients associated with radios on this device |
| syslog-connected | Switch | RO | Whether the syslog follower has an active SSH channel |
| last-warning-event | String | RO | Last warning-level syslog line |
| last-error-event | String | RO | Last error-level syslog line |
| warning-events | Number | RO | Warning event count since startup |
| error-events | Number | RO | Error event count since startup |
| last-wireless-event | String | RO | Last wireless association/deassociation event |
# Network Bridge Trigger Channels
| Channel | Kind | Description |
|---|---|---|
| dhcp-event | Trigger | Fires when a DHCP lease/renewal/release is detected |
# Device Trigger Channels
| Channel | Kind | Description |
|---|---|---|
| warning-event | Trigger | Fires when a warning-level syslog event arrives |
| error-event | Trigger | Fires when an error-level syslog event arrives |
| wireless-event | Trigger | Fires when a wireless assoc/deassoc is detected |
# Radio Channels
| Channel | Type | Read/Write | Description |
|---|---|---|---|
| enabled | Switch | RW | Whether the radio is enabled |
| ssid | String | RO | Wireless network name |
| channel | Number | RO | Wireless channel number |
| mode | String | RO | Wireless mode (e.g. Master, Client, Ad-Hoc) |
| client-count | Number | RO | Number of clients associated with this radio |
| assoclist | String | RO | Comma-separated MACs of associated clients |
# Client Channels
| Channel | Type | Read/Write | Description |
|---|---|---|---|
| online | Switch | RO | Whether the client is currently connected |
| mac-address | String | RO | Current MAC address of the client |
| hostname | String | RO | Hostname from DHCP lease |
| ip-address | String | RO | IP address from DHCP lease |
| connection-type | String | RO | How the client is connected: wireless, wired, or vpn |
| ap | String | RO | Name of the radio the client is associated with |
| ap-mac | String | RO | MAC address of the access point |
| ssid | String | RO | SSID the client is connected to |
| channel | Number | RO | Wireless channel number |
| signal | Number:Power | RO | Signal strength in dBm |
| snr | Number | RO | Signal-to-noise ratio in dB |
| rx-rate | Number:DataTransferRate | RO | Receive rate |
| tx-rate | Number:DataTransferRate | RO | Transmit rate |
| last-seen | DateTime | RO | Timestamp when the client was last seen online |
# Firewall Rule Channels
| Channel | Type | Read/Write | Description |
|---|---|---|---|
| enabled | Switch | RW | Whether the firewall rule is on |
| description | String | RO | Description of the firewall rule |
# Properties
# Device Properties
| Property | Description |
|---|---|
| mac | MAC address of the device's primary interface |
| model | Hardware model (from MOTD, DMI, or device-tree) |
| firmware | Firmware version (DD-WRT build, OpenWrt release, etc.) |
| chipset | Wireless chipset type (broadcom, atheros, marvell, etc.) |
# Full Example
# Example Thing Configuration
Bridge ddwrt:network:home "Home Network" [ hostnames="router,office-ap,garage-ap", user="root", refreshInterval=3 ] {
Thing device router "Main Router" [ hostname="router" ]
Thing device officeap "Office AP" [ hostname="office-ap" ]
Thing device garageap "Garage AP" [ hostname="garage-ap" ]
Thing radio router_wl0 "Router 2.4GHz" [ interfaceId="wl0" ]
Thing radio router_wl1 "Router 5GHz" [ interfaceId="wl1" ]
Thing radio officeap_wlan0 "Office 2.4GHz" [ interfaceId="wlan0" ]
Thing radio officeap_wlan1 "Office 5GHz" [ interfaceId="wlan1" ]
Thing client joesphone "Joe's Phone" [ hostname="Joes-Phone", mac="c2:af:b0:aa:9c:ef" ]
Thing client livingroomtv "Living Room TV" [ hostname="LivingRoomTV", mac="b0:8b:a8:7f:99:2c" ]
Thing firewall-rule bedtime10 "Bedtime 10-12" [ ruleId="filter_rule3" ]
Thing firewall-rule bedtime12 "Bedtime 12-6" [ ruleId="filter_rule4" ]
}
# Example Item Configuration
// Network bridge
String WanIp "WAN IP [%s]" { channel="ddwrt:network:home:wan-ip" }
String LastDhcp "Last DHCP [%s]" { channel="ddwrt:network:home:last-dhcp-event" }
// Device telemetry
Switch RouterOnline "Router Online [%s]" { channel="ddwrt:device:home:router:online" }
DateTime RouterUptime "Router Uptime [%1$tF %1$tR]" { channel="ddwrt:device:home:router:uptime" }
Number RouterCpuLoad "Router CPU [%.2f]" { channel="ddwrt:device:home:router:cpu-load" }
Number:Temperature RouterCpuTemp "Router Temp [%.1f %unit%]" { channel="ddwrt:device:home:router:cpu-temp" }
Switch RouterReboot "Reboot Router" { channel="ddwrt:device:home:router:reboot" }
// Syslog events
String RouterLastWifi "Last Wireless [%s]" { channel="ddwrt:device:home:router:last-wireless-event" }
// Radio
String Router24Ssid "2.4GHz SSID [%s]" { channel="ddwrt:radio:home:router_wl0:ssid" }
Number Router24Channel "2.4GHz Channel [%d]" { channel="ddwrt:radio:home:router_wl0:channel" }
Number Router24Clients "2.4GHz Clients [%d]" { channel="ddwrt:radio:home:router_wl0:client-count" }
// Client
Switch PhoneOnline "Phone Online [%s]" { channel="ddwrt:client:home:joesphone:online" }
String PhoneConnType "Phone Connection [%s]" { channel="ddwrt:client:home:joesphone:connection-type" }
String PhoneAp "Phone AP [%s]" { channel="ddwrt:client:home:joesphone:ap" }
String PhoneSsid "Phone SSID [%s]" { channel="ddwrt:client:home:joesphone:ssid" }
Number PhoneSnr "Phone SNR [%d dB]" { channel="ddwrt:client:home:joesphone:snr" }
DateTime PhoneLastSeen "Phone Last Seen [%1$tF %1$tR]" { channel="ddwrt:client:home:joesphone:last-seen" }
// Firewall
Switch Bedtime10 "Bedtime 10-12 [%s]" { channel="ddwrt:firewall-rule:home:bedtime10:enabled" }
Switch Bedtime12 "Bedtime 12-6 [%s]" { channel="ddwrt:firewall-rule:home:bedtime12:enabled" }
# Example Sitemap Configuration
sitemap home label="Home Network" {
Frame label="Network" {
Text item=WanIp
Text item=LastDhcp
}
Frame label="Router" {
Switch item=RouterOnline
Text item=RouterUptime
Text item=RouterCpuLoad
Text item=RouterCpuTemp
Switch item=RouterReboot
}
Frame label="Radios" {
Text item=Router24Ssid
Text item=Router24Channel
Text item=Router24Clients
}
Frame label="Clients" {
Switch item=PhoneOnline
Text item=PhoneConnType
Text item=PhoneAp
Text item=PhoneSsid
Text item=PhoneSnr
Text item=PhoneLastSeen
}
Frame label="Parental Controls" {
Switch item=Bedtime10
Switch item=Bedtime12
}
}
# Syslog Monitoring
The binding follows the device syslog in real time via SSH to detect events without polling. The syslog command is auto-detected:
- DD-WRT / OpenWrt —
logread -f(if BusyBoxlogreadsupports-f) - Tomato —
tail -F /var/log/messages - Generic Linux —
journalctl -f --no-pager -p <priority>
DHCP and wireless events are parsed inline from the syslog stream and immediately update the cache, so client association and presence changes are reflected within seconds.
The syslogPriority device configuration parameter controls the minimum severity for warning/error event channels.
DHCP and wireless events are always captured regardless of this setting.
# Presence and ARP/Neighbor Resolution
Current client presence uses the following precedence:
- Wireless clients are considered present while they are associated with an AP, even if they are not currently generating IP traffic. Their
connection-typechannel readswireless. - Wired clients are inferred from the DHCP lease table and ARP/neighbor cache when they are not present in any wireless radio association list. Their
connection-typechannel readswired. - Gateway ARP/neighbor data is preferred when a managed gateway is present. The local openHAB host ARP cache is only used as a fallback when no gateway is available.
- Static hostname mapping files and inline
hostnameMappingsare treated as static hints only; dynamic neighbor data is authoritative for current IP presence.
Dynamic neighbor entries are classified as active for 60 seconds, stale after 60 seconds, and expired after 120 seconds. Static entries remain static until updated. Dump APs are not queried for ARP because they are Layer-2 bridges and do not have authoritative client IP visibility.
# MAC Randomization
Modern mobile devices randomize their MAC address per network. The binding handles this by tracking clients primarily by their DHCP hostname. When a device reconnects with a new randomized MAC but the same hostname, the binding automatically merges the new MAC with the existing client record.
If a client has no DHCP hostname (some IoT devices), the binding generates a synthetic hostname from the MAC address OUI vendor prefix (e.g. Espressif-a1b2c3).
# Multi-AP Roaming
The binding aggregates wireless clients across all managed access points.
When a client roams from one AP to another, the syslog follower detects the association/deassociation events and updates the client's ap, ap-mac, and ssid channels in real time.