Skip to content

Multi-action grid widget implementation - #4783

Draft
mrdanielps wants to merge 9 commits into
home-assistant:mainfrom
mrdanielps:grid-widget
Draft

Multi-action grid widget implementation#4783
mrdanielps wants to merge 9 commits into
home-assistant:mainfrom
mrdanielps:grid-widget

Conversation

@mrdanielps

@mrdanielps mrdanielps commented Nov 3, 2024

Copy link
Copy Markdown

Summary

Adds a new widget that allows the user to configure multiple actions in a grid. It's somewhat based on the existing ButtonWidget.

The widget is responsive, adjusting the number of columns based on size, and becomes scrollable when it doesn't fit all the actions.

Fixes #1193, #4549

Screenshots

Dynamic colors - light
Screenshot_20241102_185606

Dynamic colors - dark
Screenshot_20241102_185620

API <31
Screenshot_20241102_185635

Configuration and resizing

Screen_recording_20241102_185549.mp4

Link to pull request in Documentation repository

Documentation: home-assistant/companion.home-assistant#1132

Any other notes

I've limited the scope of this PR for easier review, hence the draft status, but I will update it based on feedback.
The main features I think are necessary before merging are auto-completion for icons and action data, feedback when the button is pressed, and maybe entity states.

Additionally, the existing button widget could be updated to reflect the UI changes and share some of their code. However, I didn't want to change too much considering a proposal exists to refactor this code (#4640).

@home-assistant home-assistant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mrdanielps

It seems you haven't yet signed a CLA. Please do so here.

Once you do that we will be able to review and accept this pull request.

Thanks!

@bgoncal

bgoncal commented Nov 4, 2024

Copy link
Copy Markdown
Member

Looking great! Will the state of each item reflect in realtime? for example if the light is ON

@mrdanielps

Copy link
Copy Markdown
Author

Looking great! Will the state of each item reflect in realtime? for example if the light is ON

Not yet. As mentioned in the additional notes, I limited the scope of this PR for the initial review, just to get some feedback. Although looking at the entity widget implementation, I see it should be pretty straightforward to add, so I'll look into as soon as I can.

@bgoncal

bgoncal commented Nov 5, 2024

Copy link
Copy Markdown
Member

How often can a widget update in Android? In iOS the minimum auto-refresh is 15 minutes (besides other techniques like updating through push notification trigger)

@mrdanielps

Copy link
Copy Markdown
Author

It's actually quite similar on Android. The fastest auto-refresh is 30 minutes, but AFAIK anything that wakes up the app can manually trigger an update, like broadcasts or push notifications. Widget interactions can also update it, so pressing the button will allow for the state to be updated immediately.

@dshokouhi

Copy link
Copy Markdown
Member

The template widget updates instantly for me, there is no delay there. Our Template widget and entity state widget use a websocket subscription to update instantly.

@bgoncal

bgoncal commented Nov 5, 2024

Copy link
Copy Markdown
Member

@dshokouhi so you keep a websocket connection all the time for the widget? 🪫😭
(We do that in iOS for local push too hahaha)

@dshokouhi

Copy link
Copy Markdown
Member

so you keep a websocket connection all the time for the widget? 🪫😭

For widgets only when the screen turns on, then we stop the connection when the screen turns off. Its our best effort to keep them as up to as possible while not draining too much battery

(We do that in iOS for local push too hahaha)

yup same for local push we never stop the connection and also send a ping to the server every 30 seconds to ensure its active and current.

@mrdanielps

Copy link
Copy Markdown
Author

Looking great! Will the state of each item reflect in realtime? for example if the light is ON

Done!

Screen_recording_20241111_005218.webm

Comment thread common/src/main/res/values/strings.xml Outdated
Comment thread common/build.gradle.kts Outdated
@dshokouhi

Copy link
Copy Markdown
Member

Gave this PR a quick test run. I think you have captured the obvious missing features like auto complete, ideally the behavior for each action should mimic the existing button widget with how fields are populated. I think that will match general user expectation. For example that widget handles when a custom integration provides incomplete action data and also when a user provides invalid action data. Looking forward to seeing this PR progress!

@mrdanielps

Copy link
Copy Markdown
Author

ideally the behavior for each action should mimic the existing button widget with how fields are populated. I think that will match general user expectation. For example that widget handles when a custom integration provides incomplete action data and also when a user provides invalid action data.

I see, I was thinking of implementing it like the entity widget, which only takes an ID, but I'll certainly take that into account.

@vlaraort

Copy link
Copy Markdown

There is no progress in this? 🫠

It's a really interesting improvement

@dnestico

Copy link
Copy Markdown

Wish this could be merged....

@mrdanielps

Copy link
Copy Markdown
Author

Sorry for the long wait!
I'm currently working on an improved version written in Glance that builds on the great work by @TimoPtr with the new ToDo widget.

I hope to have it ready soon enough, but for those interested, here's a sneak peek.

glance-grid-widget.mp4

Comment thread app/src/main/res/xml/grid_widget_info.xml Fixed
@vlaraort

Copy link
Copy Markdown

This is awesome! Also like the idea of the reload button to prevent excessive connections to check status. Can't wait!

@dnestico

dnestico commented Jan 9, 2026

Copy link
Copy Markdown

Any update on this? Would love to see it merged.

@mrdanielps
mrdanielps marked this pull request as ready for review January 11, 2026 19:12
@mrdanielps

Copy link
Copy Markdown
Author

I've rebased the branch and added tests, so the PR should now be ready for review. I've also updated the lint baseline to ignore some warnings relating to stability of lists in composables which should be safe. However, I can correct them if needed.

Comment thread app/src/main/AndroidManifest.xml Outdated
Comment thread app/lint-baseline.xml Outdated
Comment on lines +114 to +126
@Composable
private fun LoadingScreen() {
Column(
verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = GlanceModifier.gridWidgetBackground().semantics { testTag = "LoadingScreen" },
) {
CircularProgressIndicator(
color = GlanceTheme.colors.primary,
modifier = GlanceModifier.size(HomeAssistantGlanceTheme.dimensions.iconSize),
)
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid duplication this since it's a duplicate of the TODOWidget and create a shared GlanceLoadingScreen.

Comment on lines +136 to +141
CircleIconButton(
imageProvider = ImageProvider(R.drawable.ic_refresh),
contentDescription = glanceStringResource(commonR.string.refresh),
onClick = actionRefreshGrid(),
backgroundColor = null,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above you could reuse the same as the one in TODO widget, it would also be helpful to add the sync_problem state that is quite important, since today the WebSocket sometimes is not working well, it helps to know when the state is not up to date.

Comment on lines +63 to +64
.getEntities()
.orEmpty()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we accept all kind of entities?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we filter by pressable or toggleable? I can see the state entities being used in the grid, although it is a bit strange to see them represented as buttons.
image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until we make it possible to attach an action to an element (another PR 🙏🏻 ) we could simply display them a bit differently that makes it clear that it is not clickable.


fun onSetup(widgetId: Int) {
if (this.widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
loadPreviousState(widgetId)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look at the TodoConfigureViewModel we do use a Mutex to ensure that the thread safety, we probably should apply some here too. I didn't look into all the details of the VM here, but most probably you have multiple sources touching the same thing, you want to avoid race condition as much as possible.

import kotlinx.coroutines.launch

@AndroidEntryPoint
class GridWidgetConfigureActivity : BaseActivity() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to start this activity from the Settings, like all the other widgets.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to match the color of existing widgets like TODO

image

Comment on lines +4 to +5
android:minWidth="40dp"
android:minHeight="40dp"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've played a bit with the minimal version it's IMO too small, but maybe it's fine for some users
image

I would personally in the smallest version only allow one item to be displayed and make it scrollable maybe.

import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class GridWidgetStateUpdaterTest {

@TimoPtr TimoPtr Jan 12, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like some test

  • to test the error scenarios.
  • test that verify that we are not updating more than we need when things doesn't change and verify the number of interaction we make with the integrationRepo to avoid making call that we shouldn't.
  • Making multiple update of the same entity is making the right amount of call on the integrationRepo (to verify that the collect within the transformLatest is properly canceled it's important if one day we change the operator to have this test).

Comment thread automotive/lint-baseline.xml Outdated
Comment on lines +2576 to +2586
<issue
id="UnusedResources"
message="The resource `R.xml.grid_widget_info` appears to be unused"
errorLine1="&lt;appwidget-provider xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
errorLine2="^">
<location
file="${:automotive*fullDebug*MAIN*sourceProvider*0*resDir*1}/xml/grid_widget_info.xml"
line="2"
column="1"/>
</issue>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't have this here. Could you revert the changes it should be used, like the other widgets.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not referenced in the automotive manifest, so I'm guessing that's why it complains about the unused resource.
I can add it to the grid_widget_info.xml like in the TODO widget:

tools:ignore="UnusedAttribute,UnusedResources"

I wonder if auto can benefit from this widget though.

@TimoPtr TimoPtr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for you contribution it's a a very nice improvements 💪🏻 and I'm sure many people are waiting for it (including myself)

It would be nice that when we click on an action only the action show the loading animation.

Few other things to fix on top of the comments

  • Add an entry in the changelog xml file since it's a nice new feature
  • Support empty content
  • Support out of sync or sync failure

val label: String?,
@ColumnInfo(name = "items")
val items: List<Item>,
) : WidgetEntity<GridWidgetEntity> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
) : WidgetEntity<GridWidgetEntity> {
) : WidgetEntity<GridWidgetEntity>, ThemeableWidgetEntity {

Comment thread common/src/main/res/values/strings.xml Outdated
Comment thread common/src/main/res/values/strings.xml Outdated
Comment thread common/src/main/res/values/strings.xml Outdated
@home-assistant

Copy link
Copy Markdown

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@home-assistant
home-assistant Bot marked this pull request as draft January 12, 2026 12:18
Adds a new widget that allows to configure multiple actions.

Fixes home-assistant#1193 home-assistant#4549
Addresses PR comments
Adds the kotlinx-collections-immutable dependency and use ImmutableList
for list arguments in the grid widget configuration screen.
Check if server is different before doing .copy
It was renamed when rebasing.
@TimoPtr

TimoPtr commented Jan 22, 2026

Copy link
Copy Markdown
Member

@mrdanielps the new entity picker has been implemented and we've replaced the old one with the new one in #6293 You can inspire yourself from the AndroidAutoFavorites for selecting the entities especially because it has the capability of reordering items which IMO would make your widget configuration even better.

Let's make sure your screen uses Material3 and the reusable components from https://github.com/home-assistant/android/tree/6b497e66400ed9f0f69466aa4d4ffb078962ebe4/common/src/main/kotlin/io/homeassistant/companion/android/common/compose

@TimoPtr TimoPtr linked an issue Jan 22, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Grid-based multi-action scene control widget. New widget type: double-button with visible state

8 participants