Remove friendly name usage from HAControls - #7268
Open
TimoPtr wants to merge 2 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Android Controls (HAControls) pipeline to avoid reading friendly_name directly from entity attributes, instead building controls from EntitiesForDisplayManager / EntityDisplay* so names, areas, icons, and other display attributes are resolved consistently (including entity registry data).
Changes:
- Refactors
HaControlsProviderServiceto source items fromEntitiesForDisplayManager(snapshot + observe) and removes registry-handling helpers/structures. - Extends
EntityDisplayandEntityhelpers to expose additional display/control data needed by HAControls (number/media player/cover/vacuum controls,device_class,entity_picture). - Migrates individual control implementations to consume
EntityDisplayWithContextrather than rawEntityattributes.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| common/src/test/kotlin/io/homeassistant/companion/android/common/data/integration/EntityTest.kt | Adds unit tests covering new entity helpers (feature bitmask + domain control extraction + display attributes). |
| common/src/main/kotlin/io/homeassistant/companion/android/util/RegistriesDataHandler.kt | Removes obsolete registry lookup helper previously used by HAControls. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/Entity.kt | Adds supportsFeature and domain-specific *Controls helpers; restricts friendlyName visibility. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/display/EntityDisplay.kt | Extends EntityDisplay with additional attributes/controls and wires them from Entity. |
| common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/display/AlarmDisplay.kt | Uses the shared supportsFeature helper instead of direct bitmask casting. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/HaControlsProviderService.kt | Switches HAControls data source to EntitiesForDisplayManager, adds failed-item display handling, simplifies domain support gating. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/HaControlInfo.kt | Drops registry area from control metadata (area now comes from EntityDisplayWithContext). |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/HaControl.kt | Uses EntityDisplayWithContext for title/subtitle/state/icon rendering. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/CameraControl.kt | Uses entityPicture from display item for thumbnails. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/ClimateControl.kt | Uses resolved climate controls/modes from the display item. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/CoverControl.kt | Uses resolved cover controls and deviceClass from the display item. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/DefaultButtonControl.kt | Migrates to EntityDisplayWithContext for templates and domain naming. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/DefaultSliderControl.kt | Uses numberControls from the display item for range configuration. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/DefaultSwitchControl.kt | Migrates to EntityDisplayWithContext for templates and domain naming. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/FanControl.kt | Uses resolved fan controls from the display item. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/HaFailedControl.kt | Migrates to display item and uses rawState for notfound/exception rendering. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/LightControl.kt | Uses resolved light controls from the display item. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/LockControl.kt | Migrates to EntityDisplayWithContext for templates. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/MediaPlayerControl.kt | Uses resolved media player controls from the display item. |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/VacuumControl.kt | Uses resolved vacuum controls from the display item (but currently retains shared mutable state). |
Comments suppressed due to low confidence (2)
app/src/main/kotlin/io/homeassistant/companion/android/controls/VacuumControl.kt:57
- performAction relies on the mutable supportsTurnOn state from the last rendered control. Use the action's templateId (systemId) to look up the capability and strip the optional server prefix before calling the HA service.
override suspend fun performAction(integrationRepository: IntegrationRepository, action: ControlAction): Boolean {
integrationRepository.callAction(
action.templateId.split(".")[0],
if (entitySupportsTurnOn) {
if ((action as? BooleanAction)?.newState == true) "turn_on" else "turn_off"
} else if ((action as? BooleanAction)?.newState == true) {
"start"
} else {
"return_to_base"
},
hashMapOf(
"entity_id" to action.templateId,
),
)
app/src/main/kotlin/io/homeassistant/companion/android/controls/HaControlsProviderService.kt:353
- sendControl catches all Exceptions, including CancellationException from a cancelled subscription, and then tries to send a failed control. Rethrow CancellationException so cancellation stops work promptly and doesn't emit extra controls after cancel().
val control = try {
domainToHaControl[if (failed) "ha_failed" else item.domain]?.createControl(
applicationContext,
item,
info,
)
} catch (e: Exception) {
Timber.e(e, "Unable to create control for ${item.domain} entity, sending error entity")
domainToHaControl["ha_failed"]?.createControl(
applicationContext,
failedItem(item.entityId, notFound = false),
info,
)
}
Comment on lines
17
to
21
| object VacuumControl : HaControl { | ||
| private const val SUPPORT_TURN_ON = 1 | ||
| private var entitySupportedFeatures = 0 | ||
| private var entitySupportsTurnOn = false | ||
|
|
||
| override fun provideControlFeatures( | ||
| context: Context, |
Member
Author
There was a problem hiding this comment.
The issue was there before but it makes sense to fix it.
Comment on lines
+65
to
78
| val colorTint = when { | ||
| item.domain == LIGHT_DOMAIN && item.rawState == "on" -> R.color.colorDeviceControlsLightOn | ||
| item.domain == CAMERA_DOMAIN -> R.color.colorDeviceControlsCamera | ||
| item.domain == CLIMATE_DOMAIN && item.rawState == "heat" | ||
| -> R.color.colorDeviceControlsThermostatHeat | ||
|
|
||
| entity.state in listOf( | ||
| "off", | ||
| "unavailable", | ||
| "unknown", | ||
| ) -> R.color.colorDeviceControlsOff | ||
| item.rawState in listOf( | ||
| "off", | ||
| "unavailable", | ||
| "unknown", | ||
| ) -> R.color.colorDeviceControlsOff | ||
|
|
||
| else -> R.color.colorDeviceControlsDefaultOn | ||
| } | ||
|
|
||
| iconDrawable.setTint(ContextCompat.getColor(context, colorTint)) | ||
| control.setCustomIcon(iconDrawable.toAndroidIconCompat().toIcon(context)) | ||
| } | ||
| } else { | ||
| // Specific override for some domain icons to match HA frontend rather than provided device type | ||
| val iconOverride = listOf(MEDIA_PLAYER_DOMAIN, "number") | ||
| if (entity.domain in iconOverride) { | ||
| val icon = IconicsDrawable(context, entity.getIcon()).apply { sizeDp = 48 } | ||
| val tint = if (entity.isActive()) { | ||
| R.color.colorDeviceControlsDefaultOn | ||
| } else { | ||
| R.color.colorDeviceControlsOff | ||
| } | ||
| icon.setTint(ContextCompat.getColor(context, tint)) | ||
| control.setCustomIcon(icon.toAndroidIconCompat().toIcon(context)) | ||
| } | ||
| else -> R.color.colorDeviceControlsDefaultOn | ||
| } |
TimoPtr
marked this pull request as draft
July 28, 2026 14:32
TimoPtr
commented
Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Remove friendly_name usage in HAControls it was accessing it directly through the attributes of the Entity. It uses instead the EntitiesForDisplayManager that gives everything required for the HaControls. I had to extend the EntityDisplay interface to add more attributes.
Checklist
Select exactly one option that describes AI usage in this contribution: