Only subscribe to supported entities on Wear OS home screen (#3103)

* Only subscribe to favorite entities on Wear OS homescreen

* Switch to subscribing to all supported domains

* Update entity states when app is resumed and entities are loaded to keep things in sync

* Only update domains when needed

* Update all registeries when we resume

* Move UI update to view model

* Review comment

* Clear values before updates

* Process review comments
This commit is contained in:
Daniel Shokouhi 2022-11-30 11:59:57 -08:00 committed by GitHub
parent 8563efef5f
commit d5714d85e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 33 deletions

View file

@ -15,6 +15,7 @@ import io.homeassistant.companion.android.onboarding.OnboardingActivity
import io.homeassistant.companion.android.onboarding.integration.MobileAppIntegrationActivity
import io.homeassistant.companion.android.sensors.SensorReceiver
import io.homeassistant.companion.android.sensors.SensorWorker
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -26,6 +27,8 @@ class HomeActivity : ComponentActivity(), HomeView {
private val mainViewModel by viewModels<MainViewModel>()
private var entityUpdateJob: Job? = null
companion object {
private const val TAG = "HomeActivity"
@ -48,7 +51,12 @@ class HomeActivity : ComponentActivity(), HomeView {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
launch { mainViewModel.entityUpdates() }
launch {
mainViewModel.supportedEntities.collect {
if (entityUpdateJob?.isActive == true) entityUpdateJob?.cancel()
entityUpdateJob = launch { mainViewModel.entityUpdates() }
}
}
launch { mainViewModel.areaUpdates() }
launch { mainViewModel.deviceUpdates() }
launch { mainViewModel.entityRegistryUpdates() }
@ -61,6 +69,11 @@ class HomeActivity : ComponentActivity(), HomeView {
SensorWorker.start(this)
mainViewModel.initAllSensors()
lifecycleScope.launch {
if (mainViewModel.loadingState.value == MainViewModel.LoadingState.READY)
mainViewModel.updateUI()
}
}
override fun onPause() {

View file

@ -28,7 +28,7 @@ interface HomePresenter {
fun getWebSocketState(): WebSocketState?
suspend fun getEntities(): List<Entity<*>>?
suspend fun getEntityUpdates(): Flow<Entity<*>>?
suspend fun getEntityUpdates(entityIds: List<String>): Flow<Entity<*>>?
suspend fun getAreaRegistry(): List<AreaRegistryResponse>?
suspend fun getDeviceRegistry(): List<DeviceRegistryResponse>?

View file

@ -77,8 +77,8 @@ class HomePresenterImpl @Inject constructor(
return integrationUseCase.getEntities()
}
override suspend fun getEntityUpdates(): Flow<Entity<*>>? {
return integrationUseCase.getEntityUpdates()
override suspend fun getEntityUpdates(entityIds: List<String>): Flow<Entity<*>>? {
return integrationUseCase.getEntityUpdates(entityIds)
}
override suspend fun onEntityClicked(entityId: String, state: String) {

View file

@ -26,9 +26,13 @@ import io.homeassistant.companion.android.database.wear.FavoritesDao
import io.homeassistant.companion.android.database.wear.getAllFlow
import io.homeassistant.companion.android.sensors.SensorReceiver
import io.homeassistant.companion.android.util.RegistriesDataHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@HiltViewModel
@ -63,6 +67,9 @@ class MainViewModel @Inject constructor(
var entities = mutableStateMapOf<String, Entity<*>>()
private set
private val _supportedEntities = MutableStateFlow(emptyList<String>())
val supportedEntities = _supportedEntities.asStateFlow()
/**
* IDs of favorites in the Favorites database.
*/
@ -133,27 +140,7 @@ class MainViewModel @Inject constructor(
try {
// Load initial state
loadingState.value = LoadingState.LOADING
val getAreaRegistry = async { homePresenter.getAreaRegistry() }
val getDeviceRegistry = async { homePresenter.getDeviceRegistry() }
val getEntityRegistry = async { homePresenter.getEntityRegistry() }
val getEntities = async { homePresenter.getEntities() }
areaRegistry = getAreaRegistry.await()?.also {
areas.addAll(it)
}
deviceRegistry = getDeviceRegistry.await()
entityRegistry = getEntityRegistry.await()
getEntities.await()?.forEach {
if (supportedDomains().contains(it.domain)) {
entities[it.entityId] = it
// add to cache if part of favorites
if (favoriteEntityIds.value.contains(it.entityId)) {
addCachedFavorite(it.entityId)
}
}
}
updateEntityDomains()
updateUI()
// Finished initial load, update state
val webSocketState = homePresenter.getWebSocketState()
@ -173,14 +160,44 @@ class MainViewModel @Inject constructor(
}
}
private fun updateEntityStates(entity: Entity<*>) {
if (supportedDomains().contains(entity.domain)) {
entities[entity.entityId] = entity
// add to cache if part of favorites
if (favoriteEntityIds.value.contains(entity.entityId)) {
addCachedFavorite(entity.entityId)
}
}
}
suspend fun updateUI() = withContext(Dispatchers.IO) {
val getAreaRegistry = async { homePresenter.getAreaRegistry() }
val getDeviceRegistry = async { homePresenter.getDeviceRegistry() }
val getEntityRegistry = async { homePresenter.getEntityRegistry() }
val getEntities = async { homePresenter.getEntities() }
areaRegistry = getAreaRegistry.await()?.also {
areas.clear()
areas.addAll(it)
}
deviceRegistry = getDeviceRegistry.await()
entityRegistry = getEntityRegistry.await()
_supportedEntities.value = getSupportedEntities()
getEntities.await()?.also {
entities.clear()
it.forEach { state -> updateEntityStates(state) }
}
updateEntityDomains()
}
suspend fun entityUpdates() {
if (!homePresenter.isConnected())
return
homePresenter.getEntityUpdates()?.collect {
if (supportedDomains().contains(it.domain)) {
entities[it.entityId] = it
updateEntityDomains()
}
homePresenter.getEntityUpdates(supportedEntities.value)?.collect {
updateEntityStates(it)
updateEntityDomains()
}
}
@ -211,11 +228,18 @@ class MainViewModel @Inject constructor(
return
homePresenter.getEntityRegistryUpdates()?.collect {
entityRegistry = homePresenter.getEntityRegistry()
_supportedEntities.value = getSupportedEntities()
updateEntityDomains()
}
}
fun updateEntityDomains() {
private fun getSupportedEntities(): List<String> =
entityRegistry
.orEmpty()
.map { it.entityId }
.filter { it.split(".")[0] in supportedDomains() }
private fun updateEntityDomains() {
val entitiesList = entities.values.toList().sortedBy { it.entityId }
val areasList = areaRegistry.orEmpty().sortedBy { it.name }
val domainsList = entitiesList.map { it.domain }.distinct()

View file

@ -151,14 +151,14 @@ private fun PreviewEntityUI() {
onEntityClicked = { _, _ -> },
isHapticEnabled = true,
isToastEnabled = false,
onEntityLongPressed = { _ -> }
onEntityLongPressed = { }
)
EntityUi(
entity = previewEntity3,
onEntityClicked = { _, _ -> },
isHapticEnabled = false,
isToastEnabled = true,
onEntityLongPressed = { _ -> }
onEntityLongPressed = { }
)
}
}