diff --git a/common/schemas/io.homeassistant.companion.android.database.AppDatabase/46.json b/common/schemas/io.homeassistant.companion.android.database.AppDatabase/46.json
new file mode 100644
index 00000000000..57f020cc0ef
--- /dev/null
+++ b/common/schemas/io.homeassistant.companion.android.database.AppDatabase/46.json
@@ -0,0 +1,1121 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 46,
+ "identityHash": "6e80c503b23cc2d48e240d72a1d9e08d",
+ "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": {
+ "autoGenerate": false,
+ "columnNames": [
+ "sensor_id",
+ "name"
+ ]
+ },
+ "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": {
+ "autoGenerate": false,
+ "columnNames": [
+ "host"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "sensors",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `enabled` INTEGER NOT NULL, `registered` INTEGER DEFAULT NULL, `state` TEXT NOT NULL, `last_sent_state` TEXT DEFAULT NULL, `last_sent_icon` TEXT DEFAULT 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, `state_class` TEXT, `entity_category` TEXT, `core_registration` TEXT, `app_registration` TEXT, PRIMARY KEY(`id`, `server_id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "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": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "lastSentIcon",
+ "columnName": "last_sent_icon",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "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": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id",
+ "server_id"
+ ]
+ },
+ "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": {
+ "autoGenerate": false,
+ "columnNames": [
+ "sensor_id",
+ "name"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "button_widgets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `icon_name` TEXT 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, `require_authentication` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "iconName",
+ "columnName": "icon_name",
+ "affinity": "TEXT",
+ "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
+ },
+ {
+ "fieldPath": "requireAuthentication",
+ "columnName": "require_authentication",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "camera_widgets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `entity_id` TEXT NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "media_player_controls_widgets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `entity_id` TEXT NOT NULL, `label` TEXT, `show_skip` INTEGER NOT NULL, `show_seek` INTEGER NOT NULL, `show_volume` INTEGER NOT NULL, `show_source` 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": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "label",
+ "columnName": "label",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "showSkip",
+ "columnName": "show_skip",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showSeek",
+ "columnName": "show_seek",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showVolume",
+ "columnName": "show_volume",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showSource",
+ "columnName": "show_source",
+ "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": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "static_widget",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `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, `tap_action` TEXT NOT NULL DEFAULT 'REFRESH', `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": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "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": "tapAction",
+ "columnName": "tap_action",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'REFRESH'"
+ },
+ {
+ "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": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "template_widgets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `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": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "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": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "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, `server_id` INTEGER)",
+ "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
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "location_history",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `created` INTEGER NOT NULL, `trigger` TEXT NOT NULL, `result` TEXT NOT NULL, `latitude` REAL, `longitude` REAL, `location_name` TEXT, `accuracy` INTEGER, `data` TEXT, `server_id` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trigger",
+ "columnName": "trigger",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "result",
+ "columnName": "result",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "latitude",
+ "columnName": "latitude",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "longitude",
+ "columnName": "longitude",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "locationName",
+ "columnName": "location_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "accuracy",
+ "columnName": "accuracy",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "qs_tiles",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `tile_id` TEXT NOT NULL, `added` INTEGER NOT NULL DEFAULT 1, `server_id` INTEGER NOT NULL DEFAULT 0, `icon_name` TEXT, `entity_id` TEXT NOT NULL, `label` TEXT NOT NULL, `subtitle` TEXT, `should_vibrate` INTEGER NOT NULL DEFAULT 0, `auth_required` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "tileId",
+ "columnName": "tile_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "added",
+ "columnName": "added",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "iconName",
+ "columnName": "icon_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "label",
+ "columnName": "label",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subtitle",
+ "columnName": "subtitle",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "shouldVibrate",
+ "columnName": "should_vibrate",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "authRequired",
+ "columnName": "auth_required",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "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": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite_cache",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `friendly_name` TEXT NOT NULL, `icon` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "friendlyName",
+ "columnName": "friendly_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "icon",
+ "columnName": "icon",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "camera_tiles",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `entity_id` TEXT, `refresh_interval` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "refreshInterval",
+ "columnName": "refresh_interval",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "entity_state_complications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `show_title` INTEGER NOT NULL DEFAULT 1, `show_unit` INTEGER NOT NULL DEFAULT 0, `forward_taps` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showTitle",
+ "columnName": "show_title",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "showUnit",
+ "columnName": "show_unit",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "forwardTaps",
+ "columnName": "forward_taps",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "servers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `_name` TEXT NOT NULL, `name_override` TEXT, `_version` TEXT, `list_order` INTEGER NOT NULL, `device_name` TEXT, `external_url` TEXT NOT NULL, `internal_url` TEXT, `cloud_url` TEXT, `webhook_id` TEXT, `secret` TEXT, `cloudhook_url` TEXT, `use_cloud` INTEGER NOT NULL, `internal_ssids` TEXT NOT NULL, `prioritize_internal` INTEGER NOT NULL, `access_token` TEXT, `refresh_token` TEXT, `token_expiration` INTEGER, `token_type` TEXT, `install_id` TEXT, `user_id` TEXT, `user_name` TEXT, `user_is_owner` INTEGER, `user_is_admin` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "_name",
+ "columnName": "_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nameOverride",
+ "columnName": "name_override",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "_version",
+ "columnName": "_version",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "listOrder",
+ "columnName": "list_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceName",
+ "columnName": "device_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.externalUrl",
+ "columnName": "external_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "connection.internalUrl",
+ "columnName": "internal_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.cloudUrl",
+ "columnName": "cloud_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.webhookId",
+ "columnName": "webhook_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.secret",
+ "columnName": "secret",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.cloudhookUrl",
+ "columnName": "cloudhook_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.useCloud",
+ "columnName": "use_cloud",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "connection.internalSsids",
+ "columnName": "internal_ssids",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "connection.prioritizeInternal",
+ "columnName": "prioritize_internal",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "session.accessToken",
+ "columnName": "access_token",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "session.refreshToken",
+ "columnName": "refresh_token",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "session.tokenExpiration",
+ "columnName": "token_expiration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "session.tokenType",
+ "columnName": "token_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "session.installId",
+ "columnName": "install_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "user.id",
+ "columnName": "user_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "user.name",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "user.isOwner",
+ "columnName": "user_is_owner",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "user.isAdmin",
+ "columnName": "user_is_admin",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "settings",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `websocket_setting` TEXT NOT NULL, `sensor_update_frequency` TEXT NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "websocketSetting",
+ "columnName": "websocket_setting",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sensorUpdateFrequency",
+ "columnName": "sensor_update_frequency",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "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, '6e80c503b23cc2d48e240d72a1d9e08d')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt b/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt
index f4089a77486..2692c887760 100644
--- a/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt
+++ b/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt
@@ -97,7 +97,7 @@ import io.homeassistant.companion.android.common.R as commonR
Server::class,
Setting::class
],
- version = 45,
+ version = 46,
autoMigrations = [
AutoMigration(from = 24, to = 25),
AutoMigration(from = 25, to = 26),
@@ -118,7 +118,8 @@ import io.homeassistant.companion.android.common.R as commonR
AutoMigration(from = 41, to = 42),
AutoMigration(from = 42, to = 43),
AutoMigration(from = 43, to = 44),
- AutoMigration(from = 44, to = 45)
+ AutoMigration(from = 44, to = 45),
+ AutoMigration(from = 45, to = 46)
]
)
@TypeConverters(
@@ -193,7 +194,7 @@ abstract class AppDatabase : RoomDatabase() {
MIGRATION_21_22,
MIGRATION_22_23,
MIGRATION_23_24,
- Migration40to41(context.assets)
+ Migration40to41(context.assets),
)
.fallbackToDestructiveMigration()
.build()
diff --git a/common/src/main/java/io/homeassistant/companion/android/database/wear/EntityStateComplications.kt b/common/src/main/java/io/homeassistant/companion/android/database/wear/EntityStateComplications.kt
index 354f731e820..7908281c01b 100644
--- a/common/src/main/java/io/homeassistant/companion/android/database/wear/EntityStateComplications.kt
+++ b/common/src/main/java/io/homeassistant/companion/android/database/wear/EntityStateComplications.kt
@@ -17,5 +17,7 @@ data class EntityStateComplications(
@ColumnInfo(name = "show_title", defaultValue = "1")
val showTitle: Boolean,
@ColumnInfo(name = "show_unit", defaultValue = "0")
- val showUnit: Boolean
+ val showUnit: Boolean,
+ @ColumnInfo(name = "forward_taps", defaultValue = "0")
+ val forwardTaps: Boolean
)
diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml
index 95179839823..1478b2ddd8e 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -795,6 +795,7 @@
Show and share logs
Show entity name
Show entity unit
+ Toggle entity when tapped
Sign in on phone
%1$s decreased
%1$s increased
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt
index 621ab488c5c..9adb1959ae9 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt
@@ -60,7 +60,8 @@ class ComplicationConfigViewModel @Inject constructor(
private set
var entityShowUnit by mutableStateOf(true)
private set
-
+ var entityForwardTaps by mutableStateOf(true)
+ private set
init {
loadEntities()
}
@@ -74,6 +75,7 @@ class ComplicationConfigViewModel @Inject constructor(
selectedEntity = SimplifiedEntity(entityId = it.entityId)
entityShowTitle = it.showTitle
entityShowUnit = it.showUnit
+ entityForwardTaps = it.forwardTaps
if (loadingState == LoadingState.READY) {
updateSelectedEntity()
}
@@ -160,9 +162,13 @@ class ComplicationConfigViewModel @Inject constructor(
entityShowUnit = show
}
+ fun setForwardTaps(show: Boolean) {
+ entityForwardTaps = show
+ }
+
fun addEntityStateComplication(id: Int, entity: SimplifiedEntity) {
viewModelScope.launch {
- entityStateComplicationsDao.add(EntityStateComplications(id, entity.entityId, entityShowTitle, entityShowUnit))
+ entityStateComplicationsDao.add(EntityStateComplications(id, entity.entityId, entityShowTitle, entityShowUnit, entityForwardTaps))
}
}
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt
index 8aa137d30a3..6650da2fee8 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt
@@ -5,9 +5,11 @@ import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.util.Log
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_COMPLICATION_ID
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUpdateRequester
import dagger.hilt.android.AndroidEntryPoint
+import io.homeassistant.companion.android.common.data.integration.onPressed
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.conversation.ConversationActivity
import kotlinx.coroutines.CoroutineScope
@@ -28,7 +30,11 @@ class ComplicationReceiver : BroadcastReceiver() {
try {
when (intent.action) {
- UPDATE_COMPLICATION -> updateComplication(context, intent.getIntExtra(EXTRA_ID, -1))
+ TOGGLE_COMPLICATION -> toggleComplication(
+ context,
+ intent.getIntExtra(EXTRA_COMPLICATION_ID, -1),
+ intent.getStringExtra(EXTRA_ENTITY_ID)
+ )
Intent.ACTION_SCREEN_ON -> onScreenOn(context)
}
} finally {
@@ -36,15 +42,32 @@ class ComplicationReceiver : BroadcastReceiver() {
}
}
- private fun updateComplication(context: Context, id: Int) {
+ private fun toggleComplication(context: Context, complicationId: Int, entityId: String?) {
scope.launch {
+ // Forward tap to entity if entityId is specified.
+ if (entityId != null) {
+ val entity = try {
+ serverManager.integrationRepository().getEntity(entityId)
+ ?: return@launch
+ } catch (t: Throwable) {
+ Log.e(
+ EntityStateDataSourceService.TAG,
+ "Unable to get entity for $entityId: ${t.message}"
+ )
+ return@launch
+ }
+
+ // Press!
+ entity.onPressed(serverManager.integrationRepository())
+ }
+
// Request an update for the complication that has just been toggled.
ComplicationDataSourceUpdateRequester
.create(
context = context,
complicationDataSourceComponent = ComponentName(context, EntityStateDataSourceService::class.java)
)
- .requestUpdate(id)
+ .requestUpdate(complicationId)
}
}
@@ -67,8 +90,9 @@ class ComplicationReceiver : BroadcastReceiver() {
companion object {
private const val TAG = "ComplicationReceiver"
- const val UPDATE_COMPLICATION = "update_complication"
- private const val EXTRA_ID = "complication_instance_id"
+ const val TOGGLE_COMPLICATION = "toggle_complication"
+ private const val EXTRA_COMPLICATION_ID = "complication_instance_id"
+ private const val EXTRA_ENTITY_ID = "entity_id"
/**
* Returns a pending intent, suitable for use as a tap intent, that causes a complication to be
@@ -76,11 +100,13 @@ class ComplicationReceiver : BroadcastReceiver() {
*/
fun getComplicationToggleIntent(
context: Context,
- complicationInstanceId: Int
+ complicationInstanceId: Int,
+ entityId: String?
): PendingIntent {
val intent = Intent(context, ComplicationReceiver::class.java).apply {
- action = UPDATE_COMPLICATION
- putExtra(EXTRA_ID, complicationInstanceId)
+ action = TOGGLE_COMPLICATION
+ putExtra(EXTRA_COMPLICATION_ID, complicationInstanceId)
+ putExtra(EXTRA_ENTITY_ID, entityId)
}
// Pass complicationId as the requestCode to ensure that different complications get
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt
index dc7a8b974d3..ba7c7fc3d2e 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt
@@ -89,7 +89,11 @@ class EntityStateDataSourceService : SuspendingComplicationDataSourceService() {
val contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description)).build()
val monochromaticImage = MonochromaticImage.Builder(Icon.createWithBitmap(iconBitmap)).build()
- val tapAction = ComplicationReceiver.getComplicationToggleIntent(this, request.complicationInstanceId)
+ val tapAction = ComplicationReceiver.getComplicationToggleIntent(
+ this,
+ request.complicationInstanceId,
+ if (settings.forwardTaps) entity.entityId else null
+ )
return when (request.complicationType) {
ComplicationType.SHORT_TEXT -> {
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/views/ComplicationConfigMainView.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/views/ComplicationConfigMainView.kt
index ccf5b42b4aa..54ac6347fc4 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/views/ComplicationConfigMainView.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/views/ComplicationConfigMainView.kt
@@ -57,12 +57,14 @@ fun LoadConfigView(
entity = complicationConfigViewModel.selectedEntity,
showTitle = complicationConfigViewModel.entityShowTitle,
showUnit = complicationConfigViewModel.entityShowUnit,
+ forwardTaps = complicationConfigViewModel.entityForwardTaps,
loadingState = complicationConfigViewModel.loadingState,
onChooseEntityClicked = {
swipeDismissableNavController.navigate(SCREEN_CHOOSE_ENTITY)
},
onShowTitleClicked = complicationConfigViewModel::setShowTitle,
onShowUnitClicked = complicationConfigViewModel::setShowUnit,
+ onForwardTapsClicked = complicationConfigViewModel::setForwardTaps,
onAcceptClicked = onAcceptClicked
)
}
@@ -88,10 +90,12 @@ fun MainConfigView(
entity: SimplifiedEntity?,
showTitle: Boolean,
showUnit: Boolean,
+ forwardTaps: Boolean,
loadingState: ComplicationConfigViewModel.LoadingState,
onChooseEntityClicked: () -> Unit,
onShowTitleClicked: (Boolean) -> Unit,
onShowUnitClicked: (Boolean) -> Unit,
+ onForwardTapsClicked: (Boolean) -> Unit,
onAcceptClicked: () -> Unit
) {
ThemeLazyColumn {
@@ -153,7 +157,18 @@ fun MainConfigView(
colors = getToggleButtonColors()
)
}
-
+ item {
+ val isChecked = if (loaded) forwardTaps else false
+ ToggleButton(
+ checked = isChecked,
+ onCheckedChange = onForwardTapsClicked,
+ label = { Text(stringResource(R.string.forward_taps_title)) },
+ selectionControl = { ToggleSwitch(isChecked) },
+ modifier = Modifier.fillMaxWidth(),
+ enabled = loaded && entity != null,
+ colors = getToggleButtonColors()
+ )
+ }
item {
FilledIconButton(
modifier = Modifier.padding(top = 8.dp).touchTargetAwareSize(IconButtonDefaults.SmallButtonSize),
@@ -182,10 +197,12 @@ fun PreviewMainConfigView() {
entity = simplifiedEntity,
showTitle = true,
showUnit = false,
+ forwardTaps = false,
loadingState = ComplicationConfigViewModel.LoadingState.READY,
onChooseEntityClicked = {},
onShowTitleClicked = {},
onShowUnitClicked = {},
+ onForwardTapsClicked = {},
onAcceptClicked = {}
)
}