@@ -20,22 +20,16 @@ import com.mikepenz.iconics.IconicsDrawable
2020import com.mikepenz.iconics.utils.sizeDp
2121import com.mikepenz.iconics.utils.toAndroidIconCompat
2222import io.homeassistant.companion.android.common.R
23- import io.homeassistant.companion.android.common.data.integration.Entity
2423import io.homeassistant.companion.android.common.data.integration.EntityExt
2524import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
26- import io.homeassistant.companion.android.common.data.integration.friendlyName
27- import io.homeassistant.companion.android.common.data.integration.friendlyState
28- import io.homeassistant.companion.android.common.data.integration.getIcon
29- import io.homeassistant.companion.android.common.data.integration.isActive
30- import io.homeassistant.companion.android.common.data.integration.isExecuting
31- import io.homeassistant.companion.android.common.data.integration.onPressed
25+ import io.homeassistant.companion.android.common.data.integration.display.EntityDisplayItem
26+ import io.homeassistant.companion.android.common.data.integration.display.EntityDisplayState
27+ import io.homeassistant.companion.android.common.data.integration.onEntityPressed
3228import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
3329import io.homeassistant.companion.android.common.data.servers.ServerManager
34- import io.homeassistant.companion.android.common.data.websocket.impl.entities.EntityRegistryResponse
3530import io.homeassistant.companion.android.util.vehicle.MAP_DOMAINS
3631import io.homeassistant.companion.android.util.vehicle.NOT_ACTIONABLE_DOMAINS
3732import io.homeassistant.companion.android.util.vehicle.SUPPORTED_DOMAINS
38- import io.homeassistant.companion.android.util.vehicle.alarmHasNoCode
3933import io.homeassistant.companion.android.util.vehicle.canNavigate
4034import io.homeassistant.companion.android.util.vehicle.getDomainList
4135import io.homeassistant.companion.android.util.vehicle.getDomainsGridItem
@@ -55,20 +49,15 @@ class EntityGridVehicleScreen(
5549 val prefsRepository : PrefsRepository ,
5650 val integrationRepositoryProvider : suspend () -> IntegrationRepository ,
5751 val title : String ,
58- private val entityRegistry : List <EntityRegistryResponse >? ,
5952 private val domains : MutableSet <String >,
60- private val entitiesFlow : Flow <List <Entity >>,
61- private val allEntities : Flow <Map < String , Entity > >,
53+ private val entitiesFlow : Flow <List <EntityDisplayItem >>,
54+ private val entitiesState : Flow <EntityDisplayState >,
6255) : Screen(carContext) {
6356
6457 private var loading = true
65- var entities: List <Entity > = listOf ()
58+ var entities: List <EntityDisplayItem > = listOf ()
6659 private val isFavorites = title == carContext.getString(R .string.favorites)
6760
68- // Index the registry by entity ID so each grid item can look up its options (such as the
69- // sensor display precision) without scanning the whole registry on every render
70- private val entityRegistryOptions = entityRegistry?.associate { it.entityId to it.options }
71-
7261 init {
7362 lifecycleScope.launch {
7463 lifecycle.repeatOnLifecycle(Lifecycle .State .STARTED ) {
@@ -92,7 +81,7 @@ class EntityGridVehicleScreen(
9281 * @param canSwitchServers If `true` and the function is called for favorites, the item limit is adjusted to keep
9382 * space for a 'Switch server' item
9483 */
95- fun getEntityGridItems (entities : List <Entity >, canSwitchServers : Boolean ): ItemList .Builder {
84+ fun getEntityGridItems (entities : List <EntityDisplayItem >, canSwitchServers : Boolean ): ItemList .Builder {
9685 val listBuilder = if (entities.isNotEmpty()) {
9786 createEntityGrid(entities, canSwitchServers)
9887 } else {
@@ -103,8 +92,7 @@ class EntityGridVehicleScreen(
10392 serverManager,
10493 serverId,
10594 prefsRepository,
106- allEntities,
107- entityRegistry,
95+ entitiesState,
10896 lifecycleScope,
10997 )
11098 }
@@ -114,8 +102,7 @@ class EntityGridVehicleScreen(
114102 carContext,
115103 screenManager,
116104 integrationRepositoryProvider,
117- allEntities,
118- entityRegistry,
105+ entitiesState,
119106 ).build(),
120107 )
121108 if (domains.isNotEmpty()) {
@@ -125,9 +112,8 @@ class EntityGridVehicleScreen(
125112 screenManager,
126113 serverManager,
127114 serverId,
128- allEntities ,
115+ entitiesState ,
129116 prefsRepository,
130- entityRegistry,
131117 ).build(),
132118 )
133119 }
@@ -149,50 +135,51 @@ class EntityGridVehicleScreen(
149135 }.build()
150136 }
151137
152- private fun createEntityGrid (entities : List <Entity >, canSwitchServers : Boolean ): ItemList .Builder {
138+ private fun createEntityGrid (entities : List <EntityDisplayItem >, canSwitchServers : Boolean ): ItemList .Builder {
153139 val listBuilder = ItemList .Builder ()
154140 val manager = carContext.getCarService(ConstraintManager ::class .java)
155141 val gridLimit = manager.getContentLimit(ConstraintManager .CONTENT_LIMIT_TYPE_GRID )
156142 val extraGrid = if (canSwitchServers) 3 else 2
157- entities.forEachIndexed { index, entity ->
143+ entities.forEachIndexed { index, displayed ->
158144 if (index >= (gridLimit - if (isFavorites) extraGrid else 0 )) {
159145 Timber .i(" Grid limit ($gridLimit ) reached, not adding more entities (${entities.size} ) for $title " )
160146 return @forEachIndexed
161147 }
162- val icon = entity.getIcon()
163148 val gridItem =
164149 GridItem .Builder ()
165150 .setLoading(false )
166- .setTitle(entity.friendlyName.ifEmpty { entity.entityId } )
167- .setText(entity.friendlyState(carContext, options = entityRegistryOptions?.get(entity.entityId) ))
151+ .setTitle(displayed.name )
152+ .setText(displayed.state.resolve(carContext ))
168153
169- if (entity .isExecuting() ) {
170- gridItem.setLoading(entity .isExecuting() )
154+ if (displayed .isExecuting) {
155+ gridItem.setLoading(displayed .isExecuting)
171156 } else {
172- if (entity.domain !in NOT_ACTIONABLE_DOMAINS || canNavigate(entity) || alarmHasNoCode(entity)) {
157+ if (displayed.domain !in NOT_ACTIONABLE_DOMAINS ||
158+ canNavigate(displayed) ||
159+ displayed.alarm?.hasNoCode == true
160+ ) {
173161 gridItem
174162 .setOnClickListener {
175- Timber .i(" ${entity .entityId} clicked" )
176- when (entity .domain) {
163+ Timber .i(" ${displayed .entityId} clicked" )
164+ when (displayed .domain) {
177165 in MAP_DOMAINS -> {
178- val attrs = entity.attributes as ? Map <* , * >
179- if (attrs != null ) {
180- val lat = attrs[" latitude" ] as ? Double
181- val lon = attrs[" longitude" ] as ? Double
182- if (lat != null && lon != null ) {
183- val intent = Intent (
184- CarContext .ACTION_NAVIGATE ,
185- " geo:$lat ,$lon " .toUri(),
186- )
187- carContext.startCarApp(intent)
188- }
166+ displayed.coordinates?.let { coordinates ->
167+ val intent = Intent (
168+ CarContext .ACTION_NAVIGATE ,
169+ " geo:${coordinates.latitude} ,${coordinates.longitude} " .toUri(),
170+ )
171+ carContext.startCarApp(intent)
189172 }
190173 }
191174
192175 in SUPPORTED_DOMAINS -> {
193176 lifecycleScope.launch {
194177 try {
195- entity.onPressed(integrationRepositoryProvider())
178+ onEntityPressed(
179+ entityId = displayed.entityId,
180+ currentState = displayed.rawState,
181+ integrationRepository = integrationRepositoryProvider(),
182+ )
196183 } catch (e: CancellationException ) {
197184 throw e
198185 } catch (e: Exception ) {
@@ -211,12 +198,12 @@ class EntityGridVehicleScreen(
211198 gridItem
212199 .setImage(
213200 CarIcon .Builder (
214- IconicsDrawable (carContext, icon).apply {
201+ IconicsDrawable (carContext, displayed. icon).apply {
215202 sizeDp = 64
216203 }.toAndroidIconCompat(),
217204 )
218205 .setTint(
219- if (entity .isActive() && entity .domain in EntityExt .STATE_COLORED_DOMAINS ) {
206+ if (displayed .isActive && displayed .domain in EntityExt .STATE_COLORED_DOMAINS ) {
220207 CarColor .createCustom(
221208 carContext.getColor(R .color.colorYellow),
222209 carContext.getColor(R .color.colorYellow),
0 commit comments