mirror of
https://github.com/home-assistant/android
synced 2024-10-15 20:43:06 +00:00
Better state change subscriptions in entity state and media player widgets (#2814)
Implement subscribe_trigger and use it in widgets - Implement support for subscribing using subscribe_trigger, currently used to get state changes only for specific entities - Change the entity state and media player widgets to use this new subscription instead of subscribing to all state changes, based on the template widget - Removed some placeholder code from media player widget
This commit is contained in:
parent
2cf5a7d215
commit
232fbb2b2d
|
@ -15,10 +15,13 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A widget provider class for widgets that update based on entity state changes.
|
||||
*/
|
||||
abstract class BaseWidgetProvider : AppWidgetProvider() {
|
||||
|
||||
companion object {
|
||||
|
@ -26,15 +29,29 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
|
|||
"io.homeassistant.companion.android.widgets.template.BaseWidgetProvider.UPDATE_VIEW"
|
||||
const val RECEIVE_DATA =
|
||||
"io.homeassistant.companion.android.widgets.template.TemplateWidget.RECEIVE_DATA"
|
||||
}
|
||||
|
||||
private var entityUpdates: Flow<Entity<*>>? = null
|
||||
var widgetScope: CoroutineScope? = null
|
||||
val widgetEntities = mutableMapOf<Int, List<String>>()
|
||||
val widgetJobs = mutableMapOf<Int, Job>()
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected lateinit var integrationUseCase: IntegrationRepository
|
||||
protected var mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
|
||||
private var thisSetScope = false
|
||||
protected var lastIntent = ""
|
||||
|
||||
init {
|
||||
setupWidgetScope()
|
||||
}
|
||||
|
||||
private fun setupWidgetScope() {
|
||||
if (widgetScope == null || !widgetScope!!.isActive) {
|
||||
widgetScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
thisSetScope = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUpdate(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
|
@ -42,7 +59,7 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
|
|||
) {
|
||||
// There may be multiple widgets active, so update all of them
|
||||
for (appWidgetId in appWidgetIds) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
val views = getWidgetRemoteViews(context, appWidgetId)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
|
@ -70,23 +87,35 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
|
|||
}
|
||||
|
||||
fun onScreenOn(context: Context) {
|
||||
mainScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
if (!isSubscribed()) {
|
||||
mainScope.launch {
|
||||
if (!integrationUseCase.isRegistered()) {
|
||||
return@launch
|
||||
}
|
||||
updateAllWidgets(context)
|
||||
if (getAllWidgetIds(context).isNotEmpty()) {
|
||||
context.applicationContext.registerReceiver(
|
||||
this@BaseWidgetProvider,
|
||||
IntentFilter(Intent.ACTION_SCREEN_OFF)
|
||||
)
|
||||
setupWidgetScope()
|
||||
widgetScope!!.launch {
|
||||
if (!integrationUseCase.isRegistered()) {
|
||||
return@launch
|
||||
}
|
||||
updateAllWidgets(context)
|
||||
|
||||
entityUpdates = integrationUseCase.getEntityUpdates()
|
||||
setSubscribed(entityUpdates != null)
|
||||
entityUpdates?.collect {
|
||||
onEntityStateChanged(context, it)
|
||||
val allWidgets = getAllWidgetIdsWithEntities(context)
|
||||
val widgetsWithDifferentEntities = allWidgets.filter { it.value != widgetEntities[it.key] }
|
||||
if (widgetsWithDifferentEntities.isNotEmpty()) {
|
||||
context.applicationContext.registerReceiver(
|
||||
this@BaseWidgetProvider,
|
||||
IntentFilter(Intent.ACTION_SCREEN_OFF)
|
||||
)
|
||||
|
||||
widgetsWithDifferentEntities.forEach { (id, entities) ->
|
||||
widgetJobs[id]?.cancel()
|
||||
|
||||
val entityUpdates = integrationUseCase.getEntityUpdates(entities)
|
||||
if (entityUpdates != null) {
|
||||
widgetEntities[id] = entities
|
||||
widgetJobs[id] = widgetScope!!.launch {
|
||||
entityUpdates.collect {
|
||||
onEntityStateChanged(context, id, it)
|
||||
}
|
||||
}
|
||||
} else { // Remove data to make it retry on the next update
|
||||
widgetEntities.remove(id)
|
||||
widgetJobs.remove(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,9 +123,12 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
|
|||
}
|
||||
|
||||
private fun onScreenOff() {
|
||||
mainScope.cancel()
|
||||
entityUpdates = null
|
||||
setSubscribed(false)
|
||||
if (thisSetScope) {
|
||||
widgetScope?.cancel()
|
||||
thisSetScope = false
|
||||
widgetEntities.clear()
|
||||
widgetJobs.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateAllWidgets(
|
||||
|
@ -106,7 +138,7 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
|
|||
val systemWidgetIds = AppWidgetManager.getInstance(context)
|
||||
.getAppWidgetIds(widgetProvider)
|
||||
.toSet()
|
||||
val dbWidgetIds = getAllWidgetIds(context)
|
||||
val dbWidgetIds = getAllWidgetIdsWithEntities(context).keys
|
||||
|
||||
val invalidWidgetIds = dbWidgetIds.minus(systemWidgetIds)
|
||||
if (invalidWidgetIds.isNotEmpty()) {
|
||||
|
@ -127,17 +159,21 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
|
|||
appWidgetId: Int,
|
||||
appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(context)
|
||||
) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
val views = getWidgetRemoteViews(context, appWidgetId)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun isSubscribed(): Boolean
|
||||
abstract fun setSubscribed(subscribed: Boolean)
|
||||
protected fun removeSubscription(appWidgetId: Int) {
|
||||
widgetEntities.remove(appWidgetId)
|
||||
widgetJobs[appWidgetId]?.cancel()
|
||||
widgetJobs.remove(appWidgetId)
|
||||
}
|
||||
|
||||
abstract fun getWidgetProvider(context: Context): ComponentName
|
||||
abstract suspend fun getWidgetRemoteViews(context: Context, appWidgetId: Int, suggestedEntity: Entity<Map<String, Any>>? = null): RemoteViews
|
||||
abstract suspend fun getAllWidgetIds(context: Context): List<Int>
|
||||
abstract suspend fun getAllWidgetIdsWithEntities(context: Context): Map<Int, List<String>>
|
||||
abstract fun saveEntityConfiguration(context: Context, extras: Bundle?, appWidgetId: Int)
|
||||
abstract suspend fun onEntityStateChanged(context: Context, entity: Entity<*>)
|
||||
abstract suspend fun onEntityStateChanged(context: Context, appWidgetId: Int, entity: Entity<*>)
|
||||
}
|
||||
|
|
|
@ -42,19 +42,11 @@ class EntityWidget : BaseWidgetProvider() {
|
|||
internal const val EXTRA_TEXT_COLOR = "EXTRA_TEXT_COLOR"
|
||||
|
||||
private data class ResolvedText(val text: CharSequence?, val exception: Boolean = false)
|
||||
|
||||
private var isSubscribed = false
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var staticWidgetDao: StaticWidgetDao
|
||||
|
||||
override fun isSubscribed(): Boolean = isSubscribed
|
||||
|
||||
override fun setSubscribed(subscribed: Boolean) {
|
||||
isSubscribed = subscribed
|
||||
}
|
||||
|
||||
override fun getWidgetProvider(context: Context): ComponentName =
|
||||
ComponentName(context, EntityWidget::class.java)
|
||||
|
||||
|
@ -130,9 +122,8 @@ class EntityWidget : BaseWidgetProvider() {
|
|||
return views
|
||||
}
|
||||
|
||||
override suspend fun getAllWidgetIds(context: Context): List<Int> {
|
||||
return staticWidgetDao.getAll().map { it.id }
|
||||
}
|
||||
override suspend fun getAllWidgetIdsWithEntities(context: Context): Map<Int, List<String>> =
|
||||
staticWidgetDao.getAll().associate { it.id to listOf(it.entityId) }
|
||||
|
||||
private suspend fun resolveTextToShow(
|
||||
context: Context,
|
||||
|
@ -197,7 +188,7 @@ class EntityWidget : BaseWidgetProvider() {
|
|||
return
|
||||
}
|
||||
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Saving entity state config data:" + System.lineSeparator() +
|
||||
|
@ -223,20 +214,17 @@ class EntityWidget : BaseWidgetProvider() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun onEntityStateChanged(context: Context, entity: Entity<*>) {
|
||||
staticWidgetDao.getAll().forEach {
|
||||
if (it.entityId == entity.entityId) {
|
||||
mainScope.launch {
|
||||
val views = getWidgetRemoteViews(context, it.id, entity as Entity<Map<String, Any>>)
|
||||
AppWidgetManager.getInstance(context).updateAppWidget(it.id, views)
|
||||
}
|
||||
}
|
||||
override suspend fun onEntityStateChanged(context: Context, appWidgetId: Int, entity: Entity<*>) {
|
||||
widgetScope?.launch {
|
||||
val views = getWidgetRemoteViews(context, appWidgetId, entity as Entity<Map<String, Any>>)
|
||||
AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
staticWidgetDao.deleteAll(appWidgetIds)
|
||||
appWidgetIds.forEach { removeSubscription(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,8 +64,6 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
internal const val EXTRA_SHOW_SKIP = "EXTRA_INCLUDE_SKIP"
|
||||
internal const val EXTRA_SHOW_SEEK = "EXTRA_INCLUDE_SEEK"
|
||||
internal const val EXTRA_BACKGROUND_TYPE = "EXTRA_BACKGROUND_TYPE"
|
||||
|
||||
private var isSubscribed = false
|
||||
}
|
||||
|
||||
@Inject
|
||||
|
@ -74,12 +72,6 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
@Inject
|
||||
lateinit var mediaPlayCtrlWidgetDao: MediaPlayerControlsWidgetDao
|
||||
|
||||
override fun isSubscribed(): Boolean = isSubscribed
|
||||
|
||||
override fun setSubscribed(subscribed: Boolean) {
|
||||
isSubscribed = subscribed
|
||||
}
|
||||
|
||||
override fun getWidgetProvider(context: Context): ComponentName =
|
||||
ComponentName(context, MediaPlayerControlsWidget::class.java)
|
||||
|
||||
|
@ -107,7 +99,7 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
Log.d(TAG, "Skipping widget update since network connection is not active")
|
||||
return
|
||||
}
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
val views = getWidgetRemoteViews(context, appWidgetId)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
onScreenOn(context)
|
||||
|
@ -412,9 +404,8 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getAllWidgetIds(context: Context): List<Int> {
|
||||
return mediaPlayCtrlWidgetDao.getAll().map { it.id }
|
||||
}
|
||||
override suspend fun getAllWidgetIdsWithEntities(context: Context): Map<Int, List<String>> =
|
||||
mediaPlayCtrlWidgetDao.getAll().associate { it.id to it.entityId.split(",") }
|
||||
|
||||
private suspend fun getEntity(context: Context, entityIds: List<String>, suggestedEntity: Entity<Map<String, Any>>?): Entity<Map<String, Any>>? {
|
||||
val entity: Entity<Map<String, Any>>?
|
||||
|
@ -488,7 +479,7 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
return
|
||||
}
|
||||
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Saving service call config data:" + System.lineSeparator() +
|
||||
|
@ -511,20 +502,17 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun onEntityStateChanged(context: Context, entity: Entity<*>) {
|
||||
mediaPlayCtrlWidgetDao.getAll().forEach {
|
||||
val entityIds = it.entityId.split(",")
|
||||
if (entityIds.contains(entity.entityId)) {
|
||||
mainScope.launch {
|
||||
val views = getWidgetRemoteViews(context, it.id, getEntity(context, entityIds, null))
|
||||
AppWidgetManager.getInstance(context).updateAppWidget(it.id, views)
|
||||
}
|
||||
override suspend fun onEntityStateChanged(context: Context, appWidgetId: Int, entity: Entity<*>) {
|
||||
mediaPlayCtrlWidgetDao.get(appWidgetId)?.let {
|
||||
widgetScope?.launch {
|
||||
val views = getWidgetRemoteViews(context, appWidgetId, getEntity(context, it.entityId.split(","), null))
|
||||
AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun callPreviousTrackService(context: Context, appWidgetId: Int) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(TAG, "Retrieving media player entity for app widget $appWidgetId")
|
||||
val entity: MediaPlayerControlsWidgetEntity? = mediaPlayCtrlWidgetDao.get(appWidgetId)
|
||||
|
||||
|
@ -550,7 +538,7 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
|
||||
private fun callRewindService(context: Context, appWidgetId: Int) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(TAG, "Retrieving media player entity for app widget $appWidgetId")
|
||||
val entity: MediaPlayerControlsWidgetEntity? = mediaPlayCtrlWidgetDao.get(appWidgetId)
|
||||
|
||||
|
@ -595,7 +583,7 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
|
||||
private fun callPlayPauseService(context: Context, appWidgetId: Int) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(TAG, "Retrieving media player entity for app widget $appWidgetId")
|
||||
val entity: MediaPlayerControlsWidgetEntity? = mediaPlayCtrlWidgetDao.get(appWidgetId)
|
||||
|
||||
|
@ -621,7 +609,7 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
|
||||
private fun callFastForwardService(context: Context, appWidgetId: Int) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(TAG, "Retrieving media player entity for app widget $appWidgetId")
|
||||
val entity: MediaPlayerControlsWidgetEntity? = mediaPlayCtrlWidgetDao.get(appWidgetId)
|
||||
|
||||
|
@ -666,7 +654,7 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
|
||||
private fun callNextTrackService(context: Context, appWidgetId: Int) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(TAG, "Retrieving media player entity for app widget $appWidgetId")
|
||||
val entity: MediaPlayerControlsWidgetEntity? = mediaPlayCtrlWidgetDao.get(appWidgetId)
|
||||
|
||||
|
@ -692,7 +680,7 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
|
||||
private fun callVolumeDownService(context: Context, appWidgetId: Int) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(TAG, "Retrieving media player entity for app widget $appWidgetId")
|
||||
val entity: MediaPlayerControlsWidgetEntity? = mediaPlayCtrlWidgetDao.get(appWidgetId)
|
||||
|
||||
|
@ -718,7 +706,7 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
|
||||
private fun callVolumeUpService(context: Context, appWidgetId: Int) {
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
Log.d(TAG, "Retrieving media player entity for app widget $appWidgetId")
|
||||
val entity: MediaPlayerControlsWidgetEntity? = mediaPlayCtrlWidgetDao.get(appWidgetId)
|
||||
|
||||
|
@ -744,20 +732,12 @@ class MediaPlayerControlsWidget : BaseWidgetProvider() {
|
|||
}
|
||||
|
||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||
// When the user deletes the widget, delete the preference associated with it.
|
||||
mainScope.launch {
|
||||
widgetScope?.launch {
|
||||
mediaPlayCtrlWidgetDao.deleteAll(appWidgetIds)
|
||||
appWidgetIds.forEach { removeSubscription(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
// Enter relevant functionality for when the last widget is disabled
|
||||
}
|
||||
|
||||
private fun isConnectionActive(context: Context): Boolean {
|
||||
val connectivityManager = context.getSystemService<ConnectivityManager>()
|
||||
val activeNetworkInfo = connectivityManager?.activeNetworkInfo
|
||||
|
|
|
@ -64,6 +64,7 @@ interface IntegrationRepository {
|
|||
suspend fun getEntities(): List<Entity<Any>>?
|
||||
suspend fun getEntity(entityId: String): Entity<Map<String, Any>>?
|
||||
suspend fun getEntityUpdates(): Flow<Entity<*>>?
|
||||
suspend fun getEntityUpdates(entityIds: List<String>): Flow<Entity<*>>?
|
||||
|
||||
suspend fun callService(domain: String, service: String, serviceData: HashMap<String, Any>)
|
||||
|
||||
|
|
|
@ -589,6 +589,21 @@ class IntegrationRepositoryImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getEntityUpdates(entityIds: List<String>): Flow<Entity<*>>? {
|
||||
return webSocketRepository.getStateChanges(entityIds)
|
||||
?.filter { it.toState != null }
|
||||
?.map {
|
||||
Entity(
|
||||
it.toState!!.entityId,
|
||||
it.toState.state,
|
||||
it.toState.attributes,
|
||||
it.toState.lastChanged,
|
||||
it.toState.lastUpdated,
|
||||
it.toState.context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun registerSensor(sensorRegistration: SensorRegistration<Any>) {
|
||||
val canRegisterCategoryStateClass = isHomeAssistantVersionAtLeast(2021, 11, 0)
|
||||
val canRegisterEntityDisabledState = isHomeAssistantVersionAtLeast(2022, 6, 0)
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.En
|
|||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.GetConfigResponse
|
||||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.StateChangedEvent
|
||||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.TemplateUpdatedEvent
|
||||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.TriggerEvent
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface WebSocketRepository {
|
||||
|
@ -23,6 +24,7 @@ interface WebSocketRepository {
|
|||
suspend fun getEntityRegistry(): List<EntityRegistryResponse>?
|
||||
suspend fun getServices(): List<DomainResponse>?
|
||||
suspend fun getStateChanges(): Flow<StateChangedEvent>?
|
||||
suspend fun getStateChanges(entityIds: List<String>): Flow<TriggerEvent>?
|
||||
suspend fun getAreaRegistryUpdates(): Flow<AreaRegistryUpdatedEvent>?
|
||||
suspend fun getDeviceRegistryUpdates(): Flow<DeviceRegistryUpdatedEvent>?
|
||||
suspend fun getEntityRegistryUpdates(): Flow<EntityRegistryUpdatedEvent>?
|
||||
|
|
|
@ -29,6 +29,7 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.Ge
|
|||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.SocketResponse
|
||||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.StateChangedEvent
|
||||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.TemplateUpdatedEvent
|
||||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.TriggerEvent
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -67,6 +68,7 @@ class WebSocketRepositoryImpl @Inject constructor(
|
|||
private const val TAG = "WebSocketRepository"
|
||||
|
||||
private const val SUBSCRIBE_TYPE_SUBSCRIBE_EVENTS = "subscribe_events"
|
||||
private const val SUBSCRIBE_TYPE_SUBSCRIBE_TRIGGER = "subscribe_trigger"
|
||||
private const val SUBSCRIBE_TYPE_RENDER_TEMPLATE = "render_template"
|
||||
private const val EVENT_STATE_CHANGED = "state_changed"
|
||||
private const val EVENT_AREA_REGISTRY_UPDATED = "area_registry_updated"
|
||||
|
@ -176,6 +178,9 @@ class WebSocketRepositoryImpl @Inject constructor(
|
|||
override suspend fun getStateChanges(): Flow<StateChangedEvent>? =
|
||||
subscribeToEventsForType(EVENT_STATE_CHANGED)
|
||||
|
||||
override suspend fun getStateChanges(entityIds: List<String>): Flow<TriggerEvent>? =
|
||||
subscribeToTrigger("state", mapOf("entity_id" to entityIds))
|
||||
|
||||
override suspend fun getAreaRegistryUpdates(): Flow<AreaRegistryUpdatedEvent>? =
|
||||
subscribeToEventsForType(EVENT_AREA_REGISTRY_UPDATED)
|
||||
|
||||
|
@ -191,6 +196,13 @@ class WebSocketRepositoryImpl @Inject constructor(
|
|||
override suspend fun getTemplateUpdates(template: String): Flow<TemplateUpdatedEvent>? =
|
||||
subscribeTo(SUBSCRIBE_TYPE_RENDER_TEMPLATE, mapOf("template" to template))
|
||||
|
||||
private suspend fun subscribeToTrigger(platform: String, data: Map<Any, Any>): Flow<TriggerEvent>? {
|
||||
val triggerData = mapOf(
|
||||
"platform" to platform
|
||||
).plus(data)
|
||||
return subscribeTo(SUBSCRIBE_TYPE_SUBSCRIBE_TRIGGER, mapOf("trigger" to triggerData))
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a subscription for events on the websocket connection and get a Flow for listening to
|
||||
* new messages. When there are no more listeners, the subscription will automatically be cancelled
|
||||
|
@ -409,6 +421,14 @@ class WebSocketRepositoryImpl @Inject constructor(
|
|||
val message: Any =
|
||||
if (subscriptionType == SUBSCRIBE_TYPE_RENDER_TEMPLATE) {
|
||||
mapper.convertValue(response.event, TemplateUpdatedEvent::class.java)
|
||||
} else if (subscriptionType == SUBSCRIBE_TYPE_SUBSCRIBE_TRIGGER) {
|
||||
val trigger = response.event?.get("variables")?.get("trigger")
|
||||
if (trigger != null) {
|
||||
mapper.convertValue(trigger, TriggerEvent::class.java)
|
||||
} else {
|
||||
Log.w(TAG, "Received no trigger value for trigger subscription, skipping")
|
||||
return
|
||||
}
|
||||
} else if (eventResponseType != null && eventResponseType.isTextual) {
|
||||
val eventResponseClass = when (eventResponseType.textValue()) {
|
||||
EVENT_STATE_CHANGED ->
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package io.homeassistant.companion.android.common.data.websocket.impl.entities
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import io.homeassistant.companion.android.common.data.integration.Entity
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class TriggerEvent(
|
||||
val platform: String,
|
||||
val entityId: String?,
|
||||
val fromState: Entity<*>?,
|
||||
val toState: Entity<*>?
|
||||
)
|
Loading…
Reference in a new issue