Giter Site home page Giter Site logo

catfriend1 / openwrt-presence Goto Github PK

View Code? Open in Web Editor NEW
35.0 35.0 9.0 81 KB

OpenWrt device presence detection bash script. Works accross multiple APs. Listens to events from OpenWrt logread via syslog-ng on a master AP "passively". Can resync "actively" by executing "wrtwifistareport" on slave APs every 5 minutes in case of missed events. Outputs "device A=[present/away]" events to a /tmp/ file and FIFOs. The information can be consumed by home automation or logger software. Presence/Away state is detected representative to the whole extent of a SSID and not limited to a single AP.

License: Mozilla Public License 2.0

Shell 82.64% Batchfile 17.36%

openwrt-presence's People

Contributors

catfriend1 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

openwrt-presence's Issues

Help for mqtt

Hello, I'm back on your project, as the others don't suit me 100%.
I only need to add a command each time associations.dto is modified. For me, this is the best way to ensure regular updates. I use MQTT so I've added mosquitto_pub commands. 1 to send the association.dto and the other to make a match on dhcp leases to get the ip and name.

I've made a modification but I'd like your opinion on whether it's in the right place.

#/bin/bash
trap "" SIGHUP
#
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
#
# set +m
#
# Filename:			wrtpresence_main.sh
# Usage:			Instanced by service wrapper.
# Purpose:			Tracks STA client associations across a WiFi backed by multiple APs.
# 
# Installation:
# 
# 	Generic advice:
# 		LUCI / System / System / Hostname
# 			Set hostname to "WifiAP-[01|02|03|..]" or adjust the variable to match your hostname convention.
# 				LOGREAD_SOURCE_PREFIX="WifiAP-.."
# 
# 	=========================
# 	== MASTER ACCESS POINT ==
# 	=========================
# 		... where this script is executed.
# 	opkg update
# 	opkg install bash
# 	opkg remove logd
# 	opkg install syslog-ng
# 	chmod +x "/root/wrtpresence"
# 	chmod +x "/root/wrtpresence_main.sh"
# 
# 	LUCI / System / Startup / Local Startup
# 		# sh /root/wrtpresence start
# 
# 	LUCI / System / System / Logging
# 		External system log server
# 			127.0.0.1
# 		Cron Log Level
# 			Warning
# 
# 	chmod +x "/root/wrtwifistareport.sh"
# 	LUCI: System / Scheduled Tasks / Add new row
# 		*/5 * * * * /bin/sh "/root/wrtwifistareport.sh" >/dev/null 2>&1
# 	Restart Cron:
# 		/etc/init.d/cron restart
#
# 	chmod +x "/root/wrtbtdevreport.sh"
# 	LUCI: System / Scheduled Tasks / Add new row
# 		*/1 * * * * /bin/sh "/root/wrtbtdevreport.sh" >/dev/null 2>&1
# 	Restart Cron:
# 		/etc/init.d/cron restart
# 
# 	========================
# 	== SLAVE ACCESS POINT ==
# 	========================
# 		... where WiFi STA clients may roam to/from.
# 	LUCI / System / System / Logging
# 		External system log server
# 			[IP_ADDRESS_OF_MASTER_ACCESS_POINT]
# 		Cron Log Level
# 			Warning
# 
# 	chmod +x "/root/wrtwifistareport.sh"
# 	LUCI: System / Scheduled Tasks / Add new row
# 		*/5 * * * * /bin/sh "/root/wrtwifistareport.sh" >/dev/null 2>&1
# 	Restart Cron:
# 		/etc/init.d/cron restart	
#
# Diagnostics:
#	Cleanup, Reset:
#		sh /root/wrtpresence clean
#	Logging and Monitoring:
#		sh /root/wrtpresence debug	
#		sh /root/wrtpresence showlog
#		sh /root/wrtpresence livelog
# 		sh /root/wrtpresence livestate
# 		sh /root/wrtpresence showstate
#
# Prerequisites:
# 	Configuration
#		LUCI / System /System / Logging
# 			External system log server
# 				127.0.0.1
# 		/etc/config/system
# 			option log_ip '127.0.0.1'
#	Files 
# 		wrtpresence								main service wrapper
# 		wrtpresence_main.sh						main service program
# 		wrtwifistareport.sh						cron job script for sync in case events got lost during reboot
# 		wrtbtdevreport.sh						cron job script for bluetooth scans
# 	Packages
# 		bash									required for arrays
# 		syslog-ng								required for log collection from other APs
# 
#
# FIFO output line examples:
# 	"G/CONSOLIDATED_PRESENCE_STATE=away"
# 	"G/CONSOLIDATED_PRESENCE_STATE=present"
# 	"DEV/[DEVICE_NAME]=away"
# 	"DEV/[DEVICE_NAME]=present"
# 	
#
# For testing purposes only:
# 	killall logread; killall tail; sh wrtpresence stop; bash wrtpresence_main.sh debug
# 	kill -INT "$(cat "/tmp/wrtpresence_main.sh.pid")"
# 
#
# ====================
# Device Configuration
# ====================
#
# 0: 
DEVICE_NAME[0]="Test-Mobile-1"
DEVICE_MAC[0]="aa:bb:cc:dd:ee:ff"
#
# 1: 
DEVICE_NAME[1]="Test-Mobile-2"
DEVICE_MAC[1]="aa:bb:cc:dd:ff:ee"
#
#
# ====================
# Mqtt Configuration
# ====================
MQTT_HOST="SERVER MQTT"
MQTT_PORT="1883"
MQTT_USER="LOGIN"
MQTT_PASSWORD="PASSWORD"
MQTT_TOPIC_ASSOCIATONS="homeassistant/openwrt/device_tracker/associations/"
MQTT_TOPIC_DHCP="homeassistant/openwrt/device_tracker/dhcp_leases/"
DHCP_LEASES="/tmp/dhcp.leases"
#
#
# ====================
# Script Configuration
# ====================
PATH=/usr/bin:/usr/sbin:/sbin:/bin
# CURRENT_SCRIPT_PATH="$(cd "$(dirname "$0")"; pwd)"
EVENT_FIFO=/tmp/"$(basename "$0")".event_fifo
PID_FILE=/tmp/"$(basename "$0")".pid
LOGFILE="/tmp/wrtpresence.log"
LOG_MAX_LINES="1000"
DEBUG_MODE="0"
REPORT_CONSOLIDATED_PRESENCE_TO_FIFO="0"
#
# External script FIFOs.
GASERVICE_FIFO="/tmp/wrtgaservice_main.sh.event_fifo"
#
# Internal script DTOs.
ASSOCIATIONS_DTO="/tmp/associations.dto"
#
# External script DTOs.
PRESENT_DEVICES_DTO="/tmp/present_devices.dto"
#
# Timing Configuration.
IDXBUMP_SLEEP_SECONDS="60"
DEVICE_DISCONNECTED_IDX="10"
#
# Optional: Enable wrtwifistareport wifi reports by the local device
CONFIG_WIFI_STA_REPORTS_ENABLED="1"
#
# Optional: Enable wrtbtdevreport bluetooth reports by the local device
CONFIG_BLUETOOTH_REPORTS_ENABLED="0"
#
# -----------------------
# --- Function Import ---
# -----------------------
# 
# -----------------------------------------------------
# -------------- START OF FUNCTION BLOCK --------------
# -----------------------------------------------------
logAdd ()
{
	TMP_DATETIME="$(date '+%Y-%m-%d [%H-%M-%S]')"
	TMP_LOGSTREAM="$(tail -n ${LOG_MAX_LINES} ${LOGFILE} 2>/dev/null)"
	echo "${TMP_LOGSTREAM}" > "$LOGFILE"
	if [ "${1}" = "-q" ]; then
		# Quiet mode.
		echo "${TMP_DATETIME} ${@:2}" >> "${LOGFILE}"
	else
		# Loud mode.
		echo "${TMP_DATETIME} $*" | tee -a "${LOGFILE}"
	fi
	return
}

fifoOut ()
{
	if [ "${DEBUG_MODE}" = "1" ]; then
		logAdd -q "[DEBUG] Skipping FIFO-OUT [$1]"
		return 0
	fi
	#
	fifoOut_do "${GASERVICE_FIFO}" "$1"
}

fifoOut_do ()
{
	#
	# Usage:		fifoOut_do [FIFO_FULLFN] [TEXT_CONTENT_WITHOUT_SPACES]
	#
	if [ ! -e "$1" ]; then
		logAdd -q "[ERROR] FIFO does not exist for FIFO-OUT [$2] to $1"
		return
	fi
	logAdd -q "[INFO] FIFO-OUT [$2] to $1"
	( echo "$2" >> "$1" ) &
}

plCheckDevicesByCounters() {
	#
	# Usage:			plCheckDevicesByCounters
	# Example:			plCheckDevicesByCounters
	# Called by:		plAddClient, disconnectIdxBumper
	# Returns:			< nothing >
	#
	# Global Variables.
	# 	[IN] ASSOCIATIONS_DTO
	#
	# Variables.
	DPTE_DEVICE_COUNT="${#DEVICE_MAC[*]}"
	#
	# For testing purposes only.
	# logAdd -q "plCheckDevicesByCounters: triggered"
	# 
	touch "${PRESENT_DEVICES_DTO}"
	touch "${PRESENT_DEVICES_DTO}.new"
	# 
	# Loop through configured DEVICE ARRAY.
	DPTE_DEVICE_I="0"
	while (true); do
		#
		# Are we done yet?
		#
		if [ ${DPTE_DEVICE_I} -eq ${DPTE_DEVICE_COUNT} ]; then
			break;
		fi
		#
		if (cat "${ASSOCIATIONS_DTO}" 2> /dev/null | grep -Fi "=${DEVICE_MAC[${DPTE_DEVICE_I}]}=" | grep -vq "=${DEVICE_DISCONNECTED_IDX}$"); then
			# 
			# Device PRESENT.
			echo "${DEVICE_NAME[${DPTE_DEVICE_I}]}" >> "${PRESENT_DEVICES_DTO}.new"
			# 
			# Did the device state change from AWAY to PRESENT?
			if ( ! grep -iq "${DEVICE_NAME[${DPTE_DEVICE_I}]}$" "${PRESENT_DEVICES_DTO}" ); then
				#
				# DEVICE_ACTION_PRESENT.
				#
				fifoOut "DEV/${DEVICE_NAME[${DPTE_DEVICE_I}]}=present"
				#
			fi
		else
			# 
			# Device AWAY.
			# 
			# Did the device state change from PRESENT to AWAY?
			if ( grep -iq "${DEVICE_NAME[${DPTE_DEVICE_I}]}$" "${PRESENT_DEVICES_DTO}" ); then
				#
				# DEVICE_ACTION_AWAY.
				# 
				fifoOut "DEV/${DEVICE_NAME[${DPTE_DEVICE_I}]}=away"
				#
			fi
		fi
		#
		# Continue with next device.
		DPTE_DEVICE_I="$((DPTE_DEVICE_I+1))"
		#
	done
	# 
	if [ "${REPORT_CONSOLIDATED_PRESENCE_TO_FIFO}" == "1" ]; then
		# Did the consolidated presence state change?
		if [ -s "${PRESENT_DEVICES_DTO}.new" ]; then
			# New state: CONSOLIDATED_DEVICE_STATE=PRESENT
			if [ ! -s "${PRESENT_DEVICES_DTO}" ]; then
				# Previous state: CONSOLIDATED_DEVICE_STATE=AWAY
				logAdd -q "[INFO] CONSOLIDATED_PRESENCE_STATE=present"
				fifoOut "G/CONSOLIDATED_PRESENCE_STATE=present"
			fi
		else
			# New state: CONSOLIDATED_DEVICE_STATE=AWAY
			if [ -s "${PRESENT_DEVICES_DTO}" ]; then
				# Previous state: CONSOLIDATED_DEVICE_STATE=PRESENT
				logAdd -q "[INFO] CONSOLIDATED_PRESENCE_STATE=away"
				fifoOut "G/CONSOLIDATED_PRESENCE_STATE=away"
			fi
		fi
	fi
	# 
	# Overwrite last state with new state.
	mv "${PRESENT_DEVICES_DTO}.new" "${PRESENT_DEVICES_DTO}"
	#
	return
}

plAddClient() {
	#
	# Usage:			plAddClient [STATION_NAME] [CLIENT_MAC_ADDR] [TEXT_REASON]
	# Example:			plAddClient "WifiAP-01_wlan0-4" "aa:bb:cc:dd:ee:ff" "AP-STA-CONNECTED"
	# Called by:		logreader
	# Returns:			< nothing >
	#
	# Global Variables.
	# 	[IN] ASSOCIATIONS_DTO
	#
	# Variables.
	TMP_PLAC_STATION_NAME="${1}"
	TMP_PLAC_MAC_ADDR="${2}"
	TMP_PLAC_TEXT_REASON="${3}"
	# 
	if [ ! -f "${ASSOCIATIONS_DTO}" ]; then
		touch "${ASSOCIATIONS_DTO}"
	fi
	# 
	if ( ! grep -F -q -i "${TMP_PLAC_STATION_NAME}=${TMP_PLAC_MAC_ADDR}=" "${ASSOCIATIONS_DTO}" ); then
		logAdd -q "plAddClient: ${TMP_PLAC_STATION_NAME} +${TMP_PLAC_MAC_ADDR} reason: ${TMP_PLAC_TEXT_REASON}"
		# "=0" means connected.
		echo "${TMP_PLAC_STATION_NAME}=${TMP_PLAC_MAC_ADDR}=0" >> "${ASSOCIATIONS_DTO}"
		cat "${ASSOCIATIONS_DTO}" 2>/dev/null | sort > "${ASSOCIATIONS_DTO}.tmp"
		mv "${ASSOCIATIONS_DTO}.tmp" "${ASSOCIATIONS_DTO}"
		mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_ASSOCIATONS" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$ASSOCIATIONS_DTO"
		sleep 2
		mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_DHCP" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$DHCP_LEASES"
		
	else
		logAdd -q "plAddClient: ${TMP_PLAC_STATION_NAME} ${TMP_PLAC_MAC_ADDR}=0 reason: ${TMP_PLAC_TEXT_REASON}"
		sed -i "s/^${TMP_PLAC_STATION_NAME}\=${TMP_PLAC_MAC_ADDR}\=.*$/${TMP_PLAC_STATION_NAME}\=${TMP_PLAC_MAC_ADDR}\=0/gI" "${ASSOCIATIONS_DTO}"
		mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_ASSOCIATONS" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$ASSOCIATIONS_DTO"
		sleep 2
		mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_DHCP" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$DHCP_LEASES"
	fi
	# 
	plCheckDevicesByCounters
	#
	return
}

plMarkClientAsDisconnected() {
	#
	# Usage:			plMarkClientAsDisconnected [STATION_NAME] [CLIENT_MAC_ADDR] [TEXT_REASON]
	# Example:			plMarkClientAsDisconnected "WifiAP-01" "aa:bb:cc:dd:ee:ff" "AP-STA-DISCONNECTED"
	# Called by:		logreader
	# Returns:			< nothing >
	#
	# Global Variables.
	# 	[IN] ASSOCIATIONS_DTO
	#
	# Variables.
	TMP_PLAC_STATION_NAME="${1}"
	TMP_PLAC_MAC_ADDR="${2}"
	TMP_PLAC_TEXT_REASON="${3}"
	# 
	if [ ! -f "${ASSOCIATIONS_DTO}" ]; then
		touch "${ASSOCIATIONS_DTO}"
	fi
	# 
	if ( grep -F -q -i "${TMP_PLAC_STATION_NAME}=${TMP_PLAC_MAC_ADDR}=" "${ASSOCIATIONS_DTO}" ); then
		logAdd -q "plMarkClientAsDisconnected: ${TMP_PLAC_STATION_NAME} -${TMP_PLAC_MAC_ADDR} reason: ${TMP_PLAC_TEXT_REASON}"
		# ">0" means disconnected.
		sed -i "s/${TMP_PLAC_STATION_NAME}\=${TMP_PLAC_MAC_ADDR}\=0/${TMP_PLAC_STATION_NAME}\=${TMP_PLAC_MAC_ADDR}\=1/gI" "${ASSOCIATIONS_DTO}"
		mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_ASSOCIATONS" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$ASSOCIATIONS_DTO"
		sleep 2
		mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_DHCP" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$DHCP_LEASES"
	else
		logAdd -q "plMarkClientAsDisconnected: ${TMP_PLAC_STATION_NAME} -${TMP_PLAC_MAC_ADDR} reason: ${TMP_PLAC_TEXT_REASON} - Skipping, client not present in DTO."
	fi
	#
	return
}

plRemoveClient() {
	#
	# Usage:			plRemoveClient [STATION_NAME] [CLIENT_MAC_ADDR]
	# Example:			plRemoveClient "WifiAP-01" "aa:bb:cc:dd:ee:ff"
	# Called by:		-
	# Returns:			< nothing >
	#
	# Global Variables.
	# 	[IN] ASSOCIATIONS_DTO
	#
	# Variables.
	TMP_PLAC_STATION_NAME="${1}"
	TMP_PLAC_MAC_ADDR="${2}"
	# 
	if [ ! -f "${ASSOCIATIONS_DTO}" ]; then
		touch "${ASSOCIATIONS_DTO}"
	fi
	# 
	if ( grep -F -q -i "${TMP_PLAC_STATION_NAME}=${TMP_PLAC_MAC_ADDR}=" "${ASSOCIATIONS_DTO}" ); then
		logAdd -q "plRemoveClient: ${TMP_PLAC_STATION_NAME} -${TMP_PLAC_MAC_ADDR}"
		sed -i "/^${TMP_PLAC_STATION_NAME}\=${TMP_PLAC_MAC_ADDR}\=.*$/d" "${ASSOCIATIONS_DTO}"
		mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_ASSOCIATONS" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$ASSOCIATIONS_DTO"
		sleep 2
		mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_DHCP" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$DHCP_LEASES"
	else
		logAdd -q "plRemoveClient: ${TMP_PLAC_STATION_NAME} -${TMP_PLAC_MAC_ADDR} - Skipping, client not present in DTO."
	fi
	#
	return
}

logreader() {
	#
	# Called by:	MAIN
	#
	# Global Variables.
	# 	[IN] ASSOCIATIONS_DTO
	# 
	logAdd -q "[INFO] BEGIN logreader"
	#
	LOGREAD_BIN="$(which logread)"
	if ( opkg list "syslog-ng" | grep -q "syslog-ng" ); then
		# logread is provided by package "syslog-ng"
		LOGREAD_SOURCE_PREFIX="WifiAP-.."
	else
		# logread is provided by default package "logd"
		LOGREAD_SOURCE_PREFIX="daemon\.notice"
	fi
	#
	if [ ! -f "/var/log/messages" ]; then
		logAdd -q "[INFO] logreader: Waiting for /var/log/messages"
	fi
	while [ ! -f "/var/log/messages" ]; do
		sleep 2
	done
	logAdd -q "[INFO] BEGIN logreader_loop"
	${LOGREAD_BIN} -f | while read line; do
		if $(echo -n "${line}" | grep -q "${LOGREAD_SOURCE_PREFIX}.*hostapd.*\(AP-STA-CONNECTED\|AP-STA-DISCONNECTED\)"); then
			if $(echo -n "${line}" | grep -q "AP-STA-CONNECTED"); then
				STATION_NAME="$(echo -n "${line}" | grep -o "${LOGREAD_SOURCE_PREFIX}")"
				WIFI_IF_NAME="$(echo -n "${line}" | grep -o -E "(wlan|phy)[[:xdigit:]]{1}(-(ap)?[[:xdigit:]]{1,})?")"
				MAC_ADDR="$(echo -n "${line}" | grep -o -E "([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}")"
				# fifoOut_do "${EVENT_FIFO}" "CONNECT: ${MAC_ADDR} on ${STATION_NAME}"
				plAddClient "${STATION_NAME}_${WIFI_IF_NAME}" "${MAC_ADDR}" "AP-STA-CONNECTED"
			elif $(echo -n "${line}" | grep -q "AP-STA-DISCONNECTED"); then
				STATION_NAME="$(echo -n "${line}" | grep -o "${LOGREAD_SOURCE_PREFIX}")"
				WIFI_IF_NAME="$(echo -n "${line}" | grep -o -E "(wlan|phy)[[:xdigit:]]{1}(-(ap)?[[:xdigit:]]{1,})?")"
				MAC_ADDR="$(echo -n "${line}" | grep -o -E "([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}")"
				# fifoOut_do "${EVENT_FIFO}" "DISCONNECT: ${MAC_ADDR} on ${STATION_NAME}"
				plMarkClientAsDisconnected "${STATION_NAME}_${WIFI_IF_NAME}" "${MAC_ADDR}" "AP-STA-DISCONNECTED"
			fi
		elif $(echo -n "${line}" | grep -q "${LOGREAD_SOURCE_PREFIX}.*\(wrtbtdevreport\|wrtwifistareport\): ;.*"); then
			STATION_NAME="$(echo -n "${line}" | grep -o "${LOGREAD_SOURCE_PREFIX}" | head -n 1)"
			WIFI_STA_LIST="$(echo -n "${line}" | cut -d ";" -f 2)"
			#
			# Sanity check to filter wrongly formatted log messages.
			if [ ! ";${WIFI_STA_LIST}" = "$(echo ";${WIFI_STA_LIST}" | grep -o -E ";(([a-zA-z0-9,:-]*\|){1,})?")" ]; then
				logAdd -q "logreader: Ignored invalid report from device [${STATION_NAME}]."
				continue
			fi
			#
			REPORT_TYPE="wrtwifistareport"
			if $(echo -n "${line}" | grep -q "${LOGREAD_SOURCE_PREFIX}.*wrtbtdevreport: ;.*"); then
				REPORT_TYPE="wrtbtdevreport"
			fi
			#
			# For testing purposes only.
			## logAdd -q "logreader: Got raw line [${line}]"
			## logAdd -q "logreader: Got ${REPORT_TYPE}: [${WIFI_STA_LIST}] on [${STATION_NAME}]"
			#
			# We store the current unix timestamp for this access point as the time when we received its
			# last report. If the AP "dies", we can detect it later and remove its STA clients from
			# ASSOCIATIONS_DTO.
			echo "$(date +%s)" > "/tmp/${STATION_NAME}.last_wrtwifistareport"
			# 
			# We need to update our associations cache according to the full report of active STA clients
			# for the given STATION_NAME.
			if [ ! -f "${ASSOCIATIONS_DTO}" ]; then
				touch "${ASSOCIATIONS_DTO}"
			fi
			# 
			# Step 1: Check for associations we did not know they were active.
			echo -e "$(echo "${WIFI_STA_LIST}" | sed -r -e "s/\|/\\\n/g")" | while read wifiif_macaddr; do
				if [ ! -z "${wifiif_macaddr}" ]; then
					WIFI_IF_NAME="$(echo "${wifiif_macaddr}" | cut -d "," -f 1)"
					MAC_ADDR="$(echo "${wifiif_macaddr}" | cut -d "," -f 2)"
					#
					# For testing purposes only.
					## logAdd -q "logreader: ${REPORT_TYPE}[1]: ... macaddr=[${MAC_ADDR}] on wifiif=[${WIFI_IF_NAME}]"
					## logger -t "wrtbtdevreport" ";hci0,aa:bb:cc:dd:ee:ff|"
					#
					# MAC address already in DTO?
					if ( ! grep -q -i "^${STATION_NAME}_${WIFI_IF_NAME}=${MAC_ADDR}=0$" "${ASSOCIATIONS_DTO}" ); then
						# logAdd -q "logreader: ${REPORT_TYPE}: MAC [${MAC_ADDR}] missing - Adding."
						plAddClient "${STATION_NAME}_${WIFI_IF_NAME}" "${MAC_ADDR}" "${REPORT_TYPE}"
						mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_ASSOCIATONS" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$ASSOCIATIONS_DTO"
						sleep 2
					    mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_DHCP" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$DHCP_LEASES"
					fi
				fi
			done
			#
			# Step 2: Check for associations we still know but they are outdated because of missed
			# disconnect events.
			cat "${ASSOCIATIONS_DTO}" 2>/dev/null | grep "^${STATION_NAME}_" | grep "=0$" | while read line; do
				if [ ! -z "${line}" ]; then
					WIFI_IF_NAME="$(echo "${line}" | cut -d "=" -f 1 | cut -d "_" -f 2)"
					MAC_ADDR="$(echo "${line}" | cut -d "=" -f 2)"
					#
					# For testing purposes only.
					# logAdd -q "logreader: ${REPORT_TYPE}[2]: ... macaddr=[${MAC_ADDR}] on wifiif=[${WIFI_IF_NAME}]"
					#
					# For testing purposes only.
					# 	logger -t "wrtbtdevreport" ";"
					# 	logger -t "wrtwifistareport" ";"
					#
					if [ "${REPORT_TYPE}" = "wrtwifistareport" ]; then
						if ( echo "${WIFI_IF_NAME}" | grep -q "hci.*" ); then
							# Skip bluetooth devices as they are not contained in wrtwifistareport messages.
							continue
						fi
					else
						if ( echo "${WIFI_IF_NAME}" | grep -q  "wlan\|phy.*" ); then
							# Skip WiFi devices as they are not contained in wrtbtdevreport messages.
							continue
						fi
					fi
					# 
					if ( ! echo -e "$(echo "${WIFI_STA_LIST}" | sed -r -e "s/\|/\\\n/g")" | grep -i -q "^${WIFI_IF_NAME},${MAC_ADDR}$" ); then
						# logAdd -q "logreader: ${REPORT_TYPE}: MAC [${MAC_ADDR}] no longer associated - Marking as disconnected."
						plMarkClientAsDisconnected "${STATION_NAME}_${WIFI_IF_NAME}" "${MAC_ADDR}" "${REPORT_TYPE}"
					fi
					# 
				fi
			done
			#
		elif $(echo -n "${line}" | grep -q "${LOGREAD_SOURCE_PREFIX}.*procd: - init complete -"); then
			STATION_NAME="$(echo -n "${line}" | grep -o "${LOGREAD_SOURCE_PREFIX}" | head -n 1)"
			logAdd -q "logreader: Detected reboot of device [${STATION_NAME}]."
			# STN_DISABLE_NOTIFICATION="1"
			# sendTelegramNotification "${HOSTNAME}: Detected reboot of device [${STATION_NAME}]." ""
		fi
	done
}

disconnectIdxBumper() {
	#
	# Called by:	MAIN
	#
	# Global Variables.
	# 	[IN] ASSOCIATIONS_DTO
	# 
	# Variables.
	LOOP_CNT=0
	logAdd -q "[INFO] BEGIN disconnectIdxBumper_loop"
	while true
	do
		LOOP_CNT="$((LOOP_CNT+1))"
		# echo "${LOOP_CNT}" >> "${EVENT_FIFO}"
		# logAdd -q "disconnectIdxBumper: LOOP_CNT=[${LOOP_CNT}]"
		#
		# TASK 1
		# Increment counter value for every disconnected STA MAC.
		# Only grab rows from disconnected devices.
		cat "${ASSOCIATIONS_DTO}" 2>/dev/null | grep -v "=0$" | while read line; do
			# 
			# Get current counter state.
			TMP_PREFIX="$(echo -n "${line}" | cut -d "=" -f 1,2)"
			TMP_DIB_COUNTER="$(echo -n "${line}" | cut -d "=" -f 3)"
			TMP_LAST_DIB_COUNTER="${TMP_DIB_COUNTER}"
			# 
			# Bump the disconnect counter.
			if [ ${TMP_DIB_COUNTER} -lt ${DEVICE_DISCONNECTED_IDX} ]; then
				TMP_DIB_COUNTER="$((TMP_DIB_COUNTER+1))"
				sed -i "s/${TMP_PREFIX}\=.*$/${TMP_PREFIX}\=${TMP_DIB_COUNTER}/g" "${ASSOCIATIONS_DTO}"
				mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_ASSOCIATONS" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$ASSOCIATIONS_DTO"
				sleep 2
				mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_DHCP" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$DHCP_LEASES"
			fi
			# 
			# If a counter is at max, the disconnected device is considered away from all APs.
			if [ ${TMP_DIB_COUNTER} -ne ${TMP_LAST_DIB_COUNTER} ]; then
				if [ ${TMP_DIB_COUNTER} -eq ${DEVICE_DISCONNECTED_IDX} ]; then
					plCheckDevicesByCounters
				fi
			fi
			#
		done
		#
		# TASK 2
		# Detect "dead" access points.
		# We will look up the station that reported a connected STA MAC.
		# Each station is expected to send us "wrtwifistareport" events every 5 minutes.
		# If the "last_wrtwifistareport" unix timestamp recorded for the access point is too old,
		# we will assume it "dead" and remove STA MAC addresses from ASSOCIATIONS_DTO it has reported before.
		# 
		# Get a all access points that have reported a connected STA MAC.
		cat "${ASSOCIATIONS_DTO}" 2>/dev/null | grep "=0$" | cut -d "_" -f 1 | uniq | while read line; do
			# Example: line="WifiAP-04"
			#
			# Did the station already submit a "wrtwifistareport"?
			if [ -f "/tmp/${line}.last_wrtwifistareport" ]; then
				LAST_REPORT_UNIX_TIME="$(cat "/tmp/${line}.last_wrtwifistareport" 2>/dev/null | head -n 1)"
				CURRENT_UNIX_TIME="$(date +%s)"
				MINUTES_SINCE_LAST_REPORT="$(((CURRENT_UNIX_TIME-LAST_REPORT_UNIX_TIME)/60))"
				if [ ${MINUTES_SINCE_LAST_REPORT} -ge 20 ]; then
					# Dead access point detected.
					logAdd -q "disconnectIdxBumper: AP [${line}] did not submit the wrtwifistareport since more than 20 minutes. Check hardware and power supply. Will remove its associations from DTO ..."
					# 
					# Remove all STA that were reported by this AP from ASSOCIATIONS_DTO.
					sed -i "/^${line}_.*/d" "${ASSOCIATIONS_DTO}"
					mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_ASSOCIATONS" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$ASSOCIATIONS_DTO"
					sleep 2
					mosquitto_pub -h "${MQTT_HOST}" -p "${MQTT_PORT}" -t "$MQTT_TOPIC_DHCP" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -f "$DHCP_LEASES"
					# 
					# Update PRESENT_DEVICES_DTO
					plCheckDevicesByCounters
				fi
			fi
		done
		#
		sleep ${IDXBUMP_SLEEP_SECONDS}
	done
}
# ---------------------------------------------------
# -------------- END OF FUNCTION BLOCK --------------
# ---------------------------------------------------





















































#
# Check prerequisites.
if [ -f "/usr/sbin/syslog-ng" ]; then
	# syslog-ng is installed.
	# Check if 'external log server ip' is set correctly to forward the local logread to syslog-ng.
	if ( ! grep -q "option log_ip '127\.0\.0\.1'$" "/etc/config/system" ); then
		logAdd "[ERROR] You are using syslog-ng without forwarding the local syslog output to it. Set \"option log_ip '127.0.0.1'\". Stop."
		exit 99
	fi
	#
else
	logAdd "[WARN] syslog-ng is not installed. Only this AP will be monitored. Run \"opkg install syslog-ng\" if you need to monitor multiple APs."
fi
# 
if ( ! grep -q "network(ip(\"0\.0\.0\.0\") port(514)" "/etc/syslog-ng.conf" ); then
	logAdd "[WARN] syslog-ng is NOT configured to listen for incoming syslog messages from slave access points. Trying to fix ..."
	sed -i -e "s/network(ip(\".*[\"]/network(ip(\"0.0.0.0\"/g" "/etc/syslog-ng.conf"
	sed -i -e "s/network_localhost(/network(ip(\"0.0.0.0\") port(514) transport(udp) ip-protocol(6)/g" "/etc/syslog-ng.conf"
	#
	# Recheck.
	if ( ! grep -q "network(ip(\"0\.0\.0\.0\") port(514)" "/etc/syslog-ng.conf" ); then
		logAdd "[ERROR] syslog-ng is NOT configured to listen for incoming syslog messages from slave access points. Stop."
		exit 99
	fi
	/etc/init.d/syslog-ng restart
	logAdd "[INFO] Successfully reconfigured syslog-ng."
fi
# 
if ( ! grep -q "option cronloglevel '9'$" "/etc/config/system" ); then
	logAdd "[WARN] Cron log level is not reduced to \"warning\" in \"/etc/config/system\". Set \"option cronloglevel '9'\"."
fi
#
if [ "${CONFIG_WIFI_STA_REPORTS_ENABLED}" = "1" ]; then
	if [ ! -f "/root/wrtwifistareport.sh" ]; then
		logAdd "[ERROR] File missing: \"/root/wrtwifistareport.sh\". Stop."
		exit 99
	fi
	# 
	if ( ! grep -Fq "/root/wrtwifistareport.sh" "/etc/crontabs/root" ); then
		logAdd "[ERROR] Missing cron job for \"wrtwifistareport\". Stop."
		exit 99
	fi
fi
# 
if [ "${CONFIG_BLUETOOTH_REPORTS_ENABLED}" = "1" ]; then
	if [ ! -f "/root/wrtbtdevreport.sh" ]; then
		logAdd "[ERROR] File missing: \"/root/wrtbtdevreport.sh\". Stop."
		exit 99
	fi
	# 
	if ( ! grep -Fq "/root/wrtbtdevreport.sh" "/etc/crontabs/root" ); then
		logAdd "[ERROR] Missing cron job for \"wrtbtdevreport\". Stop."
		exit 99
	fi
fi
#
# Check commmand line parameters.
case "$1" in 
'debug')
	# Turn DEBUG_MODE on.
	DEBUG_MODE="1"
	# Continue script execution.
	;;
esac
#
# Service Startup.
#
if [ "${DEBUG_MODE}" = "0" ]; then
	logAdd "[INFO] Service was restarted."
	sleep 10
else
	# Log message.
	logAdd "*************"
	logAdd "[INFO] Service was restarted in DEBUG_MODE."
	# 
	# Adjust variables.
	IDXBUMP_SLEEP_SECONDS="3"
	DEVICE_DISCONNECTED_IDX="5"
	if [ ! -e "${GASERVICE_FIFO}" ]; then
		logAdd "[DEBUG] Creating file instead of FIFO [${GASERVICE_FIFO}]"
		touch "${GASERVICE_FIFO}"
	fi
fi
#
# Service Main.
# 
# Create FIFO.
rm "${EVENT_FIFO}" 2> /dev/null
mkfifo "${EVENT_FIFO}"
#
# Store script PID.
echo "$$" > "${PID_FILE}"
#
# Fork two permanently running background processes.
logreader &
disconnectIdxBumper &
#
# Wait for kill -INT from service stub.
wait
#
# We should never reach here.
#
logAdd "[INFO] End of script reached."
exit 0

[FR] Remove STA rows from "associations.dto" if AP's last resync was more than 20 minutes ago

[FR] Remove STA rows from "associations.dto" if AP's last resync was more than 20 minutes ago

Reason:

  • If an AP has power issues or has died, it can no longer send a syslog event or wrtwifistareport (for full resync) to the master. The master would assume a STA that had previously connected via the broken AP as "infinite" associated, authenticated and present.

Goal:

  • If an AP "died", the STA reports are removed from "associations.dto" and "present_devices.dto" will be updated if the STA did not connect to another AP in the meantime.

Can't get it to work - wrong hostname of nodes

Hi, I followed the steps but in my device it seems the dto files are not generating.

root@Router:~# sh /root/wrtpresence show
Usage: /root/wrtpresence {start|debug|stop|reset|restart|showlog|livelog|showstate|livestate|diag|ping|clean}
root@Router:~# sh /root/wrtpresence showstate
*** associations.dto: =0 = PRESENT, >0 and <5 = PRESENT, disconnected, =5 = AWAY
cat: can't open '/tmp/associations.dto': No such file or directory

*** present_devices.dto: Device names defined in main service script.
cat: can't open '/tmp/present_devices.dto': No such file or directory

*** wrtwifistareport last received unix time
grep: /tmp/*.last_wrtwifistareport: No such file or directory

What could it be? 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.