Code Monkey home page Code Monkey logo

wifi-presence's Introduction

wifi-presence CI Go Reference

Presence detection on OpenWrt routers using connect/disconnect events of WiFi clients. Events are published to MQTT with Home Assistant integration.

  • What: Standalone application that runs on OpenWrt access points. Publishes WiFi client connect and disconnect events to an MQTT broker.
  • Why: Presence detection for home automation systems. Integrates with Home Assistant using the MQTT integration.
  • How: wifi-presence connects to hostapd's control interface to receive client connect and disconnect events.

Requirements:

  • Supported system running hostapd (such as an OpenWrt AP)
  • MQTT broker

Contents:

wifi-presence diagram

Quickstart

  1. Install wifi-presence on an OpenWrt access point. See okpg or download.
  2. Update the /etc/config/wifi-presence configuration file. Update the mqttAddr to the desired MQTT broker's address.
  3. Publish to the wifi-presence/config topic a list of MAC addresses to track. See configuration.

If using Home Assistant with the same MQTT broker, it should automatically be updated with each configured WiFi client.

Motivation

A home automation system that reacts to presence and absence events provides a more automated experience.

There are many ways to determine presence, e.g. motion sensors, network traffic monitoring, GPS & geo-fencing, etc. A person's phone typically travels with them in and out of a household. Most phones automatically connect and disconnect from home WiFi networks. Therefore a WiFi connection to one or more access points (APs) can be used as a proxy for physical presence.

There are similar projects that periodically ping client devices. This method may be less reliable than using hostapd because phones may not respond to pings while in low power mode. There is also a delay introduced by the ping frequency.

Home Assistant

wifi-presence integrates with Home Assistant using the MQTT integration. This can be enabled/disabled via the -hass.autodiscovery flag (true by default).

Although supported, Home Assistant isn't required. The messages published to MQTT can be consumed by any subscriber(s).

Configuration

Configuration is done via command-line flags at startup, and via MQTT at runtime.

The MQTT configuration determines the WiFi devices/clients wifi-presence will monitor. For each configured device, wifi-presence will publish state information on connect and disconnect. See the note about iOS devices and MAC addresses.

JSON via MQTT

Configuration is a JSON published to the config topic (<mqtt.prefix>/config).

Example configuration:

{
  "devices": [
    {
      "name": "My Phone",
      "mac": "AA:BB:CC:DD:EE:FF"
    },
    {
      "name": "TV",
      "mac": "00:11:22:33:44:55"
    },
    {
      "name": "Other Phone",
      "mac": "FF:EE:EE:DD:CC:BB"
    }
  ]
}

Example using Mosquitto to the JSON configuration in wifi-presence.config.json:

$ mosquitto_pub \
        -h 'my-mqtt-broker' \
        -t 'wifi-presence/config' \
        -r \
        -f wifi-presence.config.json

OpenWrt

The OpenWrt project is

OpenWrt is a highly extensible GNU/Linux distribution for embedded devices (typically wireless routers).

See the openwrt branch for package and build scripts.

opkg

Installation via OpenWrt's opkg package manager:

# Add public key
wget https://wifi-presence.s3.us-east-2.amazonaws.com/public.key
opkg-key add public.key

# Add package source as a custom feed
echo "src/gz wifi-presence http://wifi-presence.s3-website.us-east-2.amazonaws.com" >> /etc/opkg/customfeeds.conf

# Fetch/update list of packages
opkg update

# Install/upgrade wifi-presence
opkg install wifi-presence

# Update configuration
vim /etc/config/wifi-presence

# Enable and start wifi-presence
/etc/init.d/wifi-presence enable

Configuration

The configuration file is: /etc/config/wifi-presence.

Download

OpenWrt packages of wifi-presence are available for download.

Flags

When running wifi-presence using OpenWrt, use the provided /etc/config/wifi-presence file to configure.

As a standalone program, the following command-line flags are used:

wifi-presence [options]

Options:
  -apName string
    	Access point name (default "my-router")
  -debounce duration
    	Time to wait until considering a station disconnected. Examples: 5s, 1m (default 10s)
  -hass.autodiscovery
    	Enable Home Assistant MQTT autodiscovery (default true)
  -hass.prefix string
    	Home Assistant MQTT topic prefix (default "homeassistant")
  -help
    	Print detailed help message
  -hostapd.socks string
    	Hostapd control interface socket(s). Separate multiple paths by ':'
  -mqtt.addr string
    	MQTT broker address, e.g "tcp://mqtt.broker:1883"
  -mqtt.id string
    	MQTT client ID (default "wifi-presence.my-router")
  -mqtt.password string
    	MQTT password (optional)
  -mqtt.prefix string
    	MQTT topic prefix (default "wifi-presence")
  -mqtt.username string
    	MQTT username (optional)
  -sockDir string
    	Directory for local socket(s) (default "/var/folders/99/0z1nqy2d54x12xj2md6xz67w0000gn/T/")
  -v	Verbose logging (alias)
  -verbose
    	Verbose logging
  -version
    	Print version and exit

MQTT

wifi-presence publishes and subscribes to an MQTT broker. The -mqtt.prefix flag can be used to change the topic prefix, along with -hass.prefix for Home Assistant's topic prefix.

The following topics are used:

  • <PREFIX>/<AP_NAME>/status The status of wifi-presence (online / offline).

  • <PREFIX>/config wifi-presence subscribes to this topic for configuration updates.

  • <HASS_PREFIX>/device_tracker/<AP_NAME>/<MAC>/config If -hass.autodiscovery is enabled, then all configured devices will be published to these topics (based on their MAC address). Home Assistant subscribes to these topics and registers/unregisters entities accordingly based on messages received.

  • <PREFIX>/station/<AP_NAME>/<MAC>/state The state of a device (home / not_home) is published to these topics.

  • <PREFIX>/station/<AP_NAME>/<MAC>/attrs A JSON object with device attributes (SSID, BSSID, etc) is published to these topics.

hostapd

wifi-presence requires hostapd running with control interface(s) enabled. This is the default for OpenWrt. The hostapd configuration option is ctrl_interface. More information: https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf

The wifi-presence -hostapd.socks option should correspond to the socket locations defined by 'ctrl_interface'. Multiple sockets can be monitored (one socket per radio is created by hostapd).

hostapd full version

OpenWrt includes a stripped down version of hostapd, which is the default. This stripped down version is compatible with wifi-presence, but prevents wifi-presence from getting a list of connected devices at startup. In this case, all devices will be considered "disconnected" until a (re)-connect is seen.

The full version of hostapd can be installed to improve wifi-presence. When using the full version, wifi-presence will query hostapd for a list of connected devices at startup/when receiving new configuration. Any connected stations will immediately be considered "connected", triggering a corresponding MQTT message for each.

The full version is included as part of various packages. For example, commands to install the full version using wpad:

opkg remove wpad-basic-wolfssl
opkg install wpad-wolfssl

iOS

iOS version 14 introduced "private Wi-Fi addresses" to improve privacy. When enabled, an iOS client will connect to APs using different MAC addresses. Consider disabling this feature for APs that you control and are running wifi-presence to help make presence detection configuration easier.

Alternatives: OpenWrt Luci Integration

The OpenWrt Luci integration similar presence detection functionality.

Some differences between wifi-presence and the luci integration:

  • The luci integration communicates with the OpenWrt router using the OpenWrt admin HTTP(S) interface, while wifi-presence uses MQTT. The MQTT HomeAssistant integration is then what bridges the two. There's not necessarily an advantage to either approach, although if you don't have MQTT already integrated, then wifi-presence may not make as much sense.

  • Presence Detection:

    • The luci integration uses polling, i.e. it periodically queries the OpenWrt interface for the list of connected clients, while wifi-presence is event-driven. It connects with OpenWrt's hostapd (the system responsible for WiFi) and receives connect/disconnect events in real-time.
    • The luci integration effectively scrapes the ARP and DHCP tables on the OpenWrt router to get a list of connected clients. While wifi-presence is integrated directly with hostapd and has direct access to the connected clients.

wifi-presence's People

Contributors

awilliams avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

wifi-presence's Issues

MQTT entity name starts with the device name

A breaking change was introduced in Home Assistant 2023.8 involving naming of MQTT entities.

The following warning is logged by Home Assistant:

2023-08-03 10:52:24.376 WARNING (MainThread) [homeassistant.components.mqtt.mixins] MQTT entity name starts with the device name in your config {'availability_topic': 'wifi/access-point-4/status', 'device': {'connections': [['mac', '00:00:00:00:00:0']], 'name': 'Xiaomi Redmi K20', 'via_device': 'access-point-4', 'identifiers': []}, 'icon': 'mdi:wifi-marker', 'json_attributes_topic': 'wifi/station/access-point-4/00-00-00-00-00-00/attrs', 'name': 'Xiaomi Redmi K20 access-point-4', 'object_id': '000000000000_access-point-4', 'payload_available': 'online', 'payload_home': 'connected', 'payload_not_available': 'offline', 'payload_not_home': 'not_connected', 'qos': 2, 'source_type': 'router', 'state_topic': 'wifi/station/access-point-4/00-00-00-00-00-00/state', 'unique_id': 'wifipresence_000000000000_access-point-4', 'encoding': 'utf-8', 'enabled_by_default': True, 'payload_reset': 'None', 'availability_mode': 'latest'}, this is not expected. Please correct your configuration. The device name prefix will be stripped off the entity name and becomes 'Access-point-4'

Roaming between multiple APs

Thanks for this integration, it looks very interesting.

How does it behave with clients roaming between multiple APs on the same network?

Have you considered integrating it with DAWN? This would allow users to roam around the house from AP to AP without false disconnect notifications.

Support for MQTT over TLS (MQTTS)?

Have you considered adding support for MQTTS? It feels pretty pointless to me to have my routers sending usernames and passwords unencrytped to my MQTT broker.

Thanks for the awesome tool!

OpenWrt Boot racecondition

Hi Adam,

I've noticed that wifi-presence does not start cleanly on boot for me. I suspect the issue is that hostapd hasn't started yet, and there are no control sockets found by wifi-presence when it starts.

root@ap-office:~# logread | grep wifi-presence
Fri Apr 22 17:08:51 2022 daemon.err wifi-presence[2366]: Error: hostapd.socks cannot be blank

Restarting the process manually after boot is fine, so I'm not sure what the best way of resolving this is, whether it be through procd, or through wifi-presence itself.

Cheers,
Jon

SSID with mixed UTF-8 and ASCII characters cannot be handled correctly

Hi Adam,

Thanks for this great integration!

However, my WiFi SSID begins with UTF-8 character and then ASCII one, like ฯ‰=2ฯ€f.
In this case, status from hostapd_cli gives something below, which mixes \x and normal ASCII chars.

ssid[0]=\xcf\x89=2\xcf\x80f

case "ssid[0]":
if strings.HasPrefix(val, "\\x") {
val = strings.ReplaceAll(val, "\\x", "")
h, err := hex.DecodeString(val)
if err != nil {
return err
}
val = string(h)
}
s.SSID = val

The code above just replace \x and then decode hex, which still contains non-hex chars, resulting Error: encoding/hex: invalid byte on my device. I'm not familiar with golang though, but maybe we could use strconv.Unquote instead? (Ref)

Thanks

Needs to be started as network user/group in openwrt

Hi, thanks for the integration - it was included in openwrt head today.

In order for it to work I needed to alter the init script to start it as the network user - it can't access the control interface otherwise.

Error in wifi-presence: EventUnrecognized

It seems something is wrong. I can see the MAC of my devices, but the event is not recognized. I don't know if this is expected, but for some reason it does not publish nothing at my MQTT server, so maybe this is the culprit or maybe this is expected, I don't know:

Mon Apr 11 08:59:19 2022 daemon.notice hostapd: wlan5g: BEACON-REQ-TX-STATUS 58:cb:52:XX:XX:XX 119 ack=1
Mon Apr 11 08:59:19 2022 daemon.err wifi-presence[20175]: 2022/04/11 06:59:19 PIMINET: Event hostapd.EventUnrecognized: "<3>BEACON-REQ-TX-STATUS 58:cb:52:XX:XX:XX 119 ack=1"
Mon Apr 11 08:59:19 2022 daemon.err wifi-presence[20175]: 2022/04/11 06:59:19 PIMINET: event not handled hostapd.EventUnrecognized: "<3>BEACON-REQ-TX-STATUS 58:cb:52:XX:XX:XX 119 ack=1"
Mon Apr 11 08:59:20 2022 daemon.notice hostapd: wlan5g: BEACON-REQ-TX-STATUS f0:5c:77:XX:XX:XX 120 ack=1
Mon Apr 11 08:59:20 2022 daemon.err wifi-presence[20175]: 2022/04/11 06:59:20 PIMINET: Event hostapd.EventUnrecognized: "<3>BEACON-REQ-TX-STATUS f0:5c:77:XX:XX:XX 120 ack=1"
Mon Apr 11 08:59:20 2022 daemon.err wifi-presence[20175]: 2022/04/11 06:59:20 PIMINET: event not handled hostapd.EventUnrecognized: "<3>BEACON-REQ-TX-STATUS f0:5c:77:XX:XX:XX 120 ack=1"

Thanks!

Support for newer version of hostapd in OpenWrt master

Hi Adam,

I recently updated my APs to the latest OpenWrt master and have noted these new errors in the log:

root@openwrt-ap:~# logread | grep wifi-presence
Sat Nov 19 12:11:44 2022 daemon.err wifi-presence[4190]: Error: invalid MAC address "1a:xx:xx:xx:xx:xx auth_alg=open"
Sat Nov 19 12:31:51 2022 daemon.err wifi-presence[4282]: Error: invalid MAC address "34:xx:xx:xx:xx:xx auth_alg=open"
Sat Nov 19 13:07:59 2022 daemon.err wifi-presence[4390]: Error: invalid MAC address "34:xx:xx:xx:xx:xx auth_alg=open"
Sat Nov 19 13:10:03 2022 daemon.err wifi-presence[4573]: Error: invalid MAC address "60:xx:xx:xx:xx:xx auth_alg=ft"
Sat Nov 19 13:11:06 2022 daemon.err wifi-presence[4597]: Error: invalid MAC address "60:xx:xx:xx:xx:xx auth_alg=ft"
Sat Nov 19 13:20:16 2022 daemon.err wifi-presence[4608]: Error: invalid MAC address "60:xx:xx:xx:xx:xx auth_alg=ft"

Doing a bit of digging, I think the issue was introduced here: openwrt/openwrt@8cb9954

Could you take a look please? Happy to sponsor any package merge requests if needed.

Cheers,
Jon

Doesn't work after update to 22.03.0

Mon Sep 12 22:21:02 2022 daemon.err wifi-presence[3272]: Error: unable to connect to hostapd control socket "/var/run/hostapd/wlan0": ping error: read error from "PING" command: read unixgram /tmp/wp.wlan0->/var/run/hostapd/wlan0: i/o timeout

daemon.err wifi-presence[6693]: Error: unable to connect to hostapd control socket "/var/run/hostapd/wlan0": ping error: read error from "PING" command: read unixgram /tmp/wp.wlan0->/var/run/hostapd/wlan0: i/o timeout

Hello there

I get this error in the logs when I start wifi-presence:

daemon.err wifi-presence[6693]: Error: unable to connect to hostapd control socket "/var/run/hostapd/wlan0": ping error: read error from "PING" command: read unixgram /tmp/wp.wlan0->/var/run/hostapd/wlan0: i/o timeout

ls -lrt /var/run/hostapd/

srwxrwx--- 1 network network 0 Oct 23 17:45 global
srwxrwx--- 1 network network 0 Oct 23 17:45 wlan0
srwxrwx--- 1 network network 0 Oct 23 17:45 wlan1

JSON configuration persistency

It looks to me JSON configuration is not persistent.
According to my tests it is necessary to publish JSON configuration to the config topic (<mqtt.prefix>/config) every time OpenWrt is rebooted.
Am I missing something?

My configuration:
Model: TP-Link TL-WR2543N/ND
Architecture: Atheros AR7242 rev 1
Firmware Version: OpenWrt 19.07.8 r11364-ef56c85848 / LuCI openwrt-19.07 branch git-21.189.23240-7b931da
Kernel Version:4.14.241
wifi-presence (0.1.1-1)

Ability to publish status for all devices

@awilliams Thanks for your work, I like your project. I integrated it with my Openhab. Now I can detect myself at home or my wife.

Also, it would be helpful to be able to publish all devices' statuses to the MQTT. My use case is to track guests at my house

Conflict with wpad-basic-mbedtls

I'm seeing the following error when I try to build an image with this package installed:

   >  * check_data_file_clashes: Package wpad-basic-mbedtls wants to install file /build/openwrt-imagebuilder-23.05.2-mediatek-mt7622.Linux-x86_64/build_dir/target-aarch64_cortex-a53_musl/root-mediatek/usr/sbin/wpad
   >  But that file is already provided by package  * wpad-wolfssl
   >  * check_data_file_clashes: Package wpad-basic-mbedtls wants to install file /build/openwrt-imagebuilder-23.05.2-mediatek-mt7622.Linux-x86_64/build_dir/target-aarch64_cortex-a53_musl/root-mediatek/usr/share/hostap/hostapd.uc
   >         But that file is already provided by package  * wpad-wolfssl
   >  * check_data_file_clashes: Package wpad-basic-mbedtls wants to install file /build/openwrt-imagebuilder-23.05.2-mediatek-mt7622.Linux-x86_64/build_dir/target-aarch64_cortex-a53_musl/root-mediatek/usr/share/hostap/wpa_supplicant.uc
   >  But that file is already provided by package  * wpad-wolfssl
   >  * opkg_install_cmd: Cannot install package wpad-basic-mbedtls.

I've followed the instructions in the README to remove wpad-basic-wolfssl. I can't find an authoritative reference, but it sounds like perhaps openwrt has replaced wpad-basic-wolfssl with wpad-basic-mbedtls (refs: https://www.reddit.com/r/openwrt/comments/12fqixx/out_of_curiosity_whats_the_difference_between_the/)?

__nanosleep_time64: symbol not found

Very promising project!

I installed wifi-presence_0.3.0-1_arm_cortex-a9_vfpv3-d16.ipk on the latest 21.x version of OpenWRT:

DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='21.02.5'
DISTRIB_REVISION='r16688-fa9a932fdb'
DISTRIB_TARGET='mvebu/cortexa9'
DISTRIB_ARCH='arm_cortex-a9_vfpv3-d16'
DISTRIB_DESCRIPTION='OpenWrt 21.02.5 r16688-fa9a932fdb'
DISTRIB_TAINTS=''

When trying to start wifi-presence, I get these errors:

daemon.err wifi-presence: Error relocating /usr/bin/wifi-presence: __nanosleep_time64: symbol not found

Is wifi-presence supposed to work on 21.02?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.