Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions net/hass/Makefile
Original file line number Diff line number Diff line change
@@ -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 <johannesfalke@gmail.com>

include $(INCLUDE_DIR)/package.mk

define Package/hass
SECTION:=net
CATEGORY:=Network
TITLE:=Wireless device tracker for Home Assistant

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I missed this earlier. If this package is just the wireless device tracker, and not Home Assistant itself, then Shouldn't the package name be something like "hass-device-tracker" or something? not just "hass" itself?

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))
109 changes: 109 additions & 0 deletions net/hass/files/functions.sh
Original file line number Diff line number Diff line change
@@ -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
}
5 changes: 5 additions & 0 deletions net/hass/files/hass.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
config hass 'global'
option host 'ip.or.name.example:8123'
option pw ''

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that's password, please just use password, instead of making people guess.

option timeout_conn '24:00'
option timeout_disc '00:03'
34 changes: 34 additions & 0 deletions net/hass/files/hass.init
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what did you do that procd isn't handling this for you? consider fixing your scripts that you don't need this sort of procedure to die politely.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I'm spawning processes, procd does not correctly kill all child processes, that's why I wrote this.

}

reload_service()
{
stop
start

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

99% sure you don't need this, as you're doing the default implentation.

}
29 changes: 29 additions & 0 deletions net/hass/files/hassd.sh
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you've "" quoted above, but not here. I don't care which you use, but consistency is nice.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right, just copied the upper part everywhere.


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
7 changes: 7 additions & 0 deletions net/hass/files/push_event.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh

source /lib/functions.sh
config_load hass

source /usr/lib/hass/functions.sh
push_event $@