mirror of
https://github.com/home-assistant/android
synced 2024-10-02 22:34:46 +00:00
Entity state complication (#2574)
* Add entity state complication with hardcoded entity id
* Add config activity and save entity id in DB
* Update complications when entity state changes
* Allow all entity types for complication
* Update config layout
* remove unused file
* Upload database json
* Process review comments!
* Fix little bug
* Icon background and error catching
* Add automatic refresh and fix missed Dao inject
* Add icons for standard domains
91cd584b4b/src/common/const.ts (L66)
* Cleanup and further icon updates
This commit is contained in:
parent
fe377fcad8
commit
50d387a726
|
@ -0,0 +1,705 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 29,
|
||||
"identityHash": "889b2ef9501971685255ec40632d95f9",
|
||||
"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, `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": "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, '889b2ef9501971685255ec40632d95f9')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -39,6 +39,8 @@ import io.homeassistant.companion.android.database.settings.LocalNotificationSet
|
|||
import io.homeassistant.companion.android.database.settings.LocalSensorSettingConverter
|
||||
import io.homeassistant.companion.android.database.settings.Setting
|
||||
import io.homeassistant.companion.android.database.settings.SettingsDao
|
||||
import io.homeassistant.companion.android.database.wear.EntityStateComplications
|
||||
import io.homeassistant.companion.android.database.wear.EntityStateComplicationsDao
|
||||
import io.homeassistant.companion.android.database.wear.Favorites
|
||||
import io.homeassistant.companion.android.database.wear.FavoritesDao
|
||||
import io.homeassistant.companion.android.database.widget.ButtonWidgetDao
|
||||
|
@ -69,14 +71,16 @@ import io.homeassistant.companion.android.common.R as commonR
|
|||
NotificationItem::class,
|
||||
TileEntity::class,
|
||||
Favorites::class,
|
||||
EntityStateComplications::class,
|
||||
Setting::class
|
||||
],
|
||||
version = 28,
|
||||
version = 29,
|
||||
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 = 27, to = 28, spec = AppDatabase.Companion.Migration27to28::class),
|
||||
AutoMigration(from = 28, to = 29)
|
||||
]
|
||||
)
|
||||
@TypeConverters(
|
||||
|
@ -97,6 +101,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||
abstract fun notificationDao(): NotificationDao
|
||||
abstract fun tileDao(): TileDao
|
||||
abstract fun favoritesDao(): FavoritesDao
|
||||
abstract fun entityStateComplicationsDao(): EntityStateComplicationsDao
|
||||
abstract fun settingsDao(): SettingsDao
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.homeassistant.companion.android.database.notification.NotificationDao
|
|||
import io.homeassistant.companion.android.database.qs.TileDao
|
||||
import io.homeassistant.companion.android.database.sensor.SensorDao
|
||||
import io.homeassistant.companion.android.database.settings.SettingsDao
|
||||
import io.homeassistant.companion.android.database.wear.EntityStateComplicationsDao
|
||||
import io.homeassistant.companion.android.database.wear.FavoritesDao
|
||||
import io.homeassistant.companion.android.database.widget.ButtonWidgetDao
|
||||
import io.homeassistant.companion.android.database.widget.CameraWidgetDao
|
||||
|
@ -62,4 +63,7 @@ object DatabaseModule {
|
|||
|
||||
@Provides
|
||||
fun provideSettingsDao(database: AppDatabase): SettingsDao = database.settingsDao()
|
||||
|
||||
@Provides
|
||||
fun provideEntityStateComplicationsDao(database: AppDatabase): EntityStateComplicationsDao = database.entityStateComplicationsDao()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package io.homeassistant.companion.android.database.wear
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
/**
|
||||
* Represents the configuration of an entity state complication
|
||||
*/
|
||||
@Entity(tableName = "entityStateComplications")
|
||||
data class EntityStateComplications(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "id")
|
||||
val id: Int,
|
||||
@ColumnInfo(name = "entityId")
|
||||
val entityId: String
|
||||
)
|
|
@ -0,0 +1,15 @@
|
|||
package io.homeassistant.companion.android.database.wear
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
|
||||
@Dao
|
||||
interface EntityStateComplicationsDao {
|
||||
@Query("SELECT * FROM entityStateComplications WHERE id = :id")
|
||||
suspend fun get(id: Int): EntityStateComplications?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun add(entityStateComplications: EntityStateComplications)
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
<string name="auth_error_message">The \'Username\' and \'Password\' fields must be completed</string>
|
||||
<string name="auth_error">Authentication Error</string>
|
||||
<string name="auth_request">Authentication Requested</string>
|
||||
<string name="automation">Automation</string>
|
||||
<string name="autoplay_video_summary">Autoplay Videos when dashboard is active. Enabling this setting may increase data usage unexpectedly, proceed with caution.</string>
|
||||
<string name="autoplay_video">Autoplay Videos</string>
|
||||
<string name="background_access_disabled">Home Assistant does not have access to run in the background. Without this permission the app will not be able to reliably send data back to your server. Click here to request permissions.</string>
|
||||
|
@ -88,6 +89,7 @@
|
|||
<string name="basic_sensor_name_wifi">WiFi Connection</string>
|
||||
<string name="basic_sensor_sleep_events">Sleep Events</string>
|
||||
<string name="basic_sensor_sleep_tracking">Sleep Tracking</string>
|
||||
<string name="binary_sensor">Binary Sensor</string>
|
||||
<string name="biometric_message">Unlock using your biometric or screenlock credential</string>
|
||||
<string name="biometric_set_title">Confirm to continue</string>
|
||||
<string name="biometric_title">Home Assistant is locked</string>
|
||||
|
@ -102,9 +104,14 @@
|
|||
<string name="cancel">Cancel</string>
|
||||
<string name="changelog">View Full Change Log</string>
|
||||
<string name="checking_with_home_assistant">Checking with Home Assistant</string>
|
||||
<string name="choose_entity">Choose entity</string>
|
||||
<string name="choose_server">Choose server</string>
|
||||
<string name="clear_favorites">Clear Favorites</string>
|
||||
<string name="color_temp">Color temperature: %1$d</string>
|
||||
<string name="complication_entity_invalid">Invalid entity</string>
|
||||
<string name="complication_entity_state_content_description">Entity state</string>
|
||||
<string name="complication_entity_state_label">Entity state</string>
|
||||
<string name="complication_entity_state_preview">preview</string>
|
||||
<string name="config">Configuration</string>
|
||||
<string name="configure_service_call">Configure Service Call</string>
|
||||
<string name="configure_widget_label">Widget Label</string>
|
||||
|
@ -137,6 +144,7 @@
|
|||
<string name="developer_tools">Developer Tools</string>
|
||||
<string name="device_name">Device Name</string>
|
||||
<string name="device_registration">Device Registration</string>
|
||||
<string name="device_tracker">Device tracker</string>
|
||||
<string name="dialog_add_panel_shortcut_content">Create a shortcut to this item?</string>
|
||||
<string name="dialog_add_panel_shortcut_title">Add Shortcut</string>
|
||||
<string name="disable_all_sensors">Disable All %1$d Sensors</string>
|
||||
|
@ -171,6 +179,7 @@
|
|||
<string name="enable_sensor_missing_permission_nearby_devices">Requires \'Nearby devices\' permission</string>
|
||||
<string name="enable_sensor_missing_permission_phone">Requires \'Phone\' permission</string>
|
||||
<string name="enabled">Enabled</string>
|
||||
<string name="entity">Entity</string>
|
||||
<plurals name="entities_found">
|
||||
<item quantity="one">%d entity found</item>
|
||||
<item quantity="other">%d entities found</item>
|
||||
|
@ -309,6 +318,7 @@
|
|||
<string name="manual_title">What is your Home Assistant URL?</string>
|
||||
<string name="map">Map</string>
|
||||
<string name="maximum">Maximum</string>
|
||||
<string name="media_player">Media player</string>
|
||||
<string name="media_player_widget_desc">Control any media player and see current now playing image</string>
|
||||
<string name="message_checking">Checking Wear Devices with App</string>
|
||||
<string name="message_missing_all">The Wear app is missing on your watch, click the button below to install the app.\n\nNote: Currently the Wear OS app requires you to be enrolled in the beta for the phone app. If the button does not work then please join the beta: https://play.google.com/apps/testing/io.homeassistant.companion.android</string>
|
||||
|
@ -370,6 +380,8 @@
|
|||
<string name="password">Password</string>
|
||||
<string name="permission_explanation_calls">In order to track incoming and outgoing call\'s occurrence we need access to your phone state. No phone numbers or other call details will be stored.</string>
|
||||
<string name="permission_explanation">In order to use location tracking features or different connection urls based on WiFi SSID we need access to your location. If you want consistent background updates you will also need to allow background processing</string>
|
||||
<string name="persistent_notification">Persistent notification</string>
|
||||
<string name="person">Person</string>
|
||||
<string name="pin_shortcut">Pin Shortcut</string>
|
||||
<string name="pref_call_tracking_summary">Allow application to detect call occurrence and notify server about it.</string>
|
||||
<string name="pref_call_tracking_title">Calls Tracking</string>
|
||||
|
@ -419,6 +431,7 @@
|
|||
<string name="security_vulnerably_understand">I Understand</string>
|
||||
<string name="security_vulnerably_view">View Bulletin</string>
|
||||
<string name="security">Security</string>
|
||||
<string name="select">Select</string>
|
||||
<string name="select_entity_to_display">Select Entity to display</string>
|
||||
<string name="select_instance">Select the instance you would like to connect to:</string>
|
||||
<string name="sensor_description_active_notification_count">Total count of active notifications that are visible to the user including silent, persistent and the Sensor Worker notifications.</string>
|
||||
|
@ -657,6 +670,7 @@
|
|||
<string name="store_request_successful">Request to install app on wear device sent successfully</string>
|
||||
<string name="store_request_unsuccessful">Play Store Request Failed. Wear device(s) may not support Play Store, that is, the Wear device may be version 1.0.</string>
|
||||
<string name="successful">Successful</string>
|
||||
<string name="sun">Sun</string>
|
||||
<string name="switches">Switches</string>
|
||||
<string name="tag_reader_title">Processing Tag</string>
|
||||
<string name="template_tile">Template tile</string>
|
||||
|
@ -860,4 +874,7 @@
|
|||
<string name="sensor_description_volume_dtmf">Volume level for DTMF tones on the device</string>
|
||||
<string name="basic_sensor_name_high_accuracy_interval">High Accuracy Update Interval</string>
|
||||
<string name="sensor_description_high_accuracy_interval">The update interval set for high accuracy mode</string>
|
||||
<string name="update">Update</string>
|
||||
<string name="weather">Weather</string>
|
||||
<string name="zone">Zone</string>
|
||||
</resources>
|
|
@ -12,7 +12,7 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId = "io.homeassistant.companion.android"
|
||||
minSdk = 25
|
||||
minSdk = 26
|
||||
targetSdk = 30
|
||||
|
||||
versionName = System.getenv("VERSION") ?: "LOCAL"
|
||||
|
@ -92,7 +92,6 @@ dependencies {
|
|||
implementation("com.google.android.material:material:1.6.0")
|
||||
|
||||
implementation("androidx.wear:wear:1.2.0")
|
||||
implementation("com.google.android.support:wearable:2.9.0")
|
||||
implementation("com.google.android.gms:play-services-wearable:17.1.0")
|
||||
implementation("androidx.wear:wear-input:1.2.0-alpha02")
|
||||
implementation("androidx.wear:wear-remote-interactions:1.0.0")
|
||||
|
@ -120,4 +119,6 @@ dependencies {
|
|||
|
||||
implementation("com.google.guava:guava:31.1-android")
|
||||
implementation("androidx.wear.tiles:tiles:1.0.1")
|
||||
|
||||
implementation("androidx.wear.watchface:watchface-complications-data-source-ktx:1.1.0-rc01")
|
||||
}
|
||||
|
|
|
@ -53,6 +53,14 @@
|
|||
<activity android:name=".onboarding.integration.MobileAppIntegrationActivity" />
|
||||
<activity android:name=".onboarding.authentication.AuthenticationActivity" />
|
||||
<activity android:name=".onboarding.manual_setup.ManualSetupActivity" />
|
||||
<activity android:name=".complications.ComplicationConfigActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="io.homeassistant.companion.android.ENTITY_STATE_COMPLICATION_CONFIG" />
|
||||
<category android:name="android.support.wearable.complications.category.PROVIDER_CONFIG" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- To show confirmations and failures -->
|
||||
<activity android:name="androidx.wear.activity.ConfirmationActivity" />
|
||||
|
@ -90,6 +98,28 @@
|
|||
<action android:name="io.homeassistant.companion.android.TILE_ACTION" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Complications -->
|
||||
<service android:name=".complications.EntityStateDataSourceService"
|
||||
android:exported="true"
|
||||
android:icon="@drawable/ic_lightbulb"
|
||||
android:label="@string/complication_entity_state_label"
|
||||
android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER">
|
||||
<intent-filter>
|
||||
<action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.wearable.complications.SUPPORTED_TYPES"
|
||||
android:value="SHORT_TEXT" />
|
||||
<meta-data
|
||||
android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
|
||||
android:value="900" />
|
||||
<meta-data
|
||||
android:name="android.support.wearable.complications.PROVIDER_CONFIG_ACTION"
|
||||
android:value="io.homeassistant.companion.android.ENTITY_STATE_COMPLICATION_CONFIG" />
|
||||
</service>
|
||||
<receiver android:name=".complications.ComplicationReceiver" />
|
||||
|
||||
<service android:name=".phone.PhoneSettingsListener" android:exported="true">
|
||||
<intent-filter>
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.Application
|
|||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import io.homeassistant.companion.android.complications.ComplicationReceiver
|
||||
import io.homeassistant.companion.android.sensors.SensorReceiver
|
||||
|
||||
@HiltAndroidApp
|
||||
|
@ -24,5 +25,13 @@ open class HomeAssistantApplication : Application() {
|
|||
addAction(Intent.ACTION_POWER_DISCONNECTED)
|
||||
}
|
||||
)
|
||||
|
||||
// Update complications when the screen is on
|
||||
val complicationReceiver = ComplicationReceiver()
|
||||
|
||||
val screenIntentFilter = IntentFilter()
|
||||
screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON)
|
||||
|
||||
registerReceiver(complicationReceiver, screenIntentFilter)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package io.homeassistant.companion.android.complications
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_COMPLICATION_ID
|
||||
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_COMPLICATION_TYPE
|
||||
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_DATA_SOURCE_COMPONENT
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.complications.views.LoadConfigView
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ComplicationConfigActivity : ComponentActivity() {
|
||||
|
||||
private val complicationConfigViewModel by viewModels<ComplicationConfigViewModel>()
|
||||
|
||||
companion object {
|
||||
private const val TAG = "EntityStateConfigActivity"
|
||||
|
||||
fun newInstance(context: Context): Intent {
|
||||
return Intent(context, ComplicationConfigActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val id = intent.getIntExtra(EXTRA_CONFIG_COMPLICATION_ID, -1)
|
||||
val type = intent.getIntExtra(EXTRA_CONFIG_COMPLICATION_TYPE, -1)
|
||||
val component = intent.getParcelableExtra<ComponentName>(EXTRA_CONFIG_DATA_SOURCE_COMPONENT)
|
||||
Log.i(TAG, "Config for id $id of type $type for component ${component?.className}")
|
||||
|
||||
setContent {
|
||||
LoadConfigView(
|
||||
complicationConfigViewModel
|
||||
) {
|
||||
setResult(Activity.RESULT_OK)
|
||||
complicationConfigViewModel.selectedEntity?.let {
|
||||
complicationConfigViewModel.addEntityStateComplication(
|
||||
id,
|
||||
it
|
||||
)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package io.homeassistant.companion.android.complications
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.homeassistant.companion.android.HomeAssistantApplication
|
||||
import io.homeassistant.companion.android.common.data.integration.Entity
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.domain
|
||||
import io.homeassistant.companion.android.common.data.websocket.WebSocketRepository
|
||||
import io.homeassistant.companion.android.common.data.websocket.WebSocketState
|
||||
import io.homeassistant.companion.android.data.SimplifiedEntity
|
||||
import io.homeassistant.companion.android.database.wear.EntityStateComplications
|
||||
import io.homeassistant.companion.android.database.wear.EntityStateComplicationsDao
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ComplicationConfigViewModel @Inject constructor(
|
||||
application: Application,
|
||||
private val integrationUseCase: IntegrationRepository,
|
||||
private val webSocketUseCase: WebSocketRepository,
|
||||
private val entityStateComplicationsDao: EntityStateComplicationsDao
|
||||
) : AndroidViewModel(application) {
|
||||
companion object {
|
||||
const val TAG = "ComplicationConfigViewModel"
|
||||
}
|
||||
|
||||
enum class LoadingState {
|
||||
LOADING, READY, ERROR
|
||||
}
|
||||
|
||||
val app = getApplication<HomeAssistantApplication>()
|
||||
|
||||
var entities = mutableStateMapOf<String, Entity<*>>()
|
||||
private set
|
||||
var entitiesByDomain = mutableStateMapOf<String, SnapshotStateList<Entity<*>>>()
|
||||
private set
|
||||
var entitiesByDomainOrder = mutableStateListOf<String>()
|
||||
private set
|
||||
|
||||
var loadingState by mutableStateOf(LoadingState.LOADING)
|
||||
private set
|
||||
var selectedEntity: SimplifiedEntity? by mutableStateOf(null)
|
||||
private set
|
||||
|
||||
init {
|
||||
loadEntities()
|
||||
}
|
||||
|
||||
private fun loadEntities() {
|
||||
viewModelScope.launch {
|
||||
if (!integrationUseCase.isRegistered()) {
|
||||
loadingState = LoadingState.ERROR
|
||||
return@launch
|
||||
}
|
||||
try {
|
||||
// Load initial state
|
||||
loadingState = LoadingState.LOADING
|
||||
integrationUseCase.getEntities()?.forEach {
|
||||
entities[it.entityId] = it
|
||||
}
|
||||
updateEntityDomains()
|
||||
|
||||
// Finished initial load, update state
|
||||
val webSocketState = webSocketUseCase.getConnectionState()
|
||||
if (webSocketState == WebSocketState.CLOSED_AUTH) {
|
||||
loadingState = LoadingState.ERROR
|
||||
return@launch
|
||||
}
|
||||
loadingState = if (webSocketState == WebSocketState.ACTIVE) {
|
||||
LoadingState.READY
|
||||
} else {
|
||||
LoadingState.ERROR
|
||||
}
|
||||
|
||||
// Listen for updates
|
||||
viewModelScope.launch {
|
||||
integrationUseCase.getEntityUpdates()?.collect {
|
||||
entities[it.entityId] = it
|
||||
updateEntityDomains()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Exception while loading entities", e)
|
||||
loadingState = LoadingState.ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateEntityDomains() {
|
||||
val entitiesList = entities.values.toList().sortedBy { it.entityId }
|
||||
val domainsList = entitiesList.map { it.domain }.distinct()
|
||||
|
||||
// Create a list with all discovered domains + their entities
|
||||
domainsList.forEach { domain ->
|
||||
val entitiesInDomain = mutableStateListOf<Entity<*>>()
|
||||
entitiesInDomain.addAll(entitiesList.filter { it.domain == domain })
|
||||
entitiesByDomain[domain]?.let {
|
||||
it.clear()
|
||||
it.addAll(entitiesInDomain)
|
||||
} ?: run {
|
||||
entitiesByDomain[domain] = entitiesInDomain
|
||||
}
|
||||
}
|
||||
entitiesByDomainOrder.clear()
|
||||
entitiesByDomainOrder.addAll(domainsList)
|
||||
}
|
||||
|
||||
fun setEntity(entity: SimplifiedEntity) {
|
||||
selectedEntity = entity
|
||||
}
|
||||
|
||||
fun addEntityStateComplication(id: Int, entity: SimplifiedEntity) {
|
||||
viewModelScope.launch {
|
||||
entityStateComplicationsDao.add(EntityStateComplications(id, entity.entityId))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package io.homeassistant.companion.android.complications
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUpdateRequester
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ComplicationReceiver : BroadcastReceiver() {
|
||||
@Inject
|
||||
lateinit var integrationUseCase: IntegrationRepository
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val result = goAsync()
|
||||
|
||||
try {
|
||||
when (intent.action) {
|
||||
UPDATE_COMPLICATION -> updateComplication(context, intent.getIntExtra(EXTRA_ID, -1))
|
||||
Intent.ACTION_SCREEN_ON -> onScreenOn(context)
|
||||
}
|
||||
} finally {
|
||||
result.finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateComplication(context: Context, id: Int) {
|
||||
scope.launch {
|
||||
// Request an update for the complication that has just been toggled.
|
||||
ComplicationDataSourceUpdateRequester
|
||||
.create(
|
||||
context = context,
|
||||
complicationDataSourceComponent = ComponentName(context, EntityStateDataSourceService::class.java)
|
||||
)
|
||||
.requestUpdate(id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAllComplications(context: Context) {
|
||||
ComplicationDataSourceUpdateRequester
|
||||
.create(
|
||||
context = context,
|
||||
complicationDataSourceComponent = ComponentName(context, EntityStateDataSourceService::class.java)
|
||||
)
|
||||
.requestUpdateAll()
|
||||
}
|
||||
|
||||
private fun onScreenOn(context: Context) {
|
||||
scope.launch {
|
||||
if (!integrationUseCase.isRegistered()) {
|
||||
return@launch
|
||||
}
|
||||
updateAllComplications(context)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ComplicationReceiver"
|
||||
|
||||
const val UPDATE_COMPLICATION = "update_complication"
|
||||
private const val EXTRA_ID = "complication_instance_id"
|
||||
|
||||
/**
|
||||
* Returns a pending intent, suitable for use as a tap intent, that causes a complication to be
|
||||
* toggled and updated.
|
||||
*/
|
||||
fun getComplicationToggleIntent(
|
||||
context: Context,
|
||||
complicationInstanceId: Int
|
||||
): PendingIntent {
|
||||
val intent = Intent(context, ComplicationReceiver::class.java).apply {
|
||||
action = UPDATE_COMPLICATION
|
||||
putExtra(EXTRA_ID, complicationInstanceId)
|
||||
}
|
||||
|
||||
// Pass complicationId as the requestCode to ensure that different complications get
|
||||
// different intents.
|
||||
return PendingIntent.getBroadcast(
|
||||
context,
|
||||
complicationInstanceId,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package io.homeassistant.companion.android.complications
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Icon
|
||||
import android.util.Log
|
||||
import androidx.wear.watchface.complications.data.ComplicationData
|
||||
import androidx.wear.watchface.complications.data.ComplicationType
|
||||
import androidx.wear.watchface.complications.data.MonochromaticImage
|
||||
import androidx.wear.watchface.complications.data.PlainComplicationText
|
||||
import androidx.wear.watchface.complications.data.ShortTextComplicationData
|
||||
import androidx.wear.watchface.complications.datasource.ComplicationRequest
|
||||
import androidx.wear.watchface.complications.datasource.SuspendingComplicationDataSourceService
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.common.R
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.domain
|
||||
import io.homeassistant.companion.android.database.wear.EntityStateComplicationsDao
|
||||
import io.homeassistant.companion.android.util.getIcon
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class EntityStateDataSourceService : SuspendingComplicationDataSourceService() {
|
||||
|
||||
@Inject
|
||||
lateinit var integrationUseCase: IntegrationRepository
|
||||
@Inject
|
||||
lateinit var entityStateComplicationsDao: EntityStateComplicationsDao
|
||||
|
||||
companion object {
|
||||
const val TAG = "EntityStateDataSourceService"
|
||||
}
|
||||
|
||||
override suspend fun onComplicationRequest(request: ComplicationRequest): ComplicationData? {
|
||||
if (request.complicationType != ComplicationType.SHORT_TEXT)
|
||||
return null
|
||||
|
||||
val id = request.complicationInstanceId
|
||||
|
||||
val entityId = entityStateComplicationsDao.get(id)?.entityId
|
||||
?: return ShortTextComplicationData.Builder(
|
||||
text = PlainComplicationText.Builder(getText(R.string.complication_entity_invalid)).build(),
|
||||
contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description))
|
||||
.build()
|
||||
).build()
|
||||
|
||||
val entity = try {
|
||||
integrationUseCase.getEntity(entityId)
|
||||
?: return ShortTextComplicationData.Builder(
|
||||
text = PlainComplicationText.Builder(getText(R.string.state_unknown)).build(),
|
||||
contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description))
|
||||
.build()
|
||||
).build()
|
||||
} catch (t: Throwable) {
|
||||
Log.e(TAG, "Unable to get entity state for $entityId: ${t.message}")
|
||||
return null
|
||||
}
|
||||
|
||||
val attributes = entity.attributes as Map<*, *>
|
||||
val icon = getIcon(entity, entity.domain, applicationContext) ?: CommunityMaterial.Icon.cmd_bookmark
|
||||
val iconBitmap = IconicsDrawable(this, icon).apply {
|
||||
colorInt = Color.WHITE
|
||||
}.toBitmap()
|
||||
return ShortTextComplicationData.Builder(
|
||||
text = PlainComplicationText.Builder(entity.state).build(),
|
||||
contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description))
|
||||
.build()
|
||||
)
|
||||
.setTapAction(ComplicationReceiver.getComplicationToggleIntent(this, request.complicationInstanceId))
|
||||
.setMonochromaticImage(MonochromaticImage.Builder(Icon.createWithBitmap(iconBitmap)).build())
|
||||
.setTitle(PlainComplicationText.Builder(attributes["friendly_name"] as String? ?: entity.entityId).build())
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun getPreviewData(type: ComplicationType): ComplicationData =
|
||||
ShortTextComplicationData.Builder(
|
||||
text = PlainComplicationText.Builder(getText(R.string.complication_entity_state_preview)).build(),
|
||||
contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description)).build()
|
||||
)
|
||||
.setMonochromaticImage(
|
||||
MonochromaticImage.Builder(
|
||||
Icon.createWithResource(
|
||||
this,
|
||||
io.homeassistant.companion.android.R.drawable.ic_lightbulb,
|
||||
),
|
||||
).build(),
|
||||
)
|
||||
.setTitle(PlainComplicationText.Builder(getText(R.string.entity)).build())
|
||||
.build()
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package io.homeassistant.companion.android.complications.views
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.wear.compose.material.Button
|
||||
import androidx.wear.compose.material.ButtonDefaults
|
||||
import androidx.wear.compose.material.Chip
|
||||
import androidx.wear.compose.material.ChipDefaults
|
||||
import androidx.wear.compose.material.ExperimentalWearMaterialApi
|
||||
import androidx.wear.compose.material.Text
|
||||
import androidx.wear.compose.navigation.SwipeDismissableNavHost
|
||||
import androidx.wear.compose.navigation.composable
|
||||
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
|
||||
import com.mikepenz.iconics.compose.Image
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import io.homeassistant.companion.android.HomeAssistantApplication
|
||||
import io.homeassistant.companion.android.common.R
|
||||
import io.homeassistant.companion.android.complications.ComplicationConfigViewModel
|
||||
import io.homeassistant.companion.android.data.SimplifiedEntity
|
||||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import io.homeassistant.companion.android.theme.wearColorPalette
|
||||
import io.homeassistant.companion.android.util.getIcon
|
||||
import io.homeassistant.companion.android.views.ChooseEntityView
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
|
||||
private const val SCREEN_MAIN = "main"
|
||||
private const val SCREEN_CHOOSE_ENTITY = "choose_entity"
|
||||
|
||||
@OptIn(ExperimentalWearMaterialApi::class)
|
||||
@Composable
|
||||
fun LoadConfigView(
|
||||
complicationConfigViewModel: ComplicationConfigViewModel,
|
||||
onAcceptClicked: () -> Unit
|
||||
) {
|
||||
WearAppTheme {
|
||||
val swipeDismissableNavController = rememberSwipeDismissableNavController()
|
||||
SwipeDismissableNavHost(
|
||||
navController = swipeDismissableNavController,
|
||||
startDestination = SCREEN_MAIN
|
||||
) {
|
||||
composable(SCREEN_MAIN) {
|
||||
MainConfigView(
|
||||
entity = complicationConfigViewModel.selectedEntity,
|
||||
loadingState = complicationConfigViewModel.loadingState,
|
||||
onChooseEntityClicked = {
|
||||
swipeDismissableNavController.navigate(SCREEN_CHOOSE_ENTITY)
|
||||
},
|
||||
onAcceptClicked = onAcceptClicked
|
||||
)
|
||||
}
|
||||
composable(SCREEN_CHOOSE_ENTITY) {
|
||||
val app = complicationConfigViewModel.getApplication<HomeAssistantApplication>()
|
||||
ChooseEntityView(
|
||||
entitiesByDomainOrder = complicationConfigViewModel.entitiesByDomainOrder,
|
||||
entitiesByDomain = complicationConfigViewModel.entitiesByDomain,
|
||||
onNoneClicked = {},
|
||||
onEntitySelected = { entity ->
|
||||
complicationConfigViewModel.setEntity(entity)
|
||||
swipeDismissableNavController.navigateUp()
|
||||
},
|
||||
allowNone = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainConfigView(
|
||||
entity: SimplifiedEntity?,
|
||||
loadingState: ComplicationConfigViewModel.LoadingState,
|
||||
onChooseEntityClicked: () -> Unit,
|
||||
onAcceptClicked: () -> Unit
|
||||
) {
|
||||
ThemeLazyColumn {
|
||||
item {
|
||||
ListHeader(id = R.string.complication_entity_state_label)
|
||||
}
|
||||
if (loadingState != ComplicationConfigViewModel.LoadingState.ERROR) {
|
||||
val loaded = loadingState == ComplicationConfigViewModel.LoadingState.READY
|
||||
item {
|
||||
val iconBitmap = getIcon(
|
||||
entity?.icon,
|
||||
entity?.domain ?: "light",
|
||||
LocalContext.current
|
||||
)
|
||||
Chip(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = {
|
||||
Image(
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_bookmark,
|
||||
colorFilter = ColorFilter.tint(wearColorPalette.onSurface)
|
||||
)
|
||||
},
|
||||
colors = ChipDefaults.secondaryChipColors(),
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.choose_entity)
|
||||
)
|
||||
},
|
||||
secondaryLabel = {
|
||||
Text(
|
||||
if (loaded)
|
||||
entity?.friendlyName ?: ""
|
||||
else
|
||||
stringResource(R.string.loading)
|
||||
)
|
||||
},
|
||||
enabled = loaded,
|
||||
onClick = onChooseEntityClicked
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Button(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
onClick = { onAcceptClicked() },
|
||||
colors = ButtonDefaults.primaryButtonColors(),
|
||||
enabled = loaded && entity != null
|
||||
) {
|
||||
Image(
|
||||
CommunityMaterial.Icon.cmd_check
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
Text(text = stringResource(R.string.error_connection_failed))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,6 +36,8 @@ import io.homeassistant.companion.android.theme.WearAppTheme
|
|||
import io.homeassistant.companion.android.util.getColorTemperature
|
||||
import io.homeassistant.companion.android.util.onEntityClickedFeedback
|
||||
import io.homeassistant.companion.android.util.onEntityFeedback
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
import java.text.DateFormat
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -16,6 +16,10 @@ import io.homeassistant.companion.android.util.playPreviewEntityScene2
|
|||
import io.homeassistant.companion.android.util.playPreviewEntityScene3
|
||||
import io.homeassistant.companion.android.util.previewEntity1
|
||||
import io.homeassistant.companion.android.util.previewEntity2
|
||||
import io.homeassistant.companion.android.views.ExpandableListHeader
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
import io.homeassistant.companion.android.views.rememberExpandedStates
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -53,7 +53,7 @@ fun EntityUi(
|
|||
.fillMaxWidth(),
|
||||
appIcon = {
|
||||
Image(
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone,
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_bookmark,
|
||||
colorFilter = ColorFilter.tint(wearColorPalette.onSurface)
|
||||
)
|
||||
},
|
||||
|
@ -90,7 +90,7 @@ fun EntityUi(
|
|||
.fillMaxWidth(),
|
||||
icon = {
|
||||
Image(
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone,
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_bookmark,
|
||||
colorFilter = ColorFilter.tint(wearColorPalette.onSurface)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -27,6 +27,8 @@ import io.homeassistant.companion.android.home.MainViewModel
|
|||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import io.homeassistant.companion.android.tiles.ShortcutsTile
|
||||
import io.homeassistant.companion.android.tiles.TemplateTile
|
||||
import io.homeassistant.companion.android.views.ChooseEntityView
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
private const val ARG_SCREEN_SENSOR_MANAGER_ID = "sensorManagerId"
|
||||
|
@ -199,13 +201,14 @@ fun LoadHomePage(
|
|||
}
|
||||
composable(SCREEN_SELECT_TILE_SHORTCUT) {
|
||||
ChooseEntityView(
|
||||
mainViewModel,
|
||||
{
|
||||
entitiesByDomainOrder = mainViewModel.entitiesByDomainOrder,
|
||||
entitiesByDomain = mainViewModel.entitiesByDomain,
|
||||
onNoneClicked = {
|
||||
mainViewModel.clearTileShortcut(shortcutEntitySelectionIndex)
|
||||
TileService.getUpdater(context).requestUpdate(ShortcutsTile::class.java)
|
||||
swipeDismissableNavController.navigateUp()
|
||||
},
|
||||
{ entity ->
|
||||
onEntitySelected = { entity ->
|
||||
mainViewModel.setTileShortcut(shortcutEntitySelectionIndex, entity)
|
||||
TileService.getUpdater(context).requestUpdate(ShortcutsTile::class.java)
|
||||
swipeDismissableNavController.navigateUp()
|
||||
|
|
|
@ -35,6 +35,9 @@ import io.homeassistant.companion.android.theme.WearAppTheme
|
|||
import io.homeassistant.companion.android.theme.wearColorPalette
|
||||
import io.homeassistant.companion.android.util.getIcon
|
||||
import io.homeassistant.companion.android.util.onEntityClickedFeedback
|
||||
import io.homeassistant.companion.android.views.ExpandableListHeader
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@Composable
|
||||
|
@ -86,7 +89,7 @@ fun MainView(
|
|||
.fillMaxWidth(),
|
||||
icon = {
|
||||
Image(
|
||||
asset = CommunityMaterial.Icon.cmd_cellphone,
|
||||
asset = CommunityMaterial.Icon.cmd_bookmark,
|
||||
colorFilter = ColorFilter.tint(wearColorPalette.onSurface)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.mikepenz.iconics.compose.Image
|
|||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import io.homeassistant.companion.android.theme.wearColorPalette
|
||||
import io.homeassistant.companion.android.util.IntervalToString
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.common.R as R
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -9,6 +9,8 @@ import androidx.wear.compose.material.rememberScalingLazyListState
|
|||
import io.homeassistant.companion.android.common.sensors.SensorManager
|
||||
import io.homeassistant.companion.android.database.sensor.Sensor
|
||||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
|
||||
@Composable
|
||||
fun SensorManagerUi(
|
||||
|
|
|
@ -18,6 +18,8 @@ import androidx.wear.compose.material.rememberScalingLazyListState
|
|||
import io.homeassistant.companion.android.common.sensors.SensorManager
|
||||
import io.homeassistant.companion.android.sensors.SensorReceiver
|
||||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -22,6 +22,10 @@ import io.homeassistant.companion.android.home.MainViewModel
|
|||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import io.homeassistant.companion.android.theme.wearColorPalette
|
||||
import io.homeassistant.companion.android.util.getIcon
|
||||
import io.homeassistant.companion.android.views.ExpandableListHeader
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
import io.homeassistant.companion.android.views.rememberExpandedStates
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@Composable
|
||||
|
@ -99,7 +103,7 @@ private fun FavoriteToggleChip(
|
|||
.fillMaxWidth(),
|
||||
appIcon = {
|
||||
Image(
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone,
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_bookmark,
|
||||
colorFilter = ColorFilter.tint(wearColorPalette.onSurface)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -24,6 +24,8 @@ import io.homeassistant.companion.android.theme.WearAppTheme
|
|||
import io.homeassistant.companion.android.theme.wearColorPalette
|
||||
import io.homeassistant.companion.android.util.getIcon
|
||||
import io.homeassistant.companion.android.util.simplifiedEntity
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@Composable
|
||||
|
@ -75,7 +77,7 @@ fun SetTileShortcutsView(
|
|||
.fillMaxWidth(),
|
||||
icon = {
|
||||
Image(
|
||||
iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone,
|
||||
iconBitmap ?: CommunityMaterial.Icon.cmd_bookmark,
|
||||
colorFilter = ColorFilter.tint(Color.White)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -23,6 +23,8 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria
|
|||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import io.homeassistant.companion.android.theme.wearColorPalette
|
||||
import io.homeassistant.companion.android.util.previewFavoritesList
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -15,6 +15,8 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria
|
|||
import io.homeassistant.companion.android.common.R
|
||||
import io.homeassistant.companion.android.theme.wearColorPalette
|
||||
import io.homeassistant.companion.android.util.IntervalToString
|
||||
import io.homeassistant.companion.android.views.ListHeader
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
|
||||
@Composable
|
||||
fun TemplateTileSettingsView(
|
||||
|
|
|
@ -109,7 +109,7 @@ class ShortcutsTile : TileService() {
|
|||
entity.icon,
|
||||
entity.domain,
|
||||
this@ShortcutsTile
|
||||
) ?: CommunityMaterial.Icon.cmd_cellphone
|
||||
) ?: CommunityMaterial.Icon.cmd_bookmark
|
||||
val iconBitmap = IconicsDrawable(this@ShortcutsTile, iconIIcon).apply {
|
||||
colorInt = Color.WHITE
|
||||
sizeDp = iconSize.roundToInt()
|
||||
|
|
|
@ -8,9 +8,29 @@ import com.mikepenz.iconics.IconicsDrawable
|
|||
import com.mikepenz.iconics.typeface.IIcon
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import io.homeassistant.companion.android.common.data.integration.Entity
|
||||
import io.homeassistant.companion.android.home.HomePresenterImpl
|
||||
import java.util.Calendar
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
fun stringForDomain(domain: String, context: Context): String? =
|
||||
(
|
||||
HomePresenterImpl.domainsWithNames + mapOf(
|
||||
"automation" to commonR.string.automation,
|
||||
"binary_sensor" to commonR.string.binary_sensor,
|
||||
"device_tracker" to commonR.string.device_tracker,
|
||||
"input_number" to commonR.string.domain_input_number,
|
||||
"media_player" to commonR.string.media_player,
|
||||
"persistent_notification" to commonR.string.persistent_notification,
|
||||
"person" to commonR.string.person,
|
||||
"select" to commonR.string.select,
|
||||
"sensor" to commonR.string.sensor,
|
||||
"sun" to commonR.string.sun,
|
||||
"update" to commonR.string.update,
|
||||
"weather" to commonR.string.weather,
|
||||
"zone" to commonR.string.zone
|
||||
)
|
||||
)[domain]?.let { context.getString(it) }
|
||||
|
||||
fun getIcon(icon: String?, domain: String, context: Context): IIcon? {
|
||||
val simpleEntity = Entity(
|
||||
"",
|
||||
|
@ -40,13 +60,27 @@ fun getIcon(entity: Entity<Map<String, Any>>?, domain: String, context: Context)
|
|||
else
|
||||
entity?.attributes?.get("state") as String?
|
||||
when (domain) {
|
||||
"alert" -> CommunityMaterial.Icon.cmd_alert
|
||||
"air_quality" -> CommunityMaterial.Icon.cmd_air_filter
|
||||
"automation" -> CommunityMaterial.Icon3.cmd_robot
|
||||
"button" -> when (entity?.attributes?.get("device_class")) {
|
||||
"restart" -> CommunityMaterial.Icon3.cmd_restart
|
||||
"update" -> CommunityMaterial.Icon3.cmd_package_up
|
||||
else -> CommunityMaterial.Icon2.cmd_gesture_tap_button
|
||||
}
|
||||
"calendar" -> CommunityMaterial.Icon.cmd_calendar
|
||||
"camera" -> CommunityMaterial.Icon3.cmd_video
|
||||
"climate" -> CommunityMaterial.Icon3.cmd_thermostat
|
||||
"configurator" -> CommunityMaterial.Icon.cmd_cog
|
||||
"conversation" -> CommunityMaterial.Icon3.cmd_text_to_speech
|
||||
"cover" -> coverIcon(compareState, entity)
|
||||
"counter" -> CommunityMaterial.Icon.cmd_counter
|
||||
"fan" -> CommunityMaterial.Icon2.cmd_fan
|
||||
"google_assistant" -> CommunityMaterial.Icon2.cmd_google_assistant
|
||||
"group" -> CommunityMaterial.Icon2.cmd_google_circles_communities
|
||||
"homeassistant" -> CommunityMaterial.Icon2.cmd_home_assistant
|
||||
"homekit" -> CommunityMaterial.Icon2.cmd_home_automation
|
||||
"image_processing" -> CommunityMaterial.Icon2.cmd_image_filter_frames
|
||||
"input_boolean" -> if (entity?.entityId?.isNotBlank() == true) {
|
||||
if (compareState == "on")
|
||||
CommunityMaterial.Icon.cmd_check_circle_outline
|
||||
|
@ -56,6 +90,14 @@ fun getIcon(entity: Entity<Map<String, Any>>?, domain: String, context: Context)
|
|||
CommunityMaterial.Icon2.cmd_light_switch
|
||||
}
|
||||
"input_button" -> CommunityMaterial.Icon2.cmd_gesture_tap_button
|
||||
"input_datetime" -> if (entity?.attributes?.get("has_date") == false)
|
||||
CommunityMaterial.Icon.cmd_clock
|
||||
else if (entity?.attributes?.get("has_time") == false)
|
||||
CommunityMaterial.Icon.cmd_calendar
|
||||
else
|
||||
CommunityMaterial.Icon.cmd_calendar_clock
|
||||
"input_select" -> CommunityMaterial.Icon2.cmd_format_list_bulleted
|
||||
"input_text" -> CommunityMaterial.Icon2.cmd_form_textbox
|
||||
"light" -> CommunityMaterial.Icon2.cmd_lightbulb
|
||||
"lock" -> when (compareState) {
|
||||
"unlocked" -> CommunityMaterial.Icon2.cmd_lock_open
|
||||
|
@ -63,8 +105,24 @@ fun getIcon(entity: Entity<Map<String, Any>>?, domain: String, context: Context)
|
|||
"locking", "unlocking" -> CommunityMaterial.Icon2.cmd_lock_clock
|
||||
else -> CommunityMaterial.Icon2.cmd_lock
|
||||
}
|
||||
"script" -> CommunityMaterial.Icon3.cmd_script_text_outline // Different from frontend: outline version
|
||||
"mailbox" -> CommunityMaterial.Icon3.cmd_mailbox
|
||||
"notify" -> CommunityMaterial.Icon.cmd_comment_alert
|
||||
"number" -> CommunityMaterial.Icon3.cmd_ray_vertex
|
||||
"persistent_notification" -> CommunityMaterial.Icon.cmd_bell
|
||||
"person" -> CommunityMaterial.Icon.cmd_account
|
||||
"plant" -> CommunityMaterial.Icon2.cmd_flower
|
||||
"proximity" -> CommunityMaterial.Icon.cmd_apple_safari
|
||||
"remote" -> CommunityMaterial.Icon3.cmd_remote
|
||||
"scene" -> CommunityMaterial.Icon3.cmd_palette_outline // Different from frontend: outline version
|
||||
"script" -> CommunityMaterial.Icon3.cmd_script_text_outline // Different from frontend: outline version
|
||||
"select" -> CommunityMaterial.Icon2.cmd_format_list_bulleted
|
||||
"sensor" -> CommunityMaterial.Icon.cmd_eye
|
||||
"siren" -> CommunityMaterial.Icon.cmd_bullhorn
|
||||
"simple_alarm" -> CommunityMaterial.Icon.cmd_bell
|
||||
"sun" -> if (compareState == "above_horizon")
|
||||
CommunityMaterial.Icon3.cmd_white_balance_sunny
|
||||
else
|
||||
CommunityMaterial.Icon3.cmd_weather_night
|
||||
"switch" -> if (entity?.entityId?.isNotBlank() == true) {
|
||||
when (entity.attributes["device_class"]) {
|
||||
"outlet" -> if (compareState == "on") CommunityMaterial.Icon3.cmd_power_plug else CommunityMaterial.Icon3.cmd_power_plug_off
|
||||
|
@ -74,7 +132,13 @@ fun getIcon(entity: Entity<Map<String, Any>>?, domain: String, context: Context)
|
|||
} else { // For SimplifiedEntity without state, use a more generic icon
|
||||
CommunityMaterial.Icon2.cmd_light_switch
|
||||
}
|
||||
else -> CommunityMaterial.Icon.cmd_cellphone
|
||||
"timer" -> CommunityMaterial.Icon3.cmd_timer_outline
|
||||
"updater" -> CommunityMaterial.Icon.cmd_cloud_upload
|
||||
"vacuum" -> CommunityMaterial.Icon3.cmd_robot_vacuum
|
||||
"water_heater" -> CommunityMaterial.Icon3.cmd_thermometer
|
||||
"weather" -> CommunityMaterial.Icon3.cmd_weather_cloudy
|
||||
"zone" -> CommunityMaterial.Icon3.cmd_map_marker_radius
|
||||
else -> CommunityMaterial.Icon.cmd_bookmark
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package io.homeassistant.companion.android.home.views
|
||||
package io.homeassistant.companion.android.views
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.wear.compose.material.Chip
|
||||
|
@ -19,44 +22,50 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria
|
|||
import io.homeassistant.companion.android.common.data.integration.Entity
|
||||
import io.homeassistant.companion.android.common.data.integration.domain
|
||||
import io.homeassistant.companion.android.data.SimplifiedEntity
|
||||
import io.homeassistant.companion.android.home.MainViewModel
|
||||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import io.homeassistant.companion.android.util.getIcon
|
||||
import io.homeassistant.companion.android.util.stringForDomain
|
||||
import java.util.Locale
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@Composable
|
||||
fun ChooseEntityView(
|
||||
mainViewModel: MainViewModel,
|
||||
entitiesByDomainOrder: SnapshotStateList<String>,
|
||||
entitiesByDomain: SnapshotStateMap<String, SnapshotStateList<Entity<*>>>,
|
||||
onNoneClicked: () -> Unit,
|
||||
onEntitySelected: (entity: SimplifiedEntity) -> Unit
|
||||
onEntitySelected: (entity: SimplifiedEntity) -> Unit,
|
||||
allowNone: Boolean = true
|
||||
) {
|
||||
// Remember expanded state of each header
|
||||
val expandedStates = rememberExpandedStates(mainViewModel.supportedDomains())
|
||||
val expandedStates = rememberExpandedStates(entitiesByDomainOrder)
|
||||
|
||||
WearAppTheme {
|
||||
ThemeLazyColumn {
|
||||
item {
|
||||
ListHeader(id = commonR.string.shortcuts_choose)
|
||||
ListHeader(id = commonR.string.choose_entity)
|
||||
}
|
||||
item {
|
||||
Chip(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp),
|
||||
icon = { Image(asset = CommunityMaterial.Icon.cmd_delete) },
|
||||
label = { Text(stringResource(id = commonR.string.none)) },
|
||||
onClick = onNoneClicked,
|
||||
colors = ChipDefaults.primaryChipColors(
|
||||
contentColor = Color.Black
|
||||
if (allowNone) {
|
||||
item {
|
||||
Chip(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp),
|
||||
icon = { Image(asset = CommunityMaterial.Icon.cmd_delete) },
|
||||
label = { Text(stringResource(id = commonR.string.none)) },
|
||||
onClick = onNoneClicked,
|
||||
colors = ChipDefaults.primaryChipColors(
|
||||
contentColor = Color.Black
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
for (domain in mainViewModel.entitiesByDomainOrder) {
|
||||
val entities = mainViewModel.entitiesByDomain[domain]
|
||||
for (domain in entitiesByDomainOrder) {
|
||||
val entities = entitiesByDomain[domain]
|
||||
if (!entities.isNullOrEmpty()) {
|
||||
item {
|
||||
ExpandableListHeader(
|
||||
string = mainViewModel.stringForDomain(domain)!!,
|
||||
string = stringForDomain(domain, LocalContext.current)
|
||||
?: domain.replace('_', ' ').capitalize(Locale.getDefault()),
|
||||
key = domain,
|
||||
expandedStates = expandedStates
|
||||
)
|
||||
|
@ -91,7 +100,7 @@ private fun ChooseEntityChip(
|
|||
.fillMaxWidth(),
|
||||
icon = {
|
||||
Image(
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone,
|
||||
asset = iconBitmap ?: CommunityMaterial.Icon.cmd_bookmark,
|
||||
colorFilter = ColorFilter.tint(Color.White)
|
||||
)
|
||||
},
|
|
@ -1,4 +1,4 @@
|
|||
package io.homeassistant.companion.android.home.views
|
||||
package io.homeassistant.companion.android.views
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
|
@ -1,4 +1,4 @@
|
|||
package io.homeassistant.companion.android.home.views
|
||||
package io.homeassistant.companion.android.views
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Row
|
|
@ -1,4 +1,4 @@
|
|||
package io.homeassistant.companion.android.home.views
|
||||
package io.homeassistant.companion.android.views
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
9
wear/src/main/res/drawable/ic_lightbulb.xml
Normal file
9
wear/src/main/res/drawable/ic_lightbulb.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF03A9F4"
|
||||
android:pathData="M12,2A7,7 0,0 0,5 9C5,11.38 6.19,13.47 8,14.74V17A1,1 0,0 0,9 18H15A1,1 0,0 0,16 17V14.74C17.81,13.47 19,11.38 19,9A7,7 0,0 0,12 2M9,21A1,1 0,0 0,10 22H14A1,1 0,0 0,15 21V20H9V21Z"/>
|
||||
</vector>
|
Loading…
Reference in a new issue