Show the charging state on the Battery Level sensor's icon (#2661)

* Show the charging state on the Battery Level sensor's icon.
A different icon will be shown for wired and wireless charging.
From now on, changing only the icon (but not the state) of a sensor will trigger sending an update to the server.
A new DB property called last_sent_icon is added, thus a DB migration is needed.

* Merge SensorDao.updateLastSendState(…) and updateLastSentIcon(…) methods to updateLastSentStateAndIcon(…)
This commit is contained in:
Márton Maráz 2022-07-09 20:19:23 +02:00 committed by GitHub
parent 0883ada422
commit 4bf88ac907
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 739 additions and 15 deletions

View file

@ -0,0 +1,712 @@
{
"formatVersion": 1,
"database": {
"version": 30,
"identityHash": "3840f1ef042b0b43b370b24a4ff2fb76",
"entities": [
{
"tableName": "sensor_attributes",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, `value_type` TEXT NOT NULL, PRIMARY KEY(`sensor_id`, `name`))",
"fields": [
{
"fieldPath": "sensorId",
"columnName": "sensor_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "value_type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"sensor_id",
"name"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "Authentication_List",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `Username` TEXT NOT NULL, `Password` TEXT NOT NULL, PRIMARY KEY(`host`))",
"fields": [
{
"fieldPath": "host",
"columnName": "host",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "Username",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "Password",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"host"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "sensors",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `registered` INTEGER DEFAULT NULL, `state` TEXT NOT NULL, `last_sent_state` TEXT NOT NULL, `last_sent_icon` TEXT NOT NULL DEFAULT '', `state_type` TEXT NOT NULL, `type` TEXT NOT NULL, `icon` TEXT NOT NULL, `name` TEXT NOT NULL, `device_class` TEXT, `unit_of_measurement` TEXT, `state_class` TEXT, `entity_category` TEXT, `core_registration` TEXT, `app_registration` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "registered",
"columnName": "registered",
"affinity": "INTEGER",
"notNull": false,
"defaultValue": "NULL"
},
{
"fieldPath": "state",
"columnName": "state",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastSentState",
"columnName": "last_sent_state",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastSentIcon",
"columnName": "last_sent_icon",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "stateType",
"columnName": "state_type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "icon",
"columnName": "icon",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "deviceClass",
"columnName": "device_class",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "unitOfMeasurement",
"columnName": "unit_of_measurement",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "stateClass",
"columnName": "state_class",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "entityCategory",
"columnName": "entity_category",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coreRegistration",
"columnName": "core_registration",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "appRegistration",
"columnName": "app_registration",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "sensor_settings",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, `value_type` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `entries` TEXT NOT NULL, PRIMARY KEY(`sensor_id`, `name`))",
"fields": [
{
"fieldPath": "sensorId",
"columnName": "sensor_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "value_type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "entries",
"columnName": "entries",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"sensor_id",
"name"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "button_widgets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `icon_id` INTEGER NOT NULL, `domain` TEXT NOT NULL, `service` TEXT NOT NULL, `service_data` TEXT NOT NULL, `label` TEXT, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "iconId",
"columnName": "icon_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domain",
"columnName": "domain",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "service",
"columnName": "service",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceData",
"columnName": "service_data",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "backgroundType",
"columnName": "background_type",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'DAYNIGHT'"
},
{
"fieldPath": "textColor",
"columnName": "text_color",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "camera_widgets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `entityId` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "entityId",
"columnName": "entityId",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "mediaplayctrls_widgets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `entityId` TEXT NOT NULL, `label` TEXT, `showSkip` INTEGER NOT NULL, `showSeek` INTEGER NOT NULL, `showVolume` INTEGER NOT NULL, `showSource` INTEGER NOT NULL DEFAULT false, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "entityId",
"columnName": "entityId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "showSkip",
"columnName": "showSkip",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "showSeek",
"columnName": "showSeek",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "showVolume",
"columnName": "showVolume",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "showSource",
"columnName": "showSource",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "backgroundType",
"columnName": "background_type",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'DAYNIGHT'"
},
{
"fieldPath": "textColor",
"columnName": "text_color",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "static_widget",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `attribute_ids` TEXT, `label` TEXT, `text_size` REAL NOT NULL, `state_separator` TEXT NOT NULL, `attribute_separator` TEXT NOT NULL, `last_update` TEXT NOT NULL, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "entityId",
"columnName": "entity_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "attributeIds",
"columnName": "attribute_ids",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "textSize",
"columnName": "text_size",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "stateSeparator",
"columnName": "state_separator",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "attributeSeparator",
"columnName": "attribute_separator",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUpdate",
"columnName": "last_update",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "backgroundType",
"columnName": "background_type",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'DAYNIGHT'"
},
{
"fieldPath": "textColor",
"columnName": "text_color",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "template_widgets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `template` TEXT NOT NULL, `text_size` REAL NOT NULL DEFAULT 12.0, `last_update` TEXT NOT NULL, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "template",
"columnName": "template",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "textSize",
"columnName": "text_size",
"affinity": "REAL",
"notNull": true,
"defaultValue": "12.0"
},
{
"fieldPath": "lastUpdate",
"columnName": "last_update",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "backgroundType",
"columnName": "background_type",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'DAYNIGHT'"
},
{
"fieldPath": "textColor",
"columnName": "text_color",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "notification_history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `received` INTEGER NOT NULL, `message` TEXT NOT NULL, `data` TEXT NOT NULL, `source` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "received",
"columnName": "received",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "message",
"columnName": "message",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "data",
"columnName": "data",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "qs_tiles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `tileId` TEXT NOT NULL, `icon_id` INTEGER, `entityId` TEXT NOT NULL, `label` TEXT NOT NULL, `subtitle` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tileId",
"columnName": "tileId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "iconId",
"columnName": "icon_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "entityId",
"columnName": "entityId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "subtitle",
"columnName": "subtitle",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "favorites",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `position` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "position",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "entityStateComplications",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `entityId` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "entityId",
"columnName": "entityId",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "settings",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `websocketSetting` TEXT NOT NULL, `sensorUpdateFrequency` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "websocketSetting",
"columnName": "websocketSetting",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sensorUpdateFrequency",
"columnName": "sensorUpdateFrequency",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3840f1ef042b0b43b370b24a4ff2fb76')"
]
}
}

View file

@ -126,15 +126,23 @@ class BatterySensorManager : SensorManager {
private fun updateBatteryLevel(context: Context, intent: Intent) {
if (!isEnabled(context, batteryLevel.id))
return
val percentage: Int = getBatteryPercentage(intent)
var icon = "mdi:battery"
val batteryStep: Int = percentage / 10
icon += when (batteryStep) {
0 -> "-outline"
10 -> ""
else -> "-${batteryStep}0"
val percentage = getBatteryPercentage(intent)
val baseIcon = when (getChargingStatus(intent)) {
"charging", "full" -> when (getChargerType(intent)) {
"wireless" -> "mdi:battery-charging-wireless"
else -> "mdi:battery-charging"
}
else -> "mdi:battery"
}
val roundedPercentage = (percentage / 10) * 10
val icon = when (percentage) {
in 0..100 -> baseIcon + when (percentage) {
in 0..9 -> "-outline"
100 -> ""
else -> "-$roundedPercentage"
}
else -> "mdi:battery-unknown"
}
onSensorUpdated(

View file

@ -273,7 +273,7 @@ abstract class SensorReceiverBase : BroadcastReceiver() {
Log.e(tag, "Issue re-registering sensor ${basicSensor.id}", e)
}
}
if (canBeRegistered && sensor.enabled && sensor.registered != null && sensor.state != sensor.lastSentState) {
if (canBeRegistered && sensor.enabled && sensor.registered != null && (sensor.state != sensor.lastSentState || sensor.icon != sensor.lastSentIcon)) {
enabledRegistrations.add(fullSensor.toSensorRegistration(basicSensor))
}
}
@ -284,7 +284,7 @@ abstract class SensorReceiverBase : BroadcastReceiver() {
try {
success = integrationUseCase.updateSensors(enabledRegistrations.toTypedArray())
enabledRegistrations.forEach {
sensorDao.updateLastSendState(it.uniqueId, it.state.toString())
sensorDao.updateLastSentStateAndIcon(it.uniqueId, it.state.toString(), it.icon)
}
} catch (e: Exception) {
Log.e(tag, "Exception while updating sensors.", e)
@ -297,6 +297,7 @@ abstract class SensorReceiverBase : BroadcastReceiver() {
if (sensor != null) {
sensor.registered = null
sensor.lastSentState = ""
sensor.lastSentIcon = ""
sensorDao.update(sensor)
}
}

View file

@ -74,13 +74,14 @@ import io.homeassistant.companion.android.common.R as commonR
EntityStateComplications::class,
Setting::class
],
version = 29,
version = 30,
autoMigrations = [
AutoMigration(from = 24, to = 25),
AutoMigration(from = 25, to = 26),
AutoMigration(from = 26, to = 27),
AutoMigration(from = 27, to = 28, spec = AppDatabase.Companion.Migration27to28::class),
AutoMigration(from = 28, to = 29)
AutoMigration(from = 28, to = 29),
AutoMigration(from = 29, to = 30),
]
)
@TypeConverters(

View file

@ -17,6 +17,8 @@ data class Sensor(
var state: String,
@ColumnInfo(name = "last_sent_state")
var lastSentState: String = "",
@ColumnInfo(name = "last_sent_icon", defaultValue = "")
var lastSentIcon: String = "",
@ColumnInfo(name = "state_type")
var stateType: String = "",
@ColumnInfo(name = "type")

View file

@ -72,8 +72,8 @@ interface SensorDao {
@Query("UPDATE sensor_settings SET value = :value WHERE sensor_id = :sensorId AND name = :settingName")
fun updateSettingValue(sensorId: String, settingName: String, value: String)
@Query("UPDATE sensors SET last_sent_state = :state WHERE id = :sensorId")
suspend fun updateLastSendState(sensorId: String, state: String)
@Query("UPDATE sensors SET last_sent_state = :state, last_sent_icon = :icon WHERE id = :sensorId")
suspend fun updateLastSentStateAndIcon(sensorId: String, state: String, icon: String)
@Query("SELECT COUNT(id) FROM sensors WHERE enabled = 1")
suspend fun getEnabledCount(): Int?
@ -85,7 +85,7 @@ interface SensorDao {
async {
val sensorEntity = get(sensorId)
if (sensorEntity != null) {
update(sensorEntity.copy(enabled = enabled, lastSentState = ""))
update(sensorEntity.copy(enabled = enabled, lastSentState = "", lastSentIcon = ""))
} else {
add(Sensor(sensorId, enabled, state = ""))
}