Geofencing Improvements (#1048)

* Send an event when we enter or exit a zone

* Add more event data

* Change event name, update geofence registration logic, add zone entity ID to event

* Track geofence registration

* Remove geofences as needed, ignore updates within 5 seconds unless geofence event

* Reset variable when sending location

* Review comments
This commit is contained in:
Daniel Shokouhi 2020-10-15 05:10:03 -07:00 committed by GitHub
parent 6cf4b41d92
commit 48afca5427
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 2 deletions

View file

@ -9,6 +9,7 @@ import android.location.Location
import android.os.Build
import android.os.PowerManager
import android.util.Log
import android.widget.Toast
import androidx.core.content.ContextCompat.getSystemService
import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofencingEvent
@ -72,6 +73,8 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
private var lastLocationSend = 0L
private var lastUpdateLocation = ""
private var geofenceRegistered = false
}
@Inject
@ -122,6 +125,11 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
isBackgroundLocationSetup = false
isZoneLocationSetup = false
}
if (backgroundEnabled && !zoneEnabled && isZoneLocationSetup) {
removeGeofenceUpdateRequests()
isZoneLocationSetup = false
Log.d(TAG, "Removing geofence update requests")
}
if (backgroundEnabled && !isBackgroundLocationSetup) {
isBackgroundLocationSetup = true
requestLocationUpdates()
@ -143,9 +151,14 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
fusedLocationProviderClient.removeLocationUpdates(backgroundIntent)
removeGeofenceUpdateRequests()
}
private fun removeGeofenceUpdateRequests() {
val geofencingClient = LocationServices.getGeofencingClient(latestContext)
val zoneIntent = getLocationUpdateIntent(true)
geofencingClient.removeGeofences(zoneIntent)
geofenceRegistered = false
}
private fun requestLocationUpdates() {
@ -170,6 +183,11 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
return
}
if (geofenceRegistered) {
Log.w(TAG, "Not registering for zones as we already have")
return
}
Log.d(TAG, "Registering for zone based location updates")
try {
@ -204,12 +222,48 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
private fun handleGeoUpdate(intent: Intent) {
Log.d(TAG, "Received geofence update.")
if (!isEnabled(latestContext, zoneLocation.id)) {
isZoneLocationSetup = false
Log.w(TAG, "Unregistering geofences as zone tracking is disabled and intent was received")
removeGeofenceUpdateRequests()
return
}
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
Log.e(TAG, "Error getting geofence broadcast status code: ${geofencingEvent.errorCode}")
return
}
val validGeofencingEvents = listOf(Geofence.GEOFENCE_TRANSITION_ENTER, Geofence.GEOFENCE_TRANSITION_EXIT)
if (geofencingEvent.geofenceTransition in validGeofencingEvents) {
val zoneStatusEvent = when (geofencingEvent.geofenceTransition) {
Geofence.GEOFENCE_TRANSITION_ENTER -> "android.zone_entered"
Geofence.GEOFENCE_TRANSITION_EXIT -> "android.zone_exited"
else -> ""
}
val zoneAttr = mapOf(
"accuracy" to geofencingEvent.triggeringLocation.accuracy,
"altitude" to geofencingEvent.triggeringLocation.altitude,
"bearing" to geofencingEvent.triggeringLocation.bearing,
"latitude" to geofencingEvent.triggeringLocation.latitude,
"longitude" to geofencingEvent.triggeringLocation.longitude,
"provider" to geofencingEvent.triggeringLocation.provider,
"time" to geofencingEvent.triggeringLocation.time,
"vertical_accuracy" to if (Build.VERSION.SDK_INT >= 26) geofencingEvent.triggeringLocation.verticalAccuracyMeters.toInt() else 0,
"zone" to geofencingEvent.triggeringGeofences[0].requestId
)
runBlocking {
try {
integrationUseCase.fireEvent(zoneStatusEvent, zoneAttr as Map<String, Any>)
Log.d(TAG, "Event sent to Home Assistant")
} catch (e: Exception) {
Log.e(TAG, "Unable to send event to Home Assistant", e)
Toast.makeText(latestContext, R.string.zone_event_failure, Toast.LENGTH_LONG)
.show()
}
}
}
val sensorDao = AppDatabase.getInstance(latestContext).sensorDao()
val sensorSettings = sensorDao.getSettings(zoneLocation.id)
val minAccuracy = sensorSettings
@ -224,11 +278,11 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
)
requestSingleAccurateLocation()
} else {
sendLocationUpdate(geofencingEvent.triggeringLocation)
sendLocationUpdate(geofencingEvent.triggeringLocation, "", true)
}
}
private fun sendLocationUpdate(location: Location, name: String = "") {
private fun sendLocationUpdate(location: Location, name: String = "", geofenceUpdate: Boolean = false) {
Log.d(
TAG, "Last Location: " +
"\nCoords:(${location.latitude}, ${location.longitude})" +
@ -256,6 +310,11 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
Log.d(TAG, "Duplicate location received, not sending to HA")
return
}
} else {
if (now < lastLocationSend + 5000 && !geofenceUpdate) {
Log.d(TAG, "New location update not possible within 5 seconds, not sending to HA")
return
}
}
lastLocationSend = now
lastUpdateLocation = updateLocation.gps.contentToString()
@ -304,6 +363,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
.build()
)
}
geofenceRegistered = true
return geofencingRequestBuilder.build()
}

View file

@ -77,6 +77,7 @@ to your home internet.</string>
<string name="connected_to_home_assistant">Connected to Home Assistant</string>
<string name="create_template">Create Template</string>
<string name="database_event_failure">Unable to send migration failure event to Home Assistant</string>
<string name="zone_event_failure">Unable to send zone event to Home Assistant</string>
<string name="database_migration_failed">Database migration failed, all sensor and widget data has been reset. Please re-enable your sensors and recreate your widgets.</string>
<string name="developer_tools">Developer Tools</string>
<string name="device_class">Device Class</string>