forked from home-assistant/android
-
Notifications
You must be signed in to change notification settings - Fork 0
/
BluetoothSensorManager.kt
222 lines (198 loc) · 10.2 KB
/
BluetoothSensorManager.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package io.homeassistant.companion.android.sensors
import android.Manifest
import android.content.Context
import android.os.Build
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.bluetooth.BluetoothUtils
import io.homeassistant.companion.android.bluetooth.ble.IBeaconTransmitter
import io.homeassistant.companion.android.bluetooth.ble.TransmitterManager
import io.homeassistant.companion.android.database.AppDatabase
import io.homeassistant.companion.android.database.sensor.Setting
import java.util.UUID
import kotlin.collections.ArrayList
class BluetoothSensorManager : SensorManager {
companion object {
private const val SETTING_BLE_ID1 = "ble_uuid"
private const val SETTING_BLE_ID2 = "ble_major"
private const val SETTING_BLE_ID3 = "ble_minor"
private const val SETTING_BLE_TRANSMIT_POWER = "ble_transmit_power"
private const val SETTING_BLE_ADVERTISE_MODE = "ble_advertise_mode"
private const val SETTING_BLE_TRANSMIT_ENABLED = "ble_transmit_enabled"
private const val SETTING_BLE_ENABLE_TOGGLE_ALL = "ble_enable_toggle_all"
private const val SETTING_BLE_MEASURED_POWER = "ble_measured_power_at_1m"
private const val DEFAULT_BLE_TRANSMIT_POWER = "ultraLow"
private const val DEFAULT_BLE_ADVERTISE_MODE = "lowPower"
private const val DEFAULT_BLE_MAJOR = "100"
private const val DEFAULT_BLE_MINOR = "1"
private const val DEFAULT_MEASURED_POWER_AT_1M = "-59"
private var priorBluetoothStateEnabled = false
// private const val TAG = "BluetoothSM"
private var bleTransmitterDevice = IBeaconTransmitter("", "", "", transmitPowerSetting = "", measuredPowerSetting = 0, advertiseModeSetting = "", transmitting = false, state = "", restartRequired = false)
val bluetoothConnection = SensorManager.BasicSensor(
"bluetooth_connection",
"sensor",
R.string.basic_sensor_name_bluetooth,
R.string.sensor_description_bluetooth_connection,
unitOfMeasurement = "connection(s)"
)
val bluetoothState = SensorManager.BasicSensor(
"bluetooth_state",
"binary_sensor",
R.string.basic_sensor_name_bluetooth_state,
R.string.sensor_description_bluetooth_state
)
val bleTransmitter = SensorManager.BasicSensor(
"ble_emitter",
"sensor",
R.string.basic_sensor_name_bluetooth_ble_emitter,
R.string.sensor_description_bluetooth_ble_emitter
)
fun enableDisableBLETransmitter(context: Context, transmitEnabled: Boolean) {
val sensorDao = AppDatabase.getInstance(context).sensorDao()
val sensorEntity = sensorDao.get(bleTransmitter.id)
val sensorEnabled = (sensorEntity != null && sensorEntity.enabled)
if (!sensorEnabled)
return
TransmitterManager.stopTransmitting(bleTransmitterDevice) // stop in all instances, clean up state if start required
if (transmitEnabled) {
TransmitterManager.startTransmitting(context, bleTransmitterDevice)
}
sensorDao.add(Setting(bleTransmitter.id, SETTING_BLE_TRANSMIT_ENABLED, transmitEnabled.toString(), "toggle"))
}
}
override fun docsLink(): String {
return "https://companion.home-assistant.io/docs/core/sensors#bluetooth-sensors"
}
override val enabledByDefault: Boolean
get() = false
override val name: Int
get() = R.string.sensor_name_bluetooth
override fun getAvailableSensors(context: Context): List<SensorManager.BasicSensor> {
return listOf(bluetoothConnection, bluetoothState, bleTransmitter)
}
override fun requiredPermissions(sensorId: String): Array<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_CONNECT)
} else
arrayOf(Manifest.permission.BLUETOOTH)
}
override fun requestSensorUpdate(
context: Context
) {
updateBluetoothConnectionSensor(context)
updateBluetoothState(context)
updateBLESensor(context)
}
private fun updateBluetoothConnectionSensor(context: Context) {
if (!isEnabled(context, bluetoothConnection.id))
return
var totalConnectedDevices = 0
val icon = "mdi:bluetooth"
var connectedPairedDevices: List<String> = ArrayList()
var connectedNotPairedDevices: List<String> = ArrayList()
var bondedString = ""
if (checkPermission(context, bluetoothConnection.id)) {
val bluetoothDevices = BluetoothUtils.getBluetoothDevices(context)
bondedString = bluetoothDevices.filter { b -> b.paired }.map { it.address }.toString()
connectedPairedDevices = bluetoothDevices.filter { b -> b.paired && b.connected }.map { it.address }
connectedNotPairedDevices = bluetoothDevices.filter { b -> !b.paired && b.connected }.map { it.address }
totalConnectedDevices = bluetoothDevices.filter { b -> b.connected }.count()
}
onSensorUpdated(
context,
bluetoothConnection,
totalConnectedDevices,
icon,
mapOf(
"connected_paired_devices" to connectedPairedDevices,
"connected_not_paired_devices" to connectedNotPairedDevices,
"paired_devices" to bondedString
)
)
}
private fun isBtOn(context: Context): Boolean {
var btOn = false
if (checkPermission(context, bluetoothState.id)) {
btOn = BluetoothUtils.isOn(context)
}
return btOn
}
private fun updateBluetoothState(context: Context) {
if (!isEnabled(context, bluetoothState.id))
return
val icon = if (isBtOn(context)) "mdi:bluetooth" else "mdi:bluetooth-off"
onSensorUpdated(
context,
bluetoothState,
isBtOn(context),
icon,
mapOf()
)
}
override fun enableToggleAll(context: Context, sensorId: String): Boolean {
if (sensorId == bleTransmitter.id) {
return getSetting(context, bleTransmitter, SETTING_BLE_ENABLE_TOGGLE_ALL, "toggle", "false").toBoolean()
}
return super.enableToggleAll(context, sensorId)
}
private fun updateBLEDevice(context: Context) {
addSettingIfNotPresent(context, bleTransmitter, SETTING_BLE_ENABLE_TOGGLE_ALL, "toggle", "false")
val transmitActive = getSetting(context, bleTransmitter, SETTING_BLE_TRANSMIT_ENABLED, "toggle", "true").toBoolean()
val uuid = getSetting(context, bleTransmitter, SETTING_BLE_ID1, "string", UUID.randomUUID().toString())
val major = getSetting(context, bleTransmitter, SETTING_BLE_ID2, "string", DEFAULT_BLE_MAJOR)
val minor = getSetting(context, bleTransmitter, SETTING_BLE_ID3, "string", DEFAULT_BLE_MINOR)
val measuredPower = getSetting(context, bleTransmitter, SETTING_BLE_MEASURED_POWER, "number", DEFAULT_MEASURED_POWER_AT_1M).toInt()
val transmitPower = getSetting(context, bleTransmitter, SETTING_BLE_TRANSMIT_POWER, "list", listOf("ultraLow", "low", "medium", "high"), DEFAULT_BLE_TRANSMIT_POWER)
val advertiseMode = getSetting(context, bleTransmitter, SETTING_BLE_ADVERTISE_MODE, "list", listOf("lowPower", "balanced", "lowLatency"), DEFAULT_BLE_ADVERTISE_MODE)
bleTransmitterDevice.restartRequired = false
if (bleTransmitterDevice.uuid != uuid || bleTransmitterDevice.major != major ||
bleTransmitterDevice.minor != minor || bleTransmitterDevice.transmitPowerSetting != transmitPower ||
bleTransmitterDevice.advertiseModeSetting != advertiseMode || bleTransmitterDevice.transmitRequested != transmitActive ||
bleTransmitterDevice.measuredPowerSetting != measuredPower || priorBluetoothStateEnabled != isBtOn(context)
) {
bleTransmitterDevice.restartRequired = true
}
// stash the current BT state to help us know if we need to restart if BT state turns from off to on
priorBluetoothStateEnabled = isBtOn(context)
bleTransmitterDevice.uuid = uuid
bleTransmitterDevice.major = major
bleTransmitterDevice.minor = minor
bleTransmitterDevice.transmitPowerSetting = transmitPower
bleTransmitterDevice.measuredPowerSetting = measuredPower
bleTransmitterDevice.advertiseModeSetting = advertiseMode
bleTransmitterDevice.transmitRequested = transmitActive
}
private fun updateBLESensor(context: Context) {
// get device details from settings
updateBLEDevice(context)
// sensor disabled, stop transmitting if we have been
if (!isEnabled(context, bleTransmitter.id)) {
TransmitterManager.stopTransmitting(bleTransmitterDevice)
return
}
// transmit when BT is on, if we are not already transmitting, or details have changed
if (isBtOn(context)) {
if (bleTransmitterDevice.transmitRequested && (!bleTransmitterDevice.transmitting || bleTransmitterDevice.restartRequired)) {
TransmitterManager.startTransmitting(context, bleTransmitterDevice)
}
}
// BT off, or TransmitToggled off, stop transmitting if we have been
if (!isBtOn(context) || !bleTransmitterDevice.transmitRequested) {
TransmitterManager.stopTransmitting(bleTransmitterDevice)
}
val state = if (isBtOn(context)) bleTransmitterDevice.state else "Bluetooth is turned off"
val icon = if (bleTransmitterDevice.transmitting) "mdi:bluetooth" else "mdi:bluetooth-off"
onSensorUpdated(
context,
bleTransmitter,
state,
icon,
mapOf(
"id" to bleTransmitterDevice.uuid + "-" + bleTransmitterDevice.major + "-" + bleTransmitterDevice.minor,
"Transmitting power" to bleTransmitterDevice.transmitPowerSetting,
"Advertise mode" to bleTransmitterDevice.advertiseModeSetting,
"Measured power" to bleTransmitterDevice.measuredPowerSetting
)
)
}
}