Resolving WiFi Sleep Issue for Tracked Phones

Those using WiFi for presence detection are well aware of the issue where smartphones “sleep” to save power when not used for a period of time. To resolve this, the device tracker component uses “consider home” to allow a delay before marking the device as away. I’ve combined a few components to improve this. It requires a few things:

  • A shell script running on your router (I already had this running on my DD-WRT access points to publish a MQTT topic with device signal strengths for crude room presence detection)
  • Some sort of indicator sensor that typically fires when someone leaves (I have a z-wave lock that has a unique alarm code for when the deadbolt is unlocked using the inside handle, but there are many options for this…door sensor, garage door, car, etc.)

By combining these two sensors, I now set “consider home” to a fairly high number (20 minutes). Long enough that no device will sleep longer than this interval (and also useful in preventing devices from being marked “away” when powered off or out of battery briefly). Then I use an automation to mark devices away when aren’t seen by the router if and only if the lock has been opened recently from the inside. So essentially the lock status tells HASS “someone left” and my access points tell me “who left”. Below is the automation used for this. It would be cool to build an “auxiliary” sensor into router based device trackers to streamline this.

- alias: Update Device Tracker upon Departure
  trigger:
    - platform: numeric_state
      entity_id: sensor.signal_east_person1
      below: -89 #my ddwrt script sets the signal to -90 if the mac address isn't connected
      for:
        seconds: 10
    - platform: numeric_state
      entity_id: sensor.signal_east_person2
      below: -89
      for:
        seconds: 10
  condition:
    - condition: state #this is the sensor I setup for when the door/lock was opened from the inside
       entity_id: binary_sensor.someone_left_home
       state: 'on'
  action:
    - delay: 00:01:00 #I have two access points that publish signal strengths every minute, so I wait 1 minute then make sure the device isn't connected to any of them
    - condition: template
      value_template: '{{ is_state( trigger.entity_id | replace("east", "west"), "-90") }}'
    - condition: template
      value_template: '{{ is_state( trigger.entity_id | replace("west", "east"), "-90") }}'
    - service: device_tracker.see
      data_template:
        dev_id: "{{ trigger.entity_id.split('signal_east_')[1] }}"
        location_name: 'not_home'

It would be very helpful if you publish the (redacted) script, too.

Sure thing, but note that it was quick/dirty and likely not very versatile. It works for my setup with an mqtt client installed on the router. It should work with most standard DD-WRT versions built in the past 3 years or so using HTTP. Older builds may experience issues with curl (and possibly other packages). Not sure how the DD-WRT component is built in python, but this would probably make more sense to build into HASS and populate in something similar to the “GPS Accuracy” attribute. The RSSI signal strength is published on the DD-WRT frontend, but I imagine our existing component isn’t scraping connected clients from this table.

# Runs on DDWRT router. Place in jffs or opt partition and initiate the script at boot. May require some optware packages, depending on your version.
# ONLY MONITORS 5ghz FREQUENCY (eth2 in the "wl" command). FIXED.

iter="1 2 3"
mac1=00:00:00:00:00:00  # PERSON1
mac2=00:00:00:00:00:00	# PERSON2
mac3=00:00:00:00:00:00	# PERSON3
mac4=00:00:00:00:00:00	# PERSON4

#hass_host="" #hardcoded for now
#hass_pass="" #hardcoded. for now

while true; do

	tot1=0
	tot2=0
	tot3=0
	tot4=0

	for i in $iter; 
	do
		sleep 10

		rssi1=$(wl -i eth1 rssi $mac1)
		rssi2=$(wl -i eth1 rssi $mac2)
		rssi3=$(wl -i eth1 rssi $mac3)
		rssi4=$(wl -i eth1 rssi $mac4)

		## SEARCH ALL CHANNELs/SSIDs/VLANs - depends on your specific hardware
		if [ "$rssi1" == "" ]; then rssi1=$(wl -i eth2 rssi $mac1); fi
		if [ "$rssi2" == "" ]; then rssi2=$(wl -i eth2 rssi $mac2); fi
		if [ "$rssi3" == "" ]; then rssi3=$(wl -i eth2 rssi $mac3); fi
		if [ "$rssi4" == "" ]; then rssi4=$(wl -i eth2 rssi $mac4); fi

		# TEST THAT INTEGERS RETURNED, OTHERWISE DEFAULT TO -90
		if expr "$rssi1" : '-[0-9]\+$' >/dev/null
		then
			tot1=$((tot1 + rssi1))
		else
			tot1=$((tot1 - 90))
		fi
		if expr "$rssi2" : '-[0-9]\+$' >/dev/null
		then
			tot2=$((tot2 + rssi2))
		else
			tot2=$((tot2 - 90))
		fi
		if expr "$rssi3" : '-[0-9]\+$' >/dev/null
		then
			tot3=$((tot3 + rssi3))
		else
			tot3=$((tot3 - 90 ))
		fi
		if expr "$rssi4" : '-[0-9]\+$' >/dev/null
		then
			tot4=$((tot4 + $rssi4))
		else
			tot4=$((tot4 - 90))
		fi
		
		sleep 10;
	done

	#FIND AVERAGE RSSI OVER TIME PERIOD 
	tot1=$((tot1 / 3))
	tot2=$((tot2 / 3))
	tot3=$((tot3 / 3))
	tot4=$((tot4 / 3))

	#SEND TO HASS via Mosquitto Client Optware/Entware package on ddwrt
	/opt/bin/mosquitto_pub -h 000.000.00.0 -u USERNAME -P PASSWORD -t WAP/1/rssi/PERSON1 -m "$tot1"
	/opt/bin/mosquitto_pub -h 000.000.00.0 -u USERNAME -P PASSWORD -t WAP/1/rssi/PERSON2 -m "$tot2"
	/opt/bin/mosquitto_pub -h 000.000.00.0 -u USERNAME -P PASSWORD -t WAP/1/rssi/PERSON3 -m "$tot3"
	/opt/bin/mosquitto_pub -h 000.000.00.0 -u USERNAME -P PASSOWRD -t WAP/1/rssi/PERSON4 -m "$tot4"

	##Old Method Using HTTP rather than MQTT
	#curl -X POST --insecure --retry-delay 180 -s -H "x-ha-access: PASSWORD" -H "Content-Type: application/json" -d '{"state":"'$tot1'", "attributes": {"unit_of_measurement": "rssi", "friendly_name": "PERSON1 West Signal"}}' "https://000.000.00.0:8123/api/states/sensor.signal_west_PERSON1"
	#curl -X POST --insecure --retry-delay 180 -s -H "x-ha-access: PASSWORD" -H "Content-Type: application/json" -d '{"state":"'$tot2'", "attributes": {"unit_of_measurement": "rssi", "friendly_name": "PERSON2 West Signal"}}' "https://000.000.00.0:8123/api/states/sensor.signal_west_PERSON2"
  	#curl -X POST --insecure --retry-delay 180 -s -H "x-ha-access: PASSWORD" -H "Content-Type: application/json" -d '{"state":"'$tot3'", "attributes": {"unit_of_measurement": "rssi", "friendly_name": "PERSON3 West Signal"}}' "https://000.000.00.0:8123/api/states/sensor.signal_west_PERSON3"
	#curl -X POST --insecure --retry-delay 180 -s -H "x-ha-access: PASSWORD" -H "Content-Type: application/json" -d '{"state":"'$tot4'", "attributes": {"unit_of_measurement": "rssi", "friendly_name": "PERSON4 West Signal"}}' "https://000.000.00.0:8123/api/states/sensor.signal_west_PERSON4"

done
1 Like

Anything new done with this?