mirror of
https://github.com/home-assistant/android
synced 2024-10-04 23:29:31 +00:00
Add UUID filter to Beacon Monitor (#3178)
This commit is contained in:
parent
a5cbe04315
commit
bca80f6391
|
@ -16,6 +16,7 @@ import androidx.lifecycle.viewModelScope
|
|||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.homeassistant.companion.android.common.bluetooth.BluetoothUtils
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.sensors.BluetoothSensorManager
|
||||
import io.homeassistant.companion.android.common.sensors.NetworkSensorManager
|
||||
import io.homeassistant.companion.android.common.sensors.SensorManager
|
||||
import io.homeassistant.companion.android.common.util.DisabledLocationHandler
|
||||
|
@ -332,6 +333,13 @@ class SensorDetailViewModel @Inject constructor(
|
|||
}
|
||||
SensorSettingType.LIST_ZONES ->
|
||||
entries ?: zones
|
||||
SensorSettingType.LIST_BEACONS -> {
|
||||
// show current beacons and also previously selected UUIDs
|
||||
entries ?: (sensorManager as BluetoothSensorManager).getBeaconUUIDs()
|
||||
.plus(setting.value.split(", ").filter { it.isNotEmpty() })
|
||||
.sorted()
|
||||
.distinct()
|
||||
}
|
||||
else ->
|
||||
emptyList()
|
||||
}
|
||||
|
|
|
@ -207,7 +207,7 @@ fun SensorDetailView(
|
|||
}
|
||||
)
|
||||
}
|
||||
SensorSettingType.LIST, SensorSettingType.LIST_APPS, SensorSettingType.LIST_BLUETOOTH, SensorSettingType.LIST_ZONES -> {
|
||||
SensorSettingType.LIST, SensorSettingType.LIST_APPS, SensorSettingType.LIST_BLUETOOTH, SensorSettingType.LIST_ZONES, SensorSettingType.LIST_BEACONS -> {
|
||||
val summaryValues = setting.value.split(", ").mapNotNull { it.ifBlank { null } }
|
||||
SensorDetailRow(
|
||||
title = viewModel.getSettingTranslatedTitle(setting.name),
|
||||
|
|
|
@ -22,17 +22,32 @@ data class IBeacon(
|
|||
class IBeaconMonitor {
|
||||
lateinit var sensorManager: BluetoothSensorManager
|
||||
var beacons: List<IBeacon> = listOf()
|
||||
var lastSeenBeacons: Collection<Beacon> = listOf()
|
||||
private var uuidFilter = listOf<String>()
|
||||
private var uuidFilterExclude = false
|
||||
|
||||
private fun sort(tmp: Collection<IBeacon>): Collection<IBeacon> {
|
||||
return tmp.sortedBy { it.distance }
|
||||
}
|
||||
|
||||
private fun ignoreBeacon(uuid: String): Boolean {
|
||||
val inList = uuidFilter.contains(uuid)
|
||||
return if (uuidFilterExclude)
|
||||
inList // exclude filter, keep those not in list
|
||||
else
|
||||
!(inList || uuidFilter.isEmpty()) // include filter, keep those in list (or all if the list is empty)
|
||||
}
|
||||
|
||||
fun setBeacons(context: Context, newBeacons: Collection<Beacon>) {
|
||||
lastSeenBeacons = newBeacons // unfiltered list, for the settings UI
|
||||
var requireUpdate = false
|
||||
val tmp = mutableMapOf<String, IBeacon>()
|
||||
for (existingBeacon in beacons) {
|
||||
existingBeacon.skippedUpdated++
|
||||
tmp += existingBeacon.name to existingBeacon
|
||||
if (++existingBeacon.skippedUpdated > MAX_SKIPPED_UPDATED) { // an old beacon expired
|
||||
requireUpdate = true
|
||||
} else {
|
||||
tmp += existingBeacon.name to existingBeacon
|
||||
}
|
||||
}
|
||||
for (newBeacon in newBeacons) {
|
||||
val uuid = newBeacon.id1.toString()
|
||||
|
@ -42,8 +57,12 @@ class IBeaconMonitor {
|
|||
val rssi = newBeacon.runningAverageRssi
|
||||
|
||||
val beacon = IBeacon(uuid, major, minor, distance, rssi, 0)
|
||||
if (beacon.name !in tmp) { // we found a new beacon
|
||||
val existing = tmp[beacon.name]
|
||||
if (existing == null) { // we found a new beacon
|
||||
if (ignoreBeacon(uuid)) continue // UUID filter (note: no need to check old beacons)
|
||||
requireUpdate = true
|
||||
} else {
|
||||
existing.skippedUpdated = 0 // beacon seen, make sure skippedUpdated=0, even if requireUpdate stays false (and beacons list is not replaced)
|
||||
}
|
||||
tmp += beacon.name to beacon
|
||||
}
|
||||
|
@ -52,16 +71,6 @@ class IBeaconMonitor {
|
|||
sendUpdate(context, sorted)
|
||||
return
|
||||
}
|
||||
for (i in sorted.indices.reversed()) {
|
||||
if (sorted[i].skippedUpdated > MAX_SKIPPED_UPDATED) { // a old beacon expired
|
||||
sorted.removeAt(i)
|
||||
requireUpdate = true
|
||||
}
|
||||
}
|
||||
if (requireUpdate) {
|
||||
sendUpdate(context, sorted)
|
||||
return
|
||||
}
|
||||
for ((i, existingBeacon) in beacons.withIndex()) {
|
||||
if (i < sorted.size) {
|
||||
if (sorted[i].name != existingBeacon.name || // the distance order switched
|
||||
|
@ -85,4 +94,14 @@ class IBeaconMonitor {
|
|||
intent.action = SensorReceiverBase.ACTION_UPDATE_SENSORS
|
||||
context.sendBroadcast(intent)
|
||||
}
|
||||
|
||||
fun setUUIDFilter(uuidFilter: List<String>, uuidFilterExclude: Boolean) {
|
||||
this.uuidFilter = uuidFilter
|
||||
this.uuidFilterExclude = uuidFilterExclude
|
||||
|
||||
// existing beacons are only filtered when the filter changes
|
||||
beacons
|
||||
.filter { ignoreBeacon(it.uuid) }
|
||||
.forEach { it.skippedUpdated = MAX_SKIPPED_UPDATED } // delete in the next update
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ import io.homeassistant.companion.android.common.bluetooth.ble.name
|
|||
import io.homeassistant.companion.android.database.AppDatabase
|
||||
import io.homeassistant.companion.android.database.sensor.SensorSetting
|
||||
import io.homeassistant.companion.android.database.sensor.SensorSettingType
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
|
@ -41,6 +44,8 @@ class BluetoothSensorManager : SensorManager {
|
|||
private const val SETTING_BEACON_MONITOR_SCAN_INTERVAL = "beacon_monitor_scan_interval"
|
||||
private const val SETTING_BEACON_MONITOR_FILTER_ITERATIONS = "beacon_monitor_filter_iterations"
|
||||
private const val SETTING_BEACON_MONITOR_FILTER_RSSI_MULTIPLIER = "beacon_monitor_filter_rssi_multiplier"
|
||||
private const val SETTING_BEACON_MONITOR_UUID_FILTER = "beacon_monitor_uuid_filter"
|
||||
private const val SETTING_BEACON_MONITOR_UUID_FILTER_EXCLUDE = "beacon_monitor_uuid_filter_exclude"
|
||||
|
||||
private const val DEFAULT_BLE_TRANSMIT_POWER = "ultraLow"
|
||||
private const val DEFAULT_BLE_ADVERTISE_MODE = "lowPower"
|
||||
|
@ -126,6 +131,8 @@ class BluetoothSensorManager : SensorManager {
|
|||
}
|
||||
}
|
||||
|
||||
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
override fun docsLink(): String {
|
||||
return "https://companion.home-assistant.io/docs/core/sensors#bluetooth-sensors"
|
||||
}
|
||||
|
@ -301,6 +308,15 @@ class BluetoothSensorManager : SensorManager {
|
|||
KalmanFilter.maxIterations = getSetting(context, beaconMonitor, SETTING_BEACON_MONITOR_FILTER_ITERATIONS, SensorSettingType.NUMBER, DEFAULT_BEACON_MONITOR_FILTER_ITERATIONS).toIntOrNull() ?: DEFAULT_BEACON_MONITOR_FILTER_ITERATIONS.toInt()
|
||||
KalmanFilter.rssiMultiplier = getSetting(context, beaconMonitor, SETTING_BEACON_MONITOR_FILTER_RSSI_MULTIPLIER, SensorSettingType.NUMBER, DEFAULT_BEACON_MONITOR_FILTER_RSSI_MULTIPLIER).toDoubleOrNull() ?: DEFAULT_BEACON_MONITOR_FILTER_RSSI_MULTIPLIER.toDouble()
|
||||
|
||||
val uuidFilter = getSetting(context, beaconMonitor, SETTING_BEACON_MONITOR_UUID_FILTER, SensorSettingType.LIST_BEACONS, "").split(", ").filter { it.isNotEmpty() }
|
||||
beaconMonitoringDevice.setUUIDFilter(
|
||||
uuidFilter,
|
||||
getSetting(context, beaconMonitor, SETTING_BEACON_MONITOR_UUID_FILTER_EXCLUDE, SensorSettingType.TOGGLE, "false").toBoolean()
|
||||
)
|
||||
ioScope.launch {
|
||||
enableDisableSetting(context, beaconMonitor, SETTING_BEACON_MONITOR_UUID_FILTER_EXCLUDE, uuidFilter.isNotEmpty())
|
||||
}
|
||||
|
||||
val restart = monitoringManager.isMonitoring() &&
|
||||
(monitoringManager.scanPeriod != scanPeriod || monitoringManager.scanInterval != scanInterval)
|
||||
monitoringManager.scanPeriod = scanPeriod
|
||||
|
@ -378,6 +394,12 @@ class BluetoothSensorManager : SensorManager {
|
|||
)
|
||||
}
|
||||
|
||||
fun getBeaconUUIDs(): List<String> {
|
||||
return beaconMonitoringDevice.beacons
|
||||
.map { it.uuid }
|
||||
.plus(beaconMonitoringDevice.lastSeenBeacons.map { it.id1.toString() }) // include ignored
|
||||
}
|
||||
|
||||
private fun checkNameAddress(bt: BluetoothDevice): String {
|
||||
return if (bt.address != bt.name) "${bt.address} (${bt.name})" else bt.address
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ enum class SensorSettingType(val string: String, val listType: Boolean = false)
|
|||
LIST_APPS("list-apps", listType = true),
|
||||
LIST_BLUETOOTH("list-bluetooth", listType = true),
|
||||
LIST_ZONES("list-zones", listType = true),
|
||||
LIST_BEACONS("list-beacons", listType = true),
|
||||
}
|
||||
|
||||
@Entity(tableName = "sensor_settings", primaryKeys = ["sensor_id", "name"])
|
||||
|
|
|
@ -486,7 +486,7 @@
|
|||
<string name="sensor_description_battery_state">The current charging state of the battery</string>
|
||||
<string name="sensor_description_battery_temperature">The current battery temperature</string>
|
||||
<string name="sensor_description_bluetooth_ble_emitter">Send BLE iBeacon with configured interval, used to track presence around house, e.g. together with roomassistant, esp32-mqtt-room or espresence projects.\n\nWarning: this can affect battery life, particularly if the \"Transmitter power\" setting is set to High or \"Advertise Mode\" is set to Low latency.\n\nSettings allow for specifying:\n- \"UUID\" (standard UUID format), \"Major\" and \"Minor\" (should be 0 - 65535), to tailor identifiers and groups\n- \"Transmitter Power\" and \"Advertise Mode\" to help to preserve battery life (use lowest values if possible)\n - \"Measured Power\" to specify power measured at 1m (initial default -59)\n\nNote:\nAdditionally a separate setting exists (\"Enable Transmitter\") to stop or start transmitting.</string>
|
||||
<string name="sensor_description_bluetooth_ble_beacon_monitor">Scans for iBeacons and shows the IDs of nearby beacons and their distance in meters.\n\nWarning: this can affect battery life, especially with a short \"Scan Interval\".\n\nSettings allow for specifying:\n- \"Filter Iterations\" (should be 1 - 100, default: 10), higher values will result in more stable measurements but also less responsiveness.\n- \"Filter RSSI Multiplier\" (should be 1.0 - 2.0, default: 1.05), can be used to archive more stable measurements when beacons are farther away. This will also affect responsiveness.\n - \"Scan Interval\" (default: 500) milliseconds between scans. Shorter intervals will drain the battery more quickly.\n - \"Scan Period\" (default: 1100) milliseconds to scan for beacons. Most beacons will send a signal every second so this value should be at least 1100ms.\n\nNote:\nAdditionally a separate setting exists (\"Enable Beacon Monitor\") to stop or start scanning.</string>
|
||||
<string name="sensor_description_bluetooth_ble_beacon_monitor">Scans for iBeacons and shows the IDs of nearby beacons and their distance in meters.\n\nWarning: this can affect battery life, especially with a short \"Scan Interval\".\n\nSettings allow for specifying:\n- \"Filter Iterations\" (should be 1 - 100, default: 10), higher values will result in more stable measurements but also less responsiveness.\n- \"Filter RSSI Multiplier\" (should be 1.0 - 2.0, default: 1.05), can be used to archive more stable measurements when beacons are farther away. This will also affect responsiveness.\n- \"Scan Interval\" (default: 500) milliseconds between scans. Shorter intervals will drain the battery more quickly.\n- \"Scan Period\" (default: 1100) milliseconds to scan for beacons. Most beacons will send a signal every second so this value should be at least 1100ms.\n- \"UUID Filter\" allows to restrict the reported beacons by including (or excluding) those with the selected UUIDs.\n- \"Exclude selected UUIDs\", if false (default) only the beacons with the selected UUIDs are reported. If true all beacons except the selected ones are reported. Not available when \"UUID Filter\" is empty.\n\nNote:\nAdditionally a separate setting exists (\"Enable Beacon Monitor\") to stop or start scanning.</string>
|
||||
<string name="sensor_description_bluetooth_connection">Information about currently connected Bluetooth devices</string>
|
||||
<string name="sensor_description_bluetooth_state">Whether Bluetooth is enabled on the device</string>
|
||||
<string name="sensor_description_charger_type">The type of charger plugged into the device currently</string>
|
||||
|
@ -608,6 +608,8 @@
|
|||
<string name="sensor_setting_beacon_monitor_scan_interval_title">Scan Interval</string>
|
||||
<string name="sensor_setting_beacon_monitor_filter_iterations_title">Filter Iterations</string>
|
||||
<string name="sensor_setting_beacon_monitor_filter_rssi_multiplier_title">Filter RSSI Multiplier</string>
|
||||
<string name="sensor_setting_beacon_monitor_uuid_filter_title">UUID Filter</string>
|
||||
<string name="sensor_setting_beacon_monitor_uuid_filter_exclude_title">Exclude selected UUIDs</string>
|
||||
<string name="sensor_setting_geocode_minimum_accuracy_title">Minimum Accuracy</string>
|
||||
<string name="sensor_setting_geocode_include_location_updates_title">Update sensor with location sensors</string>
|
||||
<string name="sensor_setting_lastreboot_deadband_title">Deadband</string>
|
||||
|
|
Loading…
Reference in a new issue