# 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 -f or tail -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_rule entries (DD-WRT gateway devices only)

Discovery results appear in the openHAB inbox after each device refresh cycle.

# Quick Start

  1. Enable SSH on your router (see Firmware SSH Setup below)
  2. Set up SSH key authentication (see SSH Key Setup below)
  3. Test from the openHAB host: ssh root@router should log in without a password prompt
  4. Add the bridge in openHAB with your device hostnames:
Bridge ddwrt:network:home "Home Network" [ hostnames="router,office-ap,garage-ap" ]
  1. 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:

  1. SSH keys from $OPENHAB_USERDATA/ddwrt/keys/ (any files in this directory)
  2. SSH keys from $HOME/.ssh/ (standard OpenSSH key files like id_ed25519, id_rsa)
  3. 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:

# 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-Phonejoesphone). 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 / OpenWrtlogread -f (if BusyBox logread supports -f)
  • Tomatotail -F /var/log/messages
  • Generic Linuxjournalctl -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-type channel reads wireless.
  • 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-type channel reads wired.
  • 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 hostnameMappings are 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.