Refactor EntityPicker to use EntityDisplayState#7152
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds websocket support for Home Assistant’s floor registry and the bandwidth-efficient entity registry “list_for_display” endpoint, then introduces a GetEntitiesForDisplayUseCase to resolve display-ready entity metadata (name/icon/area/device/floor/etc.) based on server capabilities. It migrates EntityPicker (and several callers) to consume this resolved display model and adds a reusable debounced HASearchField.
Changes:
- Add websocket repository APIs + DTOs for
config/entity_registry/list_for_displayandconfig/floor_registry/list - Introduce
GetEntitiesForDisplayUseCase+EntityDisplayItem/Stateto unify entity display resolution across features - Update
EntityPickerUI/state/search, and migrate Todo widget / QS tiles / shortcuts / vehicle & wear favorites to the new display model (plus add unit + screenshot tests)
Reviewed changes
Copilot reviewed 35 out of 47 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt | Add APIs for entity registry display + floor registry |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt | Implement the new websocket commands |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/EntityRegistryDisplayResponse.kt | Add DTOs for list_for_display response (abbreviated keys) |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/FloorRegistryResponse.kt | Add DTO for floor registry |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/AreaRegistryResponse.kt | Add floorId to areas (server ≥ 2024.3) |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/display/GetEntitiesForDisplayUseCase.kt | New use case to resolve display metadata using registries and server version gates |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/display/EntityDisplayItem.kt | New display model used by picker/favorites, incl. subtitle formatting |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/display/EntityDisplayState.kt | New Loading/Error/Loaded state wrapper for display resolution |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/HomeAssistantVersion.kt | Add isAtLeast(HomeAssistantVersion) helper |
| common/src/main/kotlin/io/homeassistant/companion/android/common/compose/composable/HASearchField.kt | New reusable debounced search field + state holder |
| common/src/main/kotlin/io/homeassistant/companion/android/common/compose/composable/HAModalBottomSheet.kt | Add consumeSheetScrollFling() helper modifier for bottom sheet content |
| common/src/main/res/values/strings.xml | Add entity picker load-failure string |
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImplTest.kt | Add tests for new websocket commands being sent/decoded |
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/EntityRegistryDisplayResponseTest.kt | Add decoding tests for display registry response |
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/websocket/impl/entities/FloorRegistryResponseTest.kt | Add decoding tests for floor registry (+ area floor_id) |
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/integration/display/GetEntitiesForDisplayUseCaseTest.kt | Comprehensive tests for version gating, fallbacks, and resolution logic |
| common/src/test/kotlin/io/homeassistant/companion/android/common/compose/composable/HASearchFieldTest.kt | Robolectric Compose tests for debounce/clear behavior |
| common/src/screenshotTest/kotlin/io/homeassistant/companion/android/compose/composable/HASearchFieldScreenshotTest.kt | Screenshot coverage for search field variants |
| app/src/main/kotlin/io/homeassistant/companion/android/util/compose/entity/EntityPicker.kt | Migrate picker to EntityDisplayState, add loading/error UI, use HASearchField, introduce metadata-free fallback builder |
| app/src/main/kotlin/io/homeassistant/companion/android/util/compose/entity/EntityPickerFilter.kt | Update filtering to operate on EntityDisplayItem |
| app/src/main/kotlin/io/homeassistant/companion/android/util/compose/entity/EntityPickerState.kt | New picker UI state holder (expansion + search + dispatcher) |
| app/src/main/kotlin/io/homeassistant/companion/android/util/compose/FavoriteEntityRow.kt | Migrate favorites row to EntityDisplayItem (icon/name/subtitle) |
| app/src/main/kotlin/io/homeassistant/companion/android/widgets/todo/TodoWidgetConfigureViewModel.kt | Use GetEntitiesForDisplayUseCase for Todo list entity selection |
| app/src/main/kotlin/io/homeassistant/companion/android/widgets/todo/TodoWidgetConfigureActivity.kt | Update configure UI to pass EntityDisplayState into picker |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/qs/views/ManageTilesView.kt | Update tile entity selection to use new picker contract |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/qs/ManageTilesViewModel.kt | Resolve tile-eligible entities via GetEntitiesForDisplayUseCase |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/shortcuts/legacy/views/ManageShortcutsView.kt | Update shortcut entity picker usage |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/shortcuts/legacy/ManageShortcutsViewModel.kt | Resolve entities via GetEntitiesForDisplayUseCase |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/vehicle/views/AndroidAutoFavoritesView.kt | Migrate favorites UI + picker to EntityDisplayState/EntityDisplayItem |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoViewModel.kt | Resolve vehicle entities via GetEntitiesForDisplayUseCase |
| app/src/full/kotlin/io/homeassistant/companion/android/settings/wear/views/SettingsWearFavoritesView.kt | Use metadata-free EntityDisplayItem builder (no websocket access on wear settings path) |
| app/src/debug/kotlin/io/homeassistant/companion/android/developer/catalog/HAUserInputScreen.kt | Update catalog samples for picker + add search field example |
| app/src/test/kotlin/io/homeassistant/companion/android/util/compose/entity/EntityPickerTest.kt | Update picker tests for loading/error states and new API |
| app/src/test/kotlin/io/homeassistant/companion/android/util/compose/entity/EntityPickerFilterTest.kt | Update filter tests for EntityDisplayItem |
| app/src/screenshotTest/kotlin/io/homeassistant/companion/android/util/compose/entity/EntityPickerScreenshotTest.kt | Update/add screenshot previews for loading/loaded picker states |
ae1eaca to
048ca8d
Compare
fd24e5c to
c3fe978
Compare
048ca8d to
8e2d83b
Compare
c3fe978 to
17c3278
Compare
8e2d83b to
85fa87a
Compare
17c3278 to
db73a3e
Compare
jpelgrom
left a comment
There was a problem hiding this comment.
Some of the refactoring here, around using a state for the entire sheet and not just EntityDisplayState and the gesture handling, and introducing an area registry test, feels a bit like scope creep. They could be submitted separately but I don't want an even larger PR stack.
| color = colorScheme.colorTextPrimary, | ||
| maxLines = 1, | ||
| overflow = TextOverflow.Ellipsis, | ||
| modifier = Modifier.weight(1f), |
There was a problem hiding this comment.
This makes them centered which is weird, when loaded it isn't centered so the text jumps from center > start. Also clearly visible in the screenshot tests.
| modifier = Modifier.weight(1f), | |
| textAlign = TextAlign.Start, | |
| modifier = Modifier.weight(1f), |
Before (with added network delay):
entity-unresolved-before.mp4
After:
entity-unresolved-after.mp4
Fix lint issue
Adjust after rebase
db73a3e to
e2ea755
Compare




Summary
Updated the EntityPicker to use the use case to pull the data and avoid using the friendly name.
Checklist
Screenshots
See in the PR
Any other notes
Based on #7151