diff --git a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/BeaconNotification.kt b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/BeaconNotification.kt new file mode 100755 index 00000000000..ab527deaa7d --- /dev/null +++ b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/BeaconNotification.kt @@ -0,0 +1,64 @@ +package io.homeassistant.companion.android.common.bluetooth.ble + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R +import io.homeassistant.companion.android.common.sensors.SensorReceiverBase +import io.homeassistant.companion.android.common.sensors.SensorUpdateReceiver +import io.homeassistant.companion.android.common.util.CHANNEL_BEACON_MONITOR +import io.homeassistant.companion.android.common.util.CHANNEL_BLE_TRANSMITTER + +fun beaconNotification(isTransmitter: Boolean, context: Context): NotificationCompat.Builder { + val builder = NotificationCompat.Builder( + context, + if (isTransmitter) { + CHANNEL_BLE_TRANSMITTER + } else { + CHANNEL_BEACON_MONITOR + } + ) + builder.setSmallIcon(R.drawable.ic_stat_ic_notification) + builder.setContentTitle( + context.getString( + if (isTransmitter) { + R.string.beacon_transmitting + } else { + R.string.beacon_scanning + } + ) + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel( + if (isTransmitter) { + CHANNEL_BLE_TRANSMITTER + } else { + CHANNEL_BEACON_MONITOR + }, + context.getString( + if (isTransmitter) { + R.string.beacon_transmitting + } else { + R.string.beacon_scanning + } + ), + NotificationManager.IMPORTANCE_LOW + ) + val notifManager = context.getSystemService()!! + notifManager.createNotificationChannel(channel) + } + val stopIntent = Intent(context, SensorUpdateReceiver::class.java) + stopIntent.action = if (isTransmitter) { + SensorReceiverBase.ACTION_STOP_BEACON_TRANSMITTING + } else { + SensorReceiverBase.ACTION_STOP_BEACON_SCANNING + } + val stopPendingIntent = PendingIntent.getBroadcast(context, 0, stopIntent, PendingIntent.FLAG_MUTABLE) + builder.addAction(0, context.getString(R.string.disable), stopPendingIntent) + return builder +} diff --git a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/MonitoringManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/MonitoringManager.kt index 0127322227c..9f399f0f153 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/MonitoringManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/MonitoringManager.kt @@ -1,18 +1,7 @@ package io.homeassistant.companion.android.common.bluetooth.ble -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent import android.content.Context -import android.content.Intent -import android.os.Build -import androidx.core.app.NotificationCompat -import androidx.core.content.getSystemService import io.homeassistant.companion.android.common.BuildConfig -import io.homeassistant.companion.android.common.R -import io.homeassistant.companion.android.common.sensors.SensorReceiverBase -import io.homeassistant.companion.android.common.sensors.SensorUpdateReceiver -import io.homeassistant.companion.android.common.util.CHANNEL_BEACON_MONITOR import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -76,18 +65,7 @@ class MonitoringManager { } } - val builder = NotificationCompat.Builder(context, CHANNEL_BEACON_MONITOR) - builder.setSmallIcon(R.drawable.ic_stat_ic_notification) - builder.setContentTitle(context.getString(R.string.beacon_scanning)) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channel = NotificationChannel(CHANNEL_BEACON_MONITOR, context.getString(R.string.beacon_scanning), NotificationManager.IMPORTANCE_LOW) - val notifManager = context.getSystemService()!! - notifManager.createNotificationChannel(channel) - } - val stopScanningIntent = Intent(context, SensorUpdateReceiver::class.java) - stopScanningIntent.action = SensorReceiverBase.ACTION_STOP_BEACON_SCANNING - val stopScanningPendingIntent = PendingIntent.getBroadcast(context, 0, stopScanningIntent, PendingIntent.FLAG_MUTABLE) - builder.addAction(0, context.getString(R.string.disable), stopScanningPendingIntent) + val builder = beaconNotification(false, context) beaconManager.enableForegroundServiceScanning(builder.build(), 444) beaconManager.setEnableScheduledScanJobs(false) beaconManager.startRangingBeacons(region) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/TransmitterManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/TransmitterManager.kt index 64630c0e75d..02f614fc499 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/TransmitterManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/TransmitterManager.kt @@ -8,12 +8,17 @@ import androidx.core.content.getSystemService import io.homeassistant.companion.android.common.sensors.BluetoothSensorManager import java.util.UUID import org.altbeacon.beacon.Beacon +import org.altbeacon.beacon.BeaconManager import org.altbeacon.beacon.BeaconParser import org.altbeacon.beacon.BeaconTransmitter +import org.altbeacon.beacon.Identifier +import org.altbeacon.beacon.Region object TransmitterManager { private lateinit var physicalTransmitter: BeaconTransmitter + private var beaconManager: BeaconManager? = null private lateinit var beacon: Beacon + private val region = Region("dummy-region", Identifier.parse("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"), null, null) private fun buildBeacon(haTransmitterI: IBeaconTransmitter): Beacon { val builder = Beacon.Builder() @@ -58,9 +63,20 @@ object TransmitterManager { } val bluetoothAdapter = context.getSystemService()?.adapter val bluetoothOn = bluetoothAdapter?.isEnabled == true + beaconManager = BeaconManager.getInstanceForApplication(context) + if (bluetoothOn) { val beacon = buildBeacon(haTransmitter) if (!physicalTransmitter.isStarted) { + val builder = beaconNotification(true, context) + beaconManager?.enableForegroundServiceScanning(builder.build(), 445) + beaconManager?.setEnableScheduledScanJobs(false) + beaconManager?.beaconParsers?.clear() + beaconManager?.backgroundBetweenScanPeriod = Long.MAX_VALUE + beaconManager?.backgroundScanPeriod = 0 + beaconManager?.foregroundBetweenScanPeriod = Long.MAX_VALUE + beaconManager?.foregroundScanPeriod = 0 + beaconManager?.startMonitoring(region) physicalTransmitter.advertiseTxPowerLevel = getPowerLevel(haTransmitter) physicalTransmitter.advertiseMode = getAdvertiseMode(haTransmitter) physicalTransmitter.startAdvertising( @@ -115,5 +131,7 @@ object TransmitterManager { } haTransmitter.transmitting = false haTransmitter.state = "Stopped" + beaconManager?.stopMonitoring(region) + beaconManager?.disableForegroundServiceScanning() } } diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/BluetoothSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/BluetoothSensorManager.kt index 9de37828257..cc33b3f39e8 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/BluetoothSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/BluetoothSensorManager.kt @@ -114,6 +114,7 @@ class BluetoothSensorManager : SensorManager { } sensorDao.add(SensorSetting(bleTransmitter.id, SETTING_BLE_TRANSMIT_ENABLED, transmitEnabled.toString(), SensorSettingType.TOGGLE)) + SensorUpdateReceiver.updateSensors(context) } fun enableDisableBeaconMonitor(context: Context, monitorEnabled: Boolean) { @@ -150,7 +151,8 @@ class BluetoothSensorManager : SensorManager { (sensorId == bleTransmitter.id && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> { arrayOf( Manifest.permission.BLUETOOTH_ADVERTISE, - Manifest.permission.BLUETOOTH_CONNECT + Manifest.permission.BLUETOOTH_CONNECT, + Manifest.permission.BLUETOOTH_SCAN ) } (sensorId == beaconMonitor.id && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) -> { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorReceiverBase.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorReceiverBase.kt index 3aeec8be617..276fa234d15 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorReceiverBase.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorReceiverBase.kt @@ -43,6 +43,7 @@ abstract class SensorReceiverBase : BroadcastReceiver() { const val ACTION_UPDATE_SENSOR = "io.homeassistant.companion.android.UPDATE_SENSOR" const val ACTION_UPDATE_SENSORS = "io.homeassistant.companion.android.UPDATE_SENSORS" const val ACTION_STOP_BEACON_SCANNING = "io.homeassistant.companion.android.STOP_BEACON_SCANNING" + const val ACTION_STOP_BEACON_TRANSMITTING = "io.homeassistant.companion.android.STOP_BEACON_TRANSMITTING" const val EXTRA_SENSOR_ID = "sensorId" fun shouldDoFastUpdates(context: Context): Boolean { @@ -111,6 +112,11 @@ abstract class SensorReceiverBase : BroadcastReceiver() { return } + if (intent.action == ACTION_STOP_BEACON_TRANSMITTING) { + BluetoothSensorManager.enableDisableBLETransmitter(context, false) + return + } + @Suppress("DEPRECATION") if (isSensorEnabled(LastUpdateManager.lastUpdate.id)) { LastUpdateManager().sendLastUpdate(context, intent.action) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/util/AppNotifChannels.kt b/common/src/main/java/io/homeassistant/companion/android/common/util/AppNotifChannels.kt index cf37b19830b..38b00a4bc16 100755 --- a/common/src/main/java/io/homeassistant/companion/android/common/util/AppNotifChannels.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/util/AppNotifChannels.kt @@ -10,6 +10,7 @@ const val CHANNEL_LOCATION_DISABLED = "Location disabled" const val CHANNEL_DOWNLOADS = "downloads" const val CHANNEL_GENERAL = "general" const val CHANNEL_BEACON_MONITOR = "beacon" +const val CHANNEL_BLE_TRANSMITTER = "transmitter" val appCreatedChannels = listOf( CHANNEL_SENSOR_WORKER, @@ -21,5 +22,6 @@ val appCreatedChannels = listOf( CHANNEL_LOCATION_DISABLED, CHANNEL_DOWNLOADS, CHANNEL_GENERAL, - CHANNEL_BEACON_MONITOR + CHANNEL_BEACON_MONITOR, + CHANNEL_BLE_TRANSMITTER ) diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 0f14ce276ee..36709dc8158 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -573,7 +573,7 @@ The current battery level of the device The current charging state of the battery The current battery temperature - 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. The default Minor value 40004 has the special meaning of forcing the beacon to be recognized by the iBeacon Tracker integration\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\nIt is also possible to set beacons to only be transmitted when connected to a Home Network WiFi SSID, which may be desirable for privacy and battery life.\n\nNote:\nAdditionally a separate setting exists (\"Enable Transmitter\") to stop or start transmitting. + Send BLE iBeacon with configured interval, used to track presence around house, e.g. together with roomassistant, esp32-mqtt-room or espresence projects. A notification will be shown on the device when transmitting.\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. The default Minor value 40004 has the special meaning of forcing the beacon to be recognized by the iBeacon Tracker integration\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\nIt is also possible to set beacons to only be transmitted when connected to a Home Network WiFi SSID, which may be desirable for privacy and battery life.\n\nNote:\nAdditionally a separate setting exists (\"Enable Transmitter\") to stop or start transmitting. Scans for iBeacons and shows the IDs of nearby beacons and their distance in meters. A notification will be shown on the device when scanning is actively running.\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. Information about currently connected Bluetooth devices Whether Bluetooth is enabled on the device @@ -1261,4 +1261,5 @@ Tampering detected Update available Up-to-date + Beacon transmitting