diff --git a/net/hass/Makefile b/net/hass/Makefile new file mode 100644 index 00000000000000..eb7386704a56e0 --- /dev/null +++ b/net/hass/Makefile @@ -0,0 +1,59 @@ +# +# Copyright (c) 2018 Johannes Falke +# This is free software, licensed under the GNU General Public License v3. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=hass +PKG_VERSION:=0.1 +PKG_RELEASE:=1 +PKG_LICENSE:=GPL-3.0+ +PKG_MAINTAINER:=Johannes Falke + +include $(INCLUDE_DIR)/package.mk + +define Package/hass + SECTION:=net + CATEGORY:=Network + TITLE:=Wireless device tracker for Home Assistant + DEPENDS:=+hostapd-utils +curl + PKGARCH:=all +endef + +define Package/hass/description +Wireless device tracker for Home Assistant (home-assistant.io). Monitors wifi APs for devices via hooking into hostapd events. The info is then sent to the Home Assistant API via a simple POST. + +endef + +define Package/hass/conffiles +/etc/config/hass +endef + +define Build/Prepare +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/hass/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) ./files/hassd.sh $(1)/usr/bin/ + + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_DIR) $(1)/usr/lib/hass + $(INSTALL_BIN) ./files/functions.sh $(1)/usr/lib/hass/ + $(INSTALL_BIN) ./files/push_event.sh $(1)/usr/lib/hass/ + + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/hass.init $(1)/etc/init.d/hass + + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/hass.conf $(1)/etc/config/hass + +endef + +$(eval $(call BuildPackage,hass)) diff --git a/net/hass/files/functions.sh b/net/hass/files/functions.sh new file mode 100644 index 00000000000000..526985e1255cfc --- /dev/null +++ b/net/hass/files/functions.sh @@ -0,0 +1,109 @@ +function err_msg { + logger -t $0 -p error $@ + echo $1 1>&2 +} + +function register_hook { + logger -t $0 -p debug "register_hook $@" + if [ "$#" -ne 1 ]; then + err_msg "register_hook missing interface" + exit 1 + fi + interface=$1 + + hostapd_cli -i$interface -a/usr/lib/hass/push_event.sh & +} + +function post { + logger -t $0 -p debug "post $@" + if [ "$#" -ne 1 ]; then + err_msg "POST missing payload" + exit 1 + fi + payload=$1 + + config_get hass_host global host + config_get hass_pw global pw + + resp=$(curl "$hass_host/api/services/device_tracker/see" -sfSX POST \ + -H 'Content-Type: application/json' \ + -H "X-HA-Access: $hass_pw" \ + --data-binary "$payload" 2>&1) + + if [ $? -eq 0 ]; then + level=debug + else + level=error + fi + + logger -t $0 -p $level "post response $resp" +} + +function build_payload { + logger -t $0 -p debug "build_payload $@" + if [ "$#" -ne 3 ]; then + err_msg "Invalid payload parameters" + logger -t $0 -p warning "push_event not handled" + exit 1 + fi + mac=$1 + host=$2 + consider_home=$3 + + echo "{\"mac\":\"$mac\",\"host_name\":\"$host\",\"consider_home\":\"$consider_home\",\"source_type\":\"router\"}" +} + +function get_ip { + # get ip for mac + grep "0x2\s\+$1" /proc/net/arp | cut -f 1 -s -d" " +} + +function get_host_name { + # get hostname for mac + nslookup "$(get_ip $1)" | grep -oP "(?<=name = ).*$" +} + +function push_event { + logger -t $0 -p debug "push_event $@" + if [ "$#" -ne 3 ]; then + err_msg "Illegal number of push_event parameters" + exit 1 + fi + iface=$1 + msg=$2 + mac=$3 + + config_get hass_timeout_conn global timeout_conn + config_get hass_timeout_disc global timeout_disc + + case $msg in + "AP-STA-CONNECTED") + timeout=$hass_timeout_conn + ;; + "AP-STA-POLL-OK") + timeout=$hass_timeout_conn + ;; + "AP-STA-DISCONNECTED") + timeout=$hass_timeout_disc + ;; + *) + logger -t $0 -p warning "push_event not handled" + return + ;; + esac + + post $(build_payload "$mac" "$(get_host_name $mac)" "$timeout") +} + +function sync_state { + logger -t $0 -p debug "sync_state $@" + + config_get hass_timeout_conn global timeout_conn + + for interface in `iw dev | grep Interface | cut -f 2 -s -d" "`; do + maclist=`iw dev $interface station dump | grep Station | cut -f 2 -s -d" "` + for mac in $maclist; do + post $(build_payload "$mac" "$(get_host_name $mac)" "$hass_timeout_conn") & + done + done +} diff --git a/net/hass/files/hass.conf b/net/hass/files/hass.conf new file mode 100644 index 00000000000000..ed6ab9febd1d41 --- /dev/null +++ b/net/hass/files/hass.conf @@ -0,0 +1,5 @@ +config hass 'global' + option host 'ip.or.name.example:8123' + option pw '' + option timeout_conn '24:00' + option timeout_disc '00:03' diff --git a/net/hass/files/hass.init b/net/hass/files/hass.init new file mode 100755 index 00000000000000..86e8388305181d --- /dev/null +++ b/net/hass/files/hass.init @@ -0,0 +1,34 @@ +#!/bin/sh /etc/rc.common + +START=50 +USE_PROCD=1 +PROG=/usr/bin/hassd.sh +PIDFILE=/var/run/hass.pid + +start_service() { + procd_open_instance hass + procd_set_param command $PROG + procd_set_param pidfile $PIDFILE + procd_close_instance +} + +all_child_pids() { + pid=$1 + echo $pid + + for child_pid in $(pgrep -P $pid); do + echo "$(all_child_pids $child_pid)" + done +} + +stop_service() { + for pid in $(all_child_pids $(cat $PIDFILE)); do + kill $pid + done +} + +reload_service() +{ + stop + start +} diff --git a/net/hass/files/hassd.sh b/net/hass/files/hassd.sh new file mode 100755 index 00000000000000..eba4f6a66bf626 --- /dev/null +++ b/net/hass/files/hassd.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# todo: hostapd white (black) list so we don't have to listen to all APs + +source /lib/functions.sh +config_load hass + +logger -t $0 -p info "Starting up" + +source /usr/lib/hass/functions.sh + +logger -t $0 -p info "Hooking onto existing interfaces" +for interface in `iw dev | grep Interface | cut -f 2 -s -d" "` +do + register_hook $interface +done + +sync_state + +# will run in subshell +ubus listen ubus.object.add | \ +while read line ; do + interface=$(echo "$line" | grep -oP '"path":"hostapd\.\K[^"]*(?="\} \}$)') + if [ $? = 0 ] + then + logger -t $0 -p info "$interface is up, setting up hook" + register_hook $interface + fi + +done diff --git a/net/hass/files/push_event.sh b/net/hass/files/push_event.sh new file mode 100755 index 00000000000000..6e197370b13ba3 --- /dev/null +++ b/net/hass/files/push_event.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +source /lib/functions.sh +config_load hass + +source /usr/lib/hass/functions.sh +push_event $@