Metadata "options" -> oh-repeater

Hey @RGroll ,

is there a smart way to transfer the “label” of the “options” directly to a oh-repeater?

… without having to set props in the widget to e.g. VCR=CHROME,DVR=A1 TV,HDP=PS3,SAT/CBL=SAT …

Thank you!

1 Like

You want to set sourceType to itemStateOptions and the itemOptions to the name of the item. This yaml

component: f7-segmented
config:
  raised: true
slots:
  default:
    - component: oh-repeater
      config:
        sourceType: itemStateOptions
        itemOptions: name_of_item_with_options
        for: option
      slots:
        default:
          - component: oh-button
            config:
              text: =loop.option.label
              action: command
              actionItem: name_of_item_with_options
              actionCommand: =loop.option.value
              raised: true

With an item that has the following options:

produces this series of buttons
image

*Note the use of the label and value properties on the loop object to easily get both the acutal command and the display command.

1 Like

@JustinG ,

wow! Great stuff!

Let’s take it one step further… would it be even possible to group the options and put e.g. 2 each or 2, 3, 4 in separate repeaters?

This would be very useful, when you have too many options to be displayed in one row…

Thanks.

Yes, but it’s not very elegant.

oh-repeaters have a filter option and also give you access to the complete array that is being looped through via the loop.[array name]_source object. You can combine these two features to selectively display only a portion of the array in a single repeater by filtering on the index in the source object of the current array object. For example:

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
      slots:
        default:
          - component: Label
            config:
              text: =loop.fish.name + " = " + loop.fish.color

Produces
image

And changing the filter to loop.fish_source.indexOf(loop.fish)<1 gets the first half of the array instead:
image

To get the whole array in two different pieces then you would just need two separate repeaters, one with a < filter and one with a `>=’ filter.

With a little work you could even make it slightly more responsive comparing the index to an expression instead of a hard coded number.

@JustinG ,

thank you for dropping the idea with filters. You’re right, this is not very elegant…

What about the following… maybe not too elegant either, but quite flexible, I think…

Let’s assume we use MAP transformation and split the 4 entries below to 2 each in 2 separate .map files located in /transform.

Let’s assume the 2 map

files are:

array_1.map
array_2.map    

The widget props to which the above file names would be entered are:

    - description: Enter file name of array 1
      label: Array 1
      name: array1
      required: false
      type: TEXT
    - description: Enter file name of array 2
      label: Array 2
      name: array2
      required: false
      type: TEXT

As per docs it would require adaptation of:

      config:
        sourceType: itemStateOptions    

to

      config:
        sourceType: map    

But how to make the MAP transformation work inside the oh-repeater?

I guess it would require…

But how?

Thank you.

Hey @maxmaximax

@JustinG already gave you a great answer.
But just out of curiosity, what would you like to do with that splitted array then?

If it’s just about splitting the values from that array in multiple rows, I would recommend using css here instead of doing some fancy oh-repeater logic, which could look like this for example:

image

component: f7-card
config:
  style:
    display: grid
    grid-template-columns: repeat(4, 1fr)
    gap: 10px
slots:
  default:
    - component: oh-repeater
      config:
        sourceType: itemStateOptions
        itemOptions: testItem
        for: option
        fragment: true
      slots:
        default:
          - component: oh-button
            config:
              text: =loop.option.label
              action: command
              actionItem: testItem
              actionCommand: =loop.option.value
              raised: true
              fill: true

You can configure the number of items per row by changing the number of columns: grid-template-columns: repeat(4, 1fr)

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?