How to use variableKey in an oh-repeater list

Openhab 4.0.3 on Raspbian

My use case is I want to use an array (which is already held in an item string) as the source of an oh-list of toggles although in the examples below I’m using input-items so I can see what’s going on.

I don’t want to have to set up items for each list item because I want this to be generic so I can vary the lists. Each toggle (or input) should be reflected in a single object variable using variableKey but I can find no example of how to do this other than sparse information in the docs.

The inputs should then be reflected back in a single item for further processing using a “submit” button. I don’t want the inputs to be live-linked to items so the user can cancel the selected inputs if desired.

The problem I have is it’s not working and I don’t know why. I’d be very grateful for input.

The test page is as follows

config:
  label: Overview
  defineVars: outputvar:{"TestKey":TestValue}
  sidebar: false
blocks:
  - component: oh-block
    config:
      title: Home / Away
    slots:
      default: []
masonry:
  - component: oh-masonry
    config: {}
    slots:
      default:
        - component: widget:ListTest1
          config: {}
grid: null
canvas: null

It seems that I have to declare the variable outputvar here or I get a “Communication error” when I try to set the item with the button later because the variable is undefined.

Widget code is…

uid: ListTest1
tags: []
props:
  parameters:
    - context: item
      description: Item containing array
      label: Title
      name: arrayItem
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Nov 30, 2023, 2:20:19 AM
component: f7-block
config:
  title: Heating
  w: 600
  x: 20
  y: 20
slots:
  default:
    - component: oh-list
      slots:
        default:
          - component: oh-repeater
            config:
              for: room
              fragment: true
              in: =items["Heating_room_selection"].state.split(',')
              sourceType: array
            slots:
              default:
                - component: oh-input-item
                  config:
                    type: text
                    icon: f7:lightbulb
                    defaultValue: ="Hello" + " " + loop.room_idx
                    variable: ="outputvar"
                    variableKey: "[loop.room]"
                    title: = loop.room + " " + loop.room_idx
          - component: oh-button
            config:
              text: =vars.outputvar.BlueRoom
              outline: true
              action: command
              actionItem: Heating_selection_output
              actionCommand: =JSON.stringify(vars.outputvar)

I have tried so many different version of the variable and variableKey lines most of which generate errors. As it is I get at least something when I click the button but it makes no sense.

The Heating_room_selection item contains the following string

[
    "BlueRoom",
    "MasterBedroom",
    "Kitchen",
... etc
]

and the list in the page is correctly rendered with the defaults as

BlueRoom 0
Hello 0
MasterBedroom 1
Hello 1
Kitchen 2
Hello 2
... etc

Firstly the default values don’t seem to work. Unless I go into a value and change it then the variable is not populated with any data at all. If I click the button with no changes to those defaults then I get teh “Communication error” again because I seem to be trying to pass an undefined variable. If I edit say the BlueRoom entry to “Hell” then when I click the button (which is showing “undefined” at this point) then the command sent to the output item is

{"[loop":{"room]":"Hell"}}

which is a bizzare mishmash of what I think it should be.

Can someone point me in the right direction of what I am doing wrong - presumably on the syntax. The variableKey seems vary powerful if only I could use it properly.

Oh… my… goodness!

Having spent hours on this (and posted the above in desperation) I think I might have made some progress myself.

I just changed the variable lines to the following

defaultValue: ="Hello" + " " + loop.room_idx
variable: ="outputvar"
variableKey: =loop.room

and now the output is

{"BlueRoom":"H","MasterBedroom":"He","Kitchen":"Hel"}

when I indeed edit the input boxes to be ‘H’, ‘He’ and ‘Hel’. That is pretty much what I want (can’t believe I didn’t try that permutation before). This is a bit different from the way the docs seems to suggest it should be done.

What I still don’t get though and I’d be very grateful for input on, is how I make the default values part of the output variable without having to edit each and every list item. The output variable is only being populated with inputs that have been edited. Defaults are effectively ignored. Seems to be a loose definition of the word “default”.

The widgets will never send information or populate a variable without an action on the users part. That’s just how they are set up. defaultValue means, just as you have discovered, that if an input variable can’t find a starting value to display by some other means (e.g., linked item state or variable) then that will be the starting value.

if an input variable can’t find a starting value to display by some other means (e.g., linked item state or variable) then that will be the starting value.

Thanks for that Justin although that is a bit frustrating. It also isn’t actually the “starting value” for the variable. It’s only the starting content of the textbox. If you don’t edit that textbox then the variable remains undefined. Moreover, the toggle list item doesn’t have a default value property so I couldn’t, for example, open a list with some or all of the toggles set to ON as a starting position… or if you can then I’d love to know how.

There seems to be so much in the Main UI that is so nearly there but not quite. I saw in your answer to another thread somewhere that the UI is not the place to be doing “processing” and I agree with that but at the same time the UI seems to be a little too “dumb” at times.

I “think” I can define the variable (although why I have to do this at page level rather than widget level is beyond me) but I don’t think I can set it to an arbitrary set of values that I can change each time the page is loaded. I’ll have to look at that tonight.

So many workarounds needed when it would just be simpler if I could set the variable, say to the contents of an item, display and manipulate the variable in control widgets in the UI and then output it back to the item for further processing. As I said above, in my use case I want to be able to display an arbitrary list of controls, and ideally be able to pre-populate the list programmatically with the current state / options / defaults, which requires me to be able to pass data to the widget. If there might be 20 controls, I don’t want to have to set up 20 items linked to them and, in any case, I don’t want items to be live-linked to the controls anyway because I want the user to be able to back out of the page if necessary without having changed anything.

As I said, frustating. If you have any genius ideas to achieve what I need within the constraints we have, then I’d be very grateful to hear them.

There have been request in the past to add default values to variable in widget. I assume this hasn’t been a priority for the devs because there is already a usable solution. In most cases, whenever a variable needs to have a default value, this can be supplied as part of the expression that uses the variable with a basic OR statement:

property: =(vars.variableName || someDefaultValue)

If the variable variableName is undefined (i.e., hasn’t been set in the widget yet) then that will return someDefaultValue instead.

Yes, this makes the expressions a little longer and more awkward, but once you get it working you shouldn’t need to deal with it on a regular basis so a long expression is only a mild annoyance during widget development.

Thanks again for your input Justin.

I think that works if you need to set a property but not in in my use case because I’m trying to set the variable not set something to a variable’s value with a default as an alternative. I’m then trying to post that variable as a JSON object string back to an item.

However, your comment made me look again and I’ve also found this https://www.openhab.org/docs/ui/building-pages.html#dynamically-configuring-components-with-expressions which says I can set the initial state of the variable using an item even though I’m not dynamically linking to that item. I hadn’t spotted that. That combined with what you’ve said and what I had worked out already should do the job so I’ll play with it over the weekend. If I can get it to work, I’ll write it up because it has taken me hours of my time so far to extract what I’ve got from the docs. If it does all work, it’s quite a powerful thing but nobody else seems to be using it.

To be honest it sounds like you’re spending a lot of time on a problem that could be solved very quickly with items. I know you said you don’t want to have a proxy item for each of the items that might get controlled, but why not? Storing information in OH and passing information between other functions is what items are for.

It’s very common to see this pattern where someone is bending over backwards to avoid using items and winds up re-engineering half of the function that’s already just plain built in to items. In my system, I probably have more items that work as proxies or system variables or something similar than I do items that are linked to devices. If you are actively trying to avoid items you are working against the system, not with it and that’s why you’re finding it frustrating.

If you had proxy items then the widget you want would be very simple and the only additional function it would need is a cancel button that runs a rule to set proxy items back to the state of the actual items and an apply button that runs a rule which sets the actual items to the state of the proxy items.

Yeah - I think you’re right Justin. I’m going to have to go that way.

The UI is just not flexible enough to do much with variables. Although I can create an object within a variable, I don’t seem to be able to set that variable easily, or indeed at all. I would like to be able to build a widget that has an arbitrary number of toggles to show the state of various items, for the user to be able to tinker with those toggles (without actually altering the state of the items live) and then, when they are content, submit the new configuration.

I’ve come to the conclusion that variables, although they have the scope to be able to do that, simply cannot at the moment. So, you’re right, proxy items it is.