Metadata "options" -> oh-repeater

Hey @RGroll

thanks a lot again for guiding me that way!

Yes, it is about distributing to multiple rows.

Your example above will solve the issue when all values/labels are stated in the itemsStateOptions.

I have another case in which lots of individual commands will have to be gathered in the format command = mode in e.g. a map file for MAP transformation.
I figured out that “sourceType: map” is possible.

But how to make the MAP transformation work inside the oh-repeater?
(Maybe I should just use a dummy item for that and go with itemsStateOptions…)

What would you recommend to do?

I don’t think you can access transformation services from here.
But a MAP transform isn’t really what you want anyway, you’ve been distracted by the xxx.map file looking like a list.
A MAP transform takes a single value as key and returns a single string to you. There’s no way to examine or iterate the lookup file content.
Example mylist.map

ON=green
OFF=red
BANANA=yellow

You’ve got no way to find out the list is ON,OFF,BANANA which I think is what you’re after?

2 Likes

As @rossko57 said, the map transformation of the oh-repeater doesn’t seem like the right choice here.
But try to describe your scenario a bit more, maybe there are other ways to achieve what you’d like to see.

Hey @RGroll ,

I’m working on a widget to control my pretty old (2010) Denon AVR via the Denon/Marantz binding. The binding is very useful, since it allows to send commands via telnet.

There are a few basic channels that also work with my AVR as they are - e.g. Power on/off, volume, input source.

Some of the channels are “Read” channels, such as “Surround Program”.

In order to set the correct Surround Program" I can use a special “Write” channel (general#command).
I have around 15 commands to change the surround mode e.g. MSDOLBY DIGITAL=DOLBY DIGITAL.

I created an item linked to the command channel, I can send individual commands successfully, but when I tried to collect all commands in the items metadata, itemStateOptions didn’t work. I guess I will have to try this again tonight, since this should work.

Hence I have:

  • 8 Input sources
  • 15 Surround Programs

Ideally I would like to have an f7-picker to select the correct modes, but f7-picker doesn’t seem to be working in Main UI…
Am I right?

That’s why I would like to distribute buttons to multiple rows… that’s where the MAP transformation could have helped. I understood from @rossko57 and you that this is not possible.

Your suggestion…

in combination with the above from @JustinG is the smartest way to solve the issue = already solved for 8x Input source.

I will need to get the command issue for the surround program with itemsStateOptions working.
Will try this later today…

Thanks.

Hey @RGroll ,

Would this work with f7-segmented for every row as well?
I don’t think so, but I wanted to doublecheck with you.

Thank you.

The oh-repeater iterates over each item and grabs it’s metadata - so it’s not a problem to grab all of them.

The function is part of the oh/f7-input component but you can’t pass arguments to it right now afaik, so yes, it’s not usable atm.

But there are alternatives like:

  1. Selection list in a separate popup - better for a lot of items and more mobile friendly (similar to the UI configuration selection)

    list_selection-1

Selection list #1 (YAML)
uid: selection_type-1
component: f7-card
config:
  title: "Selection type #1"
slots:
  default:
    - component: f7-row
      config:
        class:
          - padding
      slots:
        default:
          - component: oh-repeater
            config:
              sourceType: array
              for: selection
              in:
                - title: Input source
                  icon: f7:tv
                  item: testItem
                - title: Surround programs
                  icon: f7:music_note_list
                  item: testItem5
              fragment: true
            slots:
              default:
                - component: f7-col
                  slots:
                    default:
                      - component: oh-list
                        config:
                          id: list
                        slots:
                          default:
                            - component: oh-list-item
                              config:
                                title: =loop.selection.title
                                after: =items[loop.selection.item].displayState
                                smartSelect: true
                                icon: =loop.selection.icon
                                iconColor: default
                                popupOpen: =".selectionPopup_"+loop.selection_idx
                                style:
                                  border: 1px solid lightgray
                                  border-radius: 5px
                      - component: f7-popup
                        config:
                          class: ="selectionPopup_"+loop.selection_idx
                        slots:
                          default:
                            - component: f7-page
                              slots:
                                default:
                                  - component: f7-navbar
                                    config:
                                      title: =loop.selection.title
                                      style:
                                        position: sticky
                                    slots:
                                      left:
                                        - component: oh-link
                                          config:
                                            iconIos: f7:arrow_left
                                            iconMd: material:arrow_back
                                            iconAurora: f7:arrow_left
                                            popup-close: true
                                  - component: f7-list
                                    config:
                                      virtualList: true
                                    slots:
                                      default:
                                        - component: oh-repeater
                                          config:
                                            sourceType: itemStateOptions
                                            itemOptions: =loop.selection.item
                                            for: option
                                            fragment: true
                                          slots:
                                            default:
                                              - component: oh-list-item
                                                config:
                                                  title: =loop.option.label
                                                  actionFeedback: ='{ "text":"You selected <b>' + loop.option.label + '</b><br>Sending command <b>' +  loop.option.value + '</b> to item", "position":"center","icon":"<i style=\\"color:green\\" class=\\"f7-icons\\">checkmark_alt</i>","closeButton":true }'
                                                  noChevron: true
                                                  radio: true
                                                  checked: "=loop.option.value === items[loop.selection.item].state ? true : false"
                                                  popupClose: true
                                                  action: command
                                                  actionItem: =loop.selection.item
                                                  actionCommand: =loop.option.value
  1. Automatic generated list of options in an overlay (similar to the sheet mechanics)
    list_selection-2
Selection list #2 (YAML)
uid: selection_type-2
component: f7-card
config:
  title: "Selection type #2"
slots:
  default:
    - component: f7-card
      config:
        noShadow: true
        class:
          - no-margin
      slots:
        default:
          - component: f7-card-content
            config:
              class:
                - margin-top-half
            slots:
              default:
                - component: f7-list
                  slots:
                    default:
                      - component: f7-list-item
                        config:
                          title: Input source
                          after: =items.YOUR_SOURCE_ITEM.state
                      - component: f7-list-item
                        config:
                          title: Surround programs
                          after: =items.YOUR_SURROUND_ITEM.state
    - component: f7-row
      config:
        class:
          - padding
          - padding-top-half
      slots:
        default:
          - component: f7-col
            slots:
              default:
                - component: oh-button
                  config:
                    text: Input source
                    outline: true
                    action: options
                    actionItem: YOUR_SOURCE_ITEM
          - component: f7-col
            slots:
              default:
                - component: oh-button
                  config:
                    text: Surround programs
                    outline: true
                    action: options
                    actionItem: YOUR_SURROUND_ITEM

It is (I think) - but it would be too easy giving you a solution, so I think this is a good challenge for you, if the above variants doesn’t fit your needs. :slight_smile:

6 Likes

Hey @RGroll ,

learning a lot - tried all options, got most of it working even f7-segmented, but your method is much nicer :slightly_smiling_face:

Got a small challenge with…

Working fine with item name:

                - component: oh-repeater
                  config:
                    sourceType: array
                    for: selection
                    in:
                      - title: Surround programs
                        icon: f7:music_note_list
                        item: DenonAVR4310_SendSurroundProgram 

… not working with props even though I’m using exactly the same props successfully in other places:

                - component: oh-repeater
                  config:
                    sourceType: array
                    for: selection
                    in:
                      - title: Surround programs
                        icon: f7:music_note_list
                        item: =props.itemSendSurroundProgramCommand

Why is that?
Shouldn’t it be possible to use expressions within an array?

Thanks.

The map parameter in the oh-repeater is not about the map transformations in your config folders, that’s just a coincidence of terminology.

  1. map is not an accepted value for the ‘sourceType’ parameter:
    image
  2. What map refers to in this case is an expression that can be applied to each value in the repeater’s array (after the filter is applied) that transforms the final element value. My small example widget from above could achieve exactly the same results using the map parameter to do the string building instead of doing it at the level of the label text:
component: f7-card
config: {}
slots:
  content:
    - component: oh-repeater
      config:
        for: fish
        sourceType: array
        in: 
          - name: Fish 1
            color: Red
          - name: Fish 2
            color: Blue
        filter: loop.fish_source.indexOf(loop.fish)>=1
        map: loop.fish.name + " = " + loop.fish.color
      slots:
        default:
          - component: Label
            config:
              text: =loop.fish
1 Like

This is a bit OT…

Tried to find a way to add a horizontal line as a divider or separator and just found the following which is probably a bit of a tweak rather than a good solution:

    - component: f7-card
      config:
        outline: true

showing up in the UI:

line

Is there a more elegant way to add horizontal lines?

Thanks.

Hey @RGroll ,

can you please help with the below struggle in the array for “item”?

Thank you!

Can’t say if it’s a bug or a known limitation, but It’s exactly that - expressions does not work inside the oh-repeater array definition. You could open an issue for that on the webui repo on github.

Hey @RGroll and @JustinG ,

maybe a bit OT, but still related to the oh-repeater topic…
I’m struggeling with switching visibility of one widget from another widget…

Let’s assume I have 2 widgets:
Dashboard1
Dashboard2

What would I like to achieve:
Visibility of Dashboard2 should be triggered from a button residing inside an oh-repeater of Dashboard1

                      - component: oh-repeater
                        config:
                          sourceType: array
                          for: control
                          in:
                            - icon: lightbulb
                              option: light
                            - icon: electrical_services
                              option: socket
                            - icon: storefront
                              option: markise
                            - icon: music_video
                              option: tv_av
                          fragment: true
                        slots:
                          default:
                            - component: oh-button
                              config:
                                icon-material: =loop.control.icon
                                iconSize: 30px
                                fill: "=vars[loop.control.option] || vars[loop.control.option] === undefined ? false : true"
                                outline: true
                                class:
                                  - padding-top-half
                                  - padding-bottom-half
                                  - display-flex
                                  - flex-direction-column
                                style:
                                  --f7-button-border-color: var(--f7-card-outline-border-color)
                                  height: 52px
                                action: variable
                                actionVariable: =loop.control.option
                                actionVariableValue: "=vars[loop.control.option] || vars[loop.control.option] === undefined ? false : true"

I’d like to use “option: tv_av” from the array to trigger visibility of Dashboard2.

              - component: oh-grid-col
                config:
                  medium: "33"
                  width: "100"
                slots:
                  default:
                    - component: widget:Dashboard1
                      config:
                        title: Dashboard1

              - component: oh-grid-col
                config:
                  medium: "33"
                  width: "100"
                slots:
                  default:
                    - component: widget:Dashboard2
                      config:
                        visibility: ??????????
                        title: Dashboard2

How to get this done?

You can use the widget vars object to pass values between different components within a widget. OH components with actions will have the variable option to set a variable value that can then be accessed in expressions throughout the widget. Here’s an example that uses two button created by a repeater to set two different variables to control the visibility of two different labels.

uid: widget_vars_demo
component: f7-card
config: {}
slots:
  default:
    - component: f7-row
      config: {}
      slots:
        default:
          - component: f7-col
            config: {}
            slots:
              default:
                - component: oh-repeater
                  config:
                    for: visButton
                    in:
                      - label: Dash 1
                        visibility: someWidget
                      - label: Dash 2
                        visibility: someOtherWidget
                    fragment: true
                  slots:
                    default:
                      - component: oh-button
                        config:
                          raised: true
                          text: =loop.visButton.label
                          action: variable
                          actionVariable: =loop.visButton.visibility
                          actionVariableValue: =!vars[loop.visButton.visibility]
          - component: f7-col
            config: {}
            slots:
              default:
                - component: Label
                  config:
                    text: First Dash Widget
                    visible: =!vars.someWidget
                - component: Label
                  config:
                    text: Second Dash Widget
                    visible: =!vars.someOtherWidget

Hey @JustinG ,

Yes, I am aware of the above described solution.
Thank you.

However, I’m looking for a solution to do this across widgets = on widget level instead of “component within a widget”.
(button in widget “Dashboard” triggers visibility of widget “Dreambox DM8000”)

across widgets

How to get this done?

The only way I know of to accomplish this is with a proxy item. In the PAGE yaml you can set the visibility of an entire custom widget.

config:
  layoutType: responsive
blocks: []
masonry:
  - component: oh-masonry
    slots:
      default:
        - component: widget:widget_sonos
          config:
            visibility: '=items["ProxyItem"].state=="ON"'
grid: []

So if you use a proxy item that is toggled by the first widget (or if you already have some item that is keyed to the event that you want), then the visibility of the secondary widget will be dynamic.

Something Yannick wrote a while back (I don’t remember where) made me think that it might be possible to have a vars object in the page scope, but I’ve never managed to make it work and proxy items work just as well.

Thank you.

Hey @JustinG

I would want to feed a oh-repeater automatically from the semantic model and group/filter the items, whereas the grouping/filtering is the challenge I have.

Example for “Lights” based on “Point_Control_Switch” and “Property_Light”:

          - component: oh-repeater
            config:
              sourceType: itemsWithTags
              itemTags: Light,Switch
              for: item
              fragment: true
            slots:
              default:
                - component: oh-button
                  config:
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    icon-material: lightbulb
                    iconSize: 15
                    text: =loop.item.label
                    action: toggle
                    actionItem: =loop.item.name
                    actionCommand: ON
                    actionCommandAlt: OFF
                    fill: "=(items[loop.item.name].state === 'ON') ? true : false"
                    outline: true
                    style:
                      --f7-button-border-color: var(--f7-card-outline-border-color)
                      height: auto

This adds all lights to the repeater irrespective of its location.

I’d wish to group the lights based on location. e.g all light in groundfloor or all lights in a certain room.
I thought that I could just add the tag “GroundFloor” to “itemTags”. But this doesn’t work.

I can see there is “itemsInGroup” as well… maybe using “itemsInGroup” could be used an then filter with “itemTags” - but how to apply such a filter …

What would be the best way to group “Lights” by its location?

I have several groups in a hierarchy:
gLightsLiving
gLightsDining
gLightsKitchen

All the above are members of gLightsGroundfloor.

I hoped to avoid creating lots of manual group items, since the location information is already there by the semantic model in exactly that hirarchy.

But maybe I misunderstood something along the line…

I think it would make life a lot easier, if there was a way to combine “itemTags” with “location”.
Can this be done by expressions? How?

One way would be to automatically populate the tags of an item with the tags for its specific location hirarchy in which the item is residing in.

This direct capability doesn’t exist. There’s just too much complexity (layer’s of hierarchy between higher model locations and points) and variability in the semantic model to make this easy, and the repeater’s collection of objects via itemsInGroup or itemsWithTags depends on the data returned via the api which doesn’t have any semantic filtering.

You can either create your own groups as suggested by hmerk or, if your semantic model is consistent enough, stack repeaters together. If I were to do something like this I would start with one repeater that is a hard coded array of the rooms I’m interested in:

- component: oh-repeater
  config:
    for: room
    in:
      - name: loc_DiningRoom
      - name: loc_Kitchen
      - name: loc_LivingRoom

This isn’t too problematic from a maintenance standpoint because the physical structure of the house isn’t likely to change too often. Then inside this repeater a second repeater with sourceType: itemsInGroup and groupItem: =loop.room.name which then filters for the equipment tags you are interested in (Or the other way around: gets the source list by tags and then filters by group membership). This repeater then populates the widget items accessing the actual points via a consistent nomenclature (e.g., loop.childItem.name + '_Switch' or whatever your nomenclature is).