Mastering Router-Based Presence and Network Monitoring: Integrating OpenWrt with Home Assistant

NGC 224
DIY Smart Home Creator
The Challenge: Robust Network Presence & Device Monitoring
Reliable presence detection is the cornerstone of truly intelligent home automation. While methods like Home Assistant Companion App, BLE beacons, or even dedicated ESPresence nodes offer varying degrees of accuracy, they often come with limitations: app battery drain, BLE range issues, or additional hardware dependencies. Beyond human presence, monitoring the online/offline status of critical network devices (servers, IP cameras, media players) is crucial for proactive alerting and automation stability.
This guide explores how to leverage your OpenWrt-powered router – a device already central to your network – to provide highly accurate, network-level presence detection for both people and devices. By integrating OpenWrt with Home Assistant, you gain a robust, always-on mechanism that's less prone to the flakiness of client-side solutions, and unlocks granular control over your smart home ecosystem.
Step-by-Step Setup: Integrating OpenWrt Data
1. Setting Up OpenWrt for Secure Data Export via SSH
First, we need to prepare your OpenWrt router to securely provide network data to Home Assistant. This involves enabling SSH and setting up a dedicated user with appropriate permissions.
- Enable SSH & Create a User: Ensure SSH is enabled on your OpenWrt router (System > Administration > SSH Access). For security, create a non-root user specifically for Home Assistant:
- Install
jq
for JSON Parsing (Optional but Recommended): If you plan to parse complex JSON output directly on the router before sending it to Home Assistant,jq
is invaluable. - Configure SSH Keys for Passwordless Access: On your Home Assistant host, generate an SSH key pair (if you haven't already) and copy the public key to your OpenWrt router for the
hauser
user.
ssh root@your_router_ip
adduser hauser
passwd hauser
opkg update
opkg install jq
# On Home Assistant OS/Container host
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_openwrt
ssh-copy-id -i ~/.ssh/id_rsa_openwrt.pub hauser@your_router_ip
Screenshot Placeholder: OpenWrt SSH Access Configuration Page
2. Integrating SSH Commands via Home Assistant's shell_command
With SSH set up, we can now use Home Assistant's shell_command
integration to execute commands on your OpenWrt router and retrieve data.
Add the following to your configuration.yaml
(or a dedicated package file):
# configuration.yaml
shell_command:
openwrt_wifi_clients:
command: "ssh -i /config/.ssh/id_rsa_openwrt -o StrictHostKeyChecking=no hauser@your_router_ip 'ubus call hostapd.wlan0 get_clients | jq -c . || true'"
openwrt_arp_table:
command: "ssh -i /config/.ssh/id_rsa_openwrt -o StrictHostKeyChecking=no hauser@your_router_ip 'cat /proc/net/arp || true'"
Explanation:
-i /config/.ssh/id_rsa_openwrt
: Specifies the private key for authentication. Ensure/config/.ssh/
is accessible by Home Assistant.-o StrictHostKeyChecking=no
: Bypasses the host key check for the first connection. For production, add your router's host key to~/.ssh/known_hosts
on your HA host for better security.ubus call hostapd.wlan0 get_clients | jq -c .
: Retrieves connected WiFi clients from thewlan0
interface (adjust if you have multiple, e.g.,wlan1
for 5GHz) and outputs it as a compact JSON string.cat /proc/net/arp
: Dumps the router's ARP table, showing MAC-to-IP mappings for all devices the router has communicated with recently (both wired and wireless).|| true
: Ensures the command always exits successfully, preventing Home Assistant from marking theshell_command
as failed if the SSH connection or command itself has a minor issue, which can be useful for polling.
3. Creating Template Sensors for Presence Detection
Now, we'll process the data retrieved by our shell_command
s using Home Assistant's template
sensors to create meaningful binary sensors for presence.
Add to your sensors.yaml
(or equivalent):
# sensors.yaml
- platform: template
sensors:
person_phone_presence:
friendly_name: "Person's Phone Presence"
value_template: >
{% set mac_address = "XX:XX:XX:XX:XX:XX" %}
{% set wifi_clients = state_attr('sensor.openwrt_wifi_clients_data', 'json') %}
{% if wifi_clients is not none %}
{% set client_macs = wifi_clients | map(attribute='mac') | list %}
{{ mac_address.lower() in client_macs | map('lower') | list }}
{% else %}
false
{% endif %}
device_class: connectivity
unique_id: person_phone_presence_openwrt
media_server_online:
friendly_name: "Media Server Online"
value_template: >
{% set server_mac = "YY:YY:YY:YY:YY:YY" %}
{% set arp_table = states('sensor.openwrt_arp_table_data') %}
{% if arp_table is not none %}
{{ server_mac.lower() in arp_table.lower() }}
{% else %}
false
{% endif %}
device_class: connectivity
unique_id: media_server_online_openwrt
Note: We need an intermediate command_line
sensor to capture the output of the shell_command
.
# sensors.yaml (continued)
- platform: command_line
name: OpenWrt WiFi Clients Data
command: "!include_dir_list .../your_shell_commands/openwrt_wifi_clients.sh"
json_attributes: "$"
value_template: "OK"
scan_interval: 30 # Poll every 30 seconds
- platform: command_line
name: OpenWrt ARP Table Data
command: "!include_dir_list .../your_shell_commands/openwrt_arp_table.sh"
value_template: "OK"
scan_interval: 60 # Poll every 60 seconds
Replace !include_dir_list .../your_shell_commands/openwrt_wifi_clients.sh
with the actual shell_command
call or a script that runs it. The shell_command
in configuration.yaml
cannot directly provide sensor data, it's for triggering actions. For data retrieval, use command_line
sensor directly or an automation to call the shell_command
and update an input_text
helper which then feeds the template sensor.
Correction for data retrieval: shell_command
does not return output. For data, we use the command_line
sensor. It is more direct.
# sensors.yaml for direct data retrieval
- platform: command_line
name: OpenWrt WiFi Clients
command: "ssh -i /config/.ssh/id_rsa_openwrt -o StrictHostKeyChecking=no hauser@your_router_ip 'ubus call hostapd.wlan0 get_clients | jq -c . || true'"
json_attributes: "$"
value_template: "{{ now().isoformat() }}" # Just to update state, data is in attributes
scan_interval: 30
- platform: command_line
name: OpenWrt ARP Table
command: "ssh -i /config/.ssh/id_rsa_openwrt -o StrictHostKeyChecking=no hauser@your_router_ip 'cat /proc/net/arp || true'"
value_template: "{{ value }}" # Raw output as state
scan_interval: 60
# Now, the template sensors for presence:
- platform: template
sensors:
person_phone_presence:
friendly_name: "Person's Phone Presence"
value_template: >
{% set mac_address = "XX:XX:XX:XX:XX:XX" %}
{% set wifi_clients_list = state_attr('sensor.openwrt_wifi_clients', 'value') %}
{% if wifi_clients_list is not none %}
{% set client_macs = wifi_clients_list | from_json | map(attribute='mac') | list %}
{{ mac_address.lower() in client_macs | map('lower') | list }}
{% else %}
false
{% endif %}
device_class: connectivity
unique_id: person_phone_presence_openwrt
media_server_online:
friendly_name: "Media Server Online"
value_template: >
{% set server_mac = "YY:YY:YY:YY:YY:YY" %}
{% set arp_table = states('sensor.openwrt_arp_table') %}
{% if arp_table is not none %}
{{ server_mac.lower() in arp_table.lower() }}
{% else %}
false
{% endif %}
device_class: connectivity
unique_id: media_server_online_openwrt
4. Monitoring Critical Router Metrics (Advanced)
Beyond presence, you can monitor router health. For example, check free memory:
# sensors.yaml
- platform: command_line
name: OpenWrt Free Memory
command: "ssh -i /config/.ssh/id_rsa_openwrt -o StrictHostKeyChecking=no hauser@your_router_ip 'free | grep Mem: | awk '{print $4}' || true'"
unit_of_measurement: "kB"
value_template: "{{ value | int }}"
scan_interval: 300 # Every 5 minutes
Troubleshooting Section
- SSH Connection Issues:
- Verify SSH keys: Ensure
id_rsa_openwrt
andid_rsa_openwrt.pub
are correctly generated and copied. Permissions on.ssh
folder and keys on HA host should be restrictive (e.g.,chmod 600 id_rsa_openwrt
). - Firewall on OpenWrt: Check if SSH port (22) is open for your HA host's IP.
- Test SSH manually:
ssh -i /config/.ssh/id_rsa_openwrt hauser@your_router_ip
from the HA terminal to confirm connectivity.
- Verify SSH keys: Ensure
- Command Not Returning Data / Empty State:
- Execute the command directly on OpenWrt (e.g.,
ubus call hostapd.wlan0 get_clients
) to verify its output. - Check
value_template
orjson_attributes
in Home Assistant. Use the Template Editor (Developer Tools) to test your Jinja2 templates with sample data. - Ensure
jq
is installed and working on OpenWrt if you're using it for parsing.
- Execute the command directly on OpenWrt (e.g.,
- Stale Presence Data:
- Adjust
scan_interval
forcommand_line
sensors to a shorter duration (e.g., 30-60 seconds for presence). Be mindful of router load. - Consider an automation to periodically call
homeassistant.update_entity
for yourcommand_line
sensors if you need more dynamic control over polling.
- Adjust
command_line
sensor showsunknown
: This often means the command failed or timed out. Check Home Assistant logs (Settings > System > Logs) for specific errors. Increase timeout if the command takes long to execute.
Advanced Configuration & Optimization
1. Persistent IP Addresses for Reliable Detection
For critical devices, assign static DHCP leases on your OpenWrt router. This ensures their IP addresses remain consistent, making network monitoring and ARP table lookups more reliable, especially if devices occasionally drop off and rejoin the network.
Screenshot Placeholder: OpenWrt DHCP Static Leases Configuration Page
2. Optimizing Polling Frequency and Router Load
While polling every 30-60 seconds is fine for a few devices, too many command_line
sensors polling frequently can put a strain on both your Home Assistant and your router. Consider these optimizations:
- Staggered Polling: Use different
scan_interval
values for different sensors. - Event-Driven Updates (Advanced): For true real-time, consider making your OpenWrt router push data via MQTT on specific events (e.g., a DHCP lease change, or a device connecting/disconnecting from WiFi, using custom scripts that monitor
logread
orubus
events). This requires more advanced scripting on OpenWrt.
3. Consolidating Presence with Home Assistant's ping
and nmap
For ultimate robustness, combine router-based detection with Home Assistant's built-in ping
or nmap
device trackers. Router data confirms a device is connected to the network, while ping
/nmap
confirms it's actively responding. Use a group
or a more complex template sensor to combine these states:
# configuration.yaml
device_tracker:
- platform: ping
hosts:
person_phone_ping:
- your_phone_ip
# sensors.yaml
- platform: template
sensors:
person_combined_presence:
friendly_name: "Person's Combined Presence"
value_template: >
{{ is_state('binary_sensor.person_phone_presence', 'on') and
is_state('device_tracker.person_phone_ping', 'home') }}
device_class: presence
unique_id: person_combined_presence_openwrt_ping
4. Security Hardening for SSH Access
- Restrict User Permissions: On OpenWrt, edit
/etc/passwd
for thehauser
user to restrict their shell (e.g., to/bin/false
) and useauthorized_keys
command="..."
option to only allow specific commands. - Firewall Rules: Limit SSH access on OpenWrt to only your Home Assistant's IP address.
Real-World Example: Dynamic Home Automation
Leveraging OpenWrt data, you can create highly responsive and intelligent automations. Here's how to automate lighting and notifications:
Example 1: Dynamic Lighting with Occupancy
This automation turns on specific lights when a person's phone is detected as 'home' by the router and it's dark, and then ensures they turn off when the phone leaves.
# automations.yaml
- id: '1678901234567'
alias: 'Automate Living Room Lights with Phone Presence'
description: 'Turns on/off living room lights based on phone presence and ambient light'
trigger:
- platform: state
entity_id: binary_sensor.person_phone_presence
to: 'on'
for: '00:00:30'
- platform: state
entity_id: binary_sensor.person_phone_presence
to: 'off'
for: '00:05:00'
condition:
- condition: numeric_state
entity_id: sensor.living_room_light_sensor
below: 50 # Example: Light level in lux
action:
- choose:
- conditions:
- condition: state
entity_id: binary_sensor.person_phone_presence
to: 'on'
sequence:
- service: light.turn_on
target:
entity_id: light.living_room_main_lights
data:
brightness_pct: 70
transition: 2
- conditions:
- condition: state
entity_id: binary_sensor.person_phone_presence
to: 'off'
sequence:
- service: light.turn_off
target:
entity_id: light.living_room_main_lights
data:
transition: 5
mode: single
Example 2: Critical Device Offline Notifications
Receive immediate alerts if your media server or any other critical network device goes offline, as detected by the router's ARP table.
# automations.yaml
- id: '1678901234568'
alias: 'Notify if Media Server Goes Offline'
description: 'Sends a notification if the media server is not detected on the network'
trigger:
- platform: state
entity_id: binary_sensor.media_server_online
to: 'off'
for: '00:05:00' # Only trigger if offline for 5 minutes (to avoid transient drops)
action:
- service: notify.mobile_app_your_phone
data:
title: "Smart Home Alert: Media Server Offline"
message: "The media server ({{ states('sensor.media_server_online') }}) has been offline for 5 minutes. Please check its status."
data:
tag: "media-server-offline"
priority: "high"
mode: single
Best Practices & Wrap-up
- Modularity: Keep your OpenWrt scripts simple and focused on data extraction. Do complex logic and parsing within Home Assistant's templates. This makes debugging easier.
- Version Control: Treat your Home Assistant configuration (including these sensor definitions) and any custom OpenWrt scripts as code. Use Git to track changes, allowing you to easily revert to previous working states.
- Regular Backups: Always maintain backups of your Home Assistant configuration and your OpenWrt router's settings.
- Performance Monitoring: Keep an eye on your Home Assistant's system resource usage and your OpenWrt router's load. If you notice performance degradation, review your
scan_interval
values and consider optimizing your SSH commands. - Security First: Always follow the principle of least privilege. The dedicated SSH user on OpenWrt should only have the minimum necessary permissions.
By integrating OpenWrt with Home Assistant, you transform your router into a powerful data source for your smart home, enabling more reliable presence detection, proactive monitoring, and ultimately, a more intelligent and resilient automation environment.

NGC 224
Author bio: DIY Smart Home Creator