mirror of
https://github.com/home-assistant/android
synced 2024-09-19 16:11:37 +00:00
Fix a race condition Sensor Update (#907)
* Fix a race condition between a sensor updating and sending the data to Home Assistant. * ktlint * UI enhancements + force update if re-enabled.
This commit is contained in:
parent
01d33067e7
commit
2c990e2c7b
|
@ -64,7 +64,8 @@ abstract class AppDatabase : RoomDatabase() {
|
||||||
MIGRATION_5_6,
|
MIGRATION_5_6,
|
||||||
MIGRATION_6_7,
|
MIGRATION_6_7,
|
||||||
MIGRATION_7_8,
|
MIGRATION_7_8,
|
||||||
MIGRATION_8_9
|
MIGRATION_8_9,
|
||||||
|
MIGRATION_9_10
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -168,5 +169,30 @@ abstract class AppDatabase : RoomDatabase() {
|
||||||
database.execSQL("ALTER TABLE `sensors` ADD `state_changed` INTEGER NOT NULL DEFAULT ''")
|
database.execSQL("ALTER TABLE `sensors` ADD `state_changed` INTEGER NOT NULL DEFAULT ''")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private val MIGRATION_9_10 = object : Migration(9, 10) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
val cursor = database.query("SELECT * FROM sensors")
|
||||||
|
val sensors = mutableListOf<ContentValues>()
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
sensors.add(ContentValues().also {
|
||||||
|
it.put("id", cursor.getString(cursor.getColumnIndex("id")))
|
||||||
|
it.put("enabled", cursor.getInt(cursor.getColumnIndex("enabled")))
|
||||||
|
it.put("registered", cursor.getInt(cursor.getColumnIndex("registered")))
|
||||||
|
it.put("state", "")
|
||||||
|
it.put("last_sent_state", "")
|
||||||
|
it.put("state_type", "")
|
||||||
|
it.put("type", "")
|
||||||
|
it.put("icon", "")
|
||||||
|
it.put("name", "")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
cursor.close()
|
||||||
|
database.execSQL("DROP TABLE IF EXISTS `sensors`")
|
||||||
|
database.execSQL("CREATE TABLE IF NOT EXISTS `sensors` (`id` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `registered` INTEGER NOT NULL, `state` TEXT NOT NULL, `last_sent_state` TEXT NOT NULL, `state_type` TEXT NOT NULL, `type` TEXT NOT NULL, `icon` TEXT NOT NULL, `name` TEXT NOT NULL, `device_class` TEXT, `unit_of_measurement` TEXT, PRIMARY KEY(`id`))")
|
||||||
|
sensors.forEach {
|
||||||
|
database.insert("sensors", OnConflictStrategy.REPLACE, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,10 @@ data class Sensor(
|
||||||
var enabled: Boolean,
|
var enabled: Boolean,
|
||||||
@ColumnInfo(name = "registered")
|
@ColumnInfo(name = "registered")
|
||||||
var registered: Boolean,
|
var registered: Boolean,
|
||||||
@ColumnInfo(name = "state_changed")
|
|
||||||
var stateChanged: Boolean,
|
|
||||||
@ColumnInfo(name = "state")
|
@ColumnInfo(name = "state")
|
||||||
var state: String,
|
var state: String,
|
||||||
|
@ColumnInfo(name = "last_sent_state")
|
||||||
|
var lastSentState: String = "",
|
||||||
@ColumnInfo(name = "state_type")
|
@ColumnInfo(name = "state_type")
|
||||||
var stateType: String = "",
|
var stateType: String = "",
|
||||||
@ColumnInfo(name = "type")
|
@ColumnInfo(name = "type")
|
||||||
|
|
|
@ -28,4 +28,7 @@ interface SensorDao {
|
||||||
|
|
||||||
@Query("DELETE FROM sensor_attributes WHERE sensor_id = :sensorId")
|
@Query("DELETE FROM sensor_attributes WHERE sensor_id = :sensorId")
|
||||||
fun clearAttributes(sensorId: String)
|
fun clearAttributes(sensorId: String)
|
||||||
|
|
||||||
|
@Query("UPDATE sensors SET last_sent_state = :state WHERE id = :sensorId")
|
||||||
|
fun updateLastSendState(sensorId: String, state: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,7 +251,6 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
|
||||||
uniqueId,
|
uniqueId,
|
||||||
isChecked,
|
isChecked,
|
||||||
false,
|
false,
|
||||||
true,
|
|
||||||
""
|
""
|
||||||
)
|
)
|
||||||
sensorDao.add(sensor)
|
sensorDao.add(sensor)
|
||||||
|
|
|
@ -69,7 +69,6 @@ class SensorDetailFragment(
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSensorEntity(isEnabled)
|
updateSensorEntity(isEnabled)
|
||||||
this@SensorDetailFragment.refreshSensorData()
|
|
||||||
|
|
||||||
return@setOnPreferenceChangeListener true
|
return@setOnPreferenceChangeListener true
|
||||||
}
|
}
|
||||||
|
@ -115,22 +114,24 @@ class SensorDetailFragment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
findPreference<Preference>("device_class")?.let {
|
findPreference<Preference>("device_class")?.let {
|
||||||
if (sensorData.deviceClass == null)
|
if (sensorData.enabled && sensorData.deviceClass != null) {
|
||||||
it.isVisible = false
|
|
||||||
else
|
|
||||||
it.summary = sensorData.deviceClass
|
it.summary = sensorData.deviceClass
|
||||||
|
it.isVisible = true
|
||||||
|
} else {
|
||||||
|
it.isVisible = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
findPreference<Preference>("icon")?.let {
|
findPreference<Preference>("icon")?.let {
|
||||||
if (sensorData.icon == "")
|
if (sensorData.enabled && sensorData.icon != "") {
|
||||||
it.isVisible = false
|
|
||||||
else
|
|
||||||
it.summary = sensorData.icon
|
it.summary = sensorData.icon
|
||||||
|
it.isVisible = true
|
||||||
|
} else {
|
||||||
|
it.isVisible = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findPreference<PreferenceCategory>("attributes")?.let {
|
findPreference<PreferenceCategory>("attributes")?.let {
|
||||||
if (attributes.isNullOrEmpty())
|
if (sensorData.enabled && !attributes.isNullOrEmpty()) {
|
||||||
it.isVisible = false
|
|
||||||
else {
|
|
||||||
attributes.forEach { attribute ->
|
attributes.forEach { attribute ->
|
||||||
val key = "attribute_${attribute.name}"
|
val key = "attribute_${attribute.name}"
|
||||||
val pref = findPreference(key) ?: Preference(requireContext())
|
val pref = findPreference(key) ?: Preference(requireContext())
|
||||||
|
@ -143,7 +144,9 @@ class SensorDetailFragment(
|
||||||
if (!it.contains(pref))
|
if (!it.contains(pref))
|
||||||
it.addPreference(pref)
|
it.addPreference(pref)
|
||||||
}
|
}
|
||||||
}
|
it.isVisible = true
|
||||||
|
} else
|
||||||
|
it.isVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,9 +156,10 @@ class SensorDetailFragment(
|
||||||
var sensorEntity = sensorDao.get(basicSensor.id)
|
var sensorEntity = sensorDao.get(basicSensor.id)
|
||||||
if (sensorEntity != null) {
|
if (sensorEntity != null) {
|
||||||
sensorEntity.enabled = isEnabled
|
sensorEntity.enabled = isEnabled
|
||||||
|
sensorEntity.lastSentState = ""
|
||||||
sensorDao.update(sensorEntity)
|
sensorDao.update(sensorEntity)
|
||||||
} else {
|
} else {
|
||||||
sensorEntity = Sensor(basicSensor.id, isEnabled, false, true, "")
|
sensorEntity = Sensor(basicSensor.id, isEnabled, false, "")
|
||||||
sensorDao.add(sensorEntity)
|
sensorDao.add(sensorEntity)
|
||||||
}
|
}
|
||||||
refreshSensorData()
|
refreshSensorData()
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Process.myPid
|
import android.os.Process.myPid
|
||||||
import android.os.Process.myUid
|
import android.os.Process.myUid
|
||||||
import android.util.Log
|
|
||||||
import io.homeassistant.companion.android.R
|
import io.homeassistant.companion.android.R
|
||||||
import io.homeassistant.companion.android.database.AppDatabase
|
import io.homeassistant.companion.android.database.AppDatabase
|
||||||
import io.homeassistant.companion.android.database.sensor.Attribute
|
import io.homeassistant.companion.android.database.sensor.Attribute
|
||||||
|
@ -40,7 +39,7 @@ interface SensorManager {
|
||||||
|
|
||||||
// If we haven't created the entity yet do so and default to enabled if required
|
// If we haven't created the entity yet do so and default to enabled if required
|
||||||
if (sensor == null) {
|
if (sensor == null) {
|
||||||
sensor = Sensor(sensorId, permission && enabledByDefault, false, true, "")
|
sensor = Sensor(sensorId, permission && enabledByDefault, false, "")
|
||||||
sensorDao.add(sensor)
|
sensorDao.add(sensor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +67,6 @@ interface SensorManager {
|
||||||
) {
|
) {
|
||||||
val sensorDao = AppDatabase.getInstance(context).sensorDao()
|
val sensorDao = AppDatabase.getInstance(context).sensorDao()
|
||||||
val sensor = sensorDao.get(basicSensor.id) ?: return
|
val sensor = sensorDao.get(basicSensor.id) ?: return
|
||||||
Log.d("SensorManager", "Old sensor state ${sensor.state} compared to new state $state for ${sensor.name}")
|
|
||||||
sensor.stateChanged = sensor.stateChanged || (sensor.state != state.toString())
|
|
||||||
sensor.id = basicSensor.id
|
sensor.id = basicSensor.id
|
||||||
sensor.state = state.toString()
|
sensor.state = state.toString()
|
||||||
sensor.stateType = when (state) {
|
sensor.stateType = when (state) {
|
||||||
|
|
|
@ -131,7 +131,7 @@ class SensorReceiver : BroadcastReceiver() {
|
||||||
Log.e(TAG, "Issue registering sensor: ${reg.uniqueId}", e)
|
Log.e(TAG, "Issue registering sensor: ${reg.uniqueId}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sensor?.enabled == true && fullSensor != null && sensor?.registered && sensor?.stateChanged) {
|
if (sensor?.enabled == true && sensor.registered && sensor.state != sensor.lastSentState) {
|
||||||
enabledRegistrations.add(fullSensor.toSensorRegistration())
|
enabledRegistrations.add(fullSensor.toSensorRegistration())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,9 @@ class SensorReceiver : BroadcastReceiver() {
|
||||||
var success = false
|
var success = false
|
||||||
try {
|
try {
|
||||||
success = integrationUseCase.updateSensors(enabledRegistrations.toTypedArray())
|
success = integrationUseCase.updateSensors(enabledRegistrations.toTypedArray())
|
||||||
|
enabledRegistrations.forEach {
|
||||||
|
sensorDao.updateLastSendState(it.uniqueId, it.state.toString())
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Exception while updating sensors.", e)
|
Log.e(TAG, "Exception while updating sensors.", e)
|
||||||
}
|
}
|
||||||
|
@ -151,7 +154,7 @@ class SensorReceiver : BroadcastReceiver() {
|
||||||
val sensor = sensorDao.get(it.uniqueId)
|
val sensor = sensorDao.get(it.uniqueId)
|
||||||
if (sensor != null) {
|
if (sensor != null) {
|
||||||
sensor.registered = false
|
sensor.registered = false
|
||||||
sensor.stateChanged = false
|
sensor.lastSentState = ""
|
||||||
sensorDao.update(sensor)
|
sensorDao.update(sensor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue