OH4: Set variable based on result of oh-repeater

Well, that is a different story. There’s certainly still a solution here, and all the pieces are already in this thread, though spread around quite a bit. But, I will say, you’re not really gaining anything by avoiding a group item. Items cost you next to nothing in terms of OH performance. As far as a widget goes, calling for the state of one extra item means a slight increase in the size of the information stream from OH to the UI which may add a millisecond or two to a page load, but the calculations and extra structure required to work around NOT having that easy item access will almost certainly coast you more loading time.

Really I can think of only one situation where it makes sense to go out of your way to avoid using an item state as part of a widget structure and that would be if you are planning on sharing this widget with someone else and want to save them the configuration effort of making the extra item. Even that’s a stretch though as anyone administrating an OH instance should have no difficulty creating 1 item with specific parameters.

That said, the solution here comes back to the fact that you can perform array operations on the loop array both in the repeater definition (by using the map property) and within expressions themselves. For this, I would just rely on the some() and every() methods again, you just have to make the functions those methods call slightly more involved. For example, set a block to visible if some of the exit delay items are ON from a repeater with loop.partition might look something like this:

visible: =loop.partition_source.some( (x) => items[x.name.replace('state','_In_Exit_Delay')].state == "ON")

Hi Justin
Yes, my goal is to eventually share this widget. The current version however relies on recent enhancements to the ParadoxAlarm Binding, which are still pending merge. So I am waiting for that and tweaking where I can in the meantime. I also want to look into the map() options as well.

As I mention I have started implementing your last suggestion by just swapping the contents of my Group, which will require a few minor other changes, but seems possible.

Still enjoying the learning and challenges.

I have however bumped my head on the tests for visibility that you suggested:

After a hiccup or two (hence the deleted post) I have managed to get the expression working and when I log it out I get what I expect:

Labelling using the following:

text: ="source.length" + loop.exitDelayItems_source.length + "name.state" + items[loop.exitDelayItems.name].state + "source.every" + (loop.exitDelayItems_source.every( (x) => items[x.name].state == "OFF"))

And toggling ONE item I see the correct results (true when all OFF and false when one ON):
Toggle On OFF

My issue seems to come in when I start in the f7-block and then “log” out the same information the result changes to true and hence the visibility expression no longer works?
Toggle On OFF - 2

The lines that are not working:

visible: =(loop.exitDelayItems_idx==0) && (loop.exitDelayItems_source.every( (x) => items[x.name].state == "OFF"))) 


text: = "IDX " + loop.partitionState_idx + " source.every filter " + (loop.exitDelayItems_source.some( (x) => items[x.name].state == "OFF"))

Full Code:
YAML- Filter on every().txt (9.3 KB)

Can you see what I am doing wrong now?

This line has an extra ) at the end so it’s not being evaluated at all

This line you are using the some() method instead of the every() method which gives you a different logical result. The opposite of “every state is off” is either “every state is NOT off” or alternatively “some state is on”.

1 Like

Thank you. Sorry if I am frustrating you.
These were both changed while I was trying to find an early syntax error… And then I overlooked them.

I did think that the widget editor highlighted missing brackets etc? That was why I added one to clear and earlier error - Anyway, thanks to you it is working now so I can continue.

Please could I ask one more question? Hopefully the last as I think i have an almost complete solution for what I wanted. I feel like you did all the heavy lifting though. I hope I get to remember all that you have helped with.

uid Test_OverlapBlocks_BOTH_With_Ba.txt (9.4 KB)

I now have the two blocks displaying when they should (i.e when Block 1 when every Partitons_In_Exit_Delay is OFF and Block 2 when some Partitons_In_Exit_Delay is/are ON.
I am just having an issue with using split to construct the final outputs that I need.

It seems that after adding a MAP to my first oh-repeater “lost” the loop.exitDelayList.name etc functions? All I can access is loop.exitDelayList, and I cannot get my split to work in that case? Should the .name etc be available?

                      - component: oh-repeater
                        config:
                          for: exitDelayList
                          fragment: true
                          in: =loop.exitDelayItems_source
                          sourceType: array
                        slots:
                          default:
                            - component: Label
                              config:
                                large: true
                                raised: true
                                style:
                                  background: var(--paradox-background-color)
                                  color: var(--paradox-font-color)
                                  flex: 0 0 420px
                                  font-size: 15px
                                  font-weight: 700
                                  top: 45px
                                  white-space: nowrap
                                  width: 300px
                                text: =loop.exitDelayList.slice (0,10) + items[loop.exitDelayList.slice (0,10) + "_Partition_Label"].state 

The split just gets ignored. Probably something dumb on my end again?
Slice

I have looked at as many oh-repeater examples as I can find, but nothing like this seems to come up.

My other option is to just create a new oh-repeater and make the extra API call.

Just for info what is working:
Working 98%

Thank You.

When you use the map property of the repeater you are changing the actual result array. So, it would be expected that if your map expression does not return an object with a name key then you cannot use .name on the resultant element.

In one of the examples above, I showed how to make sure the map expression returns an object:

But it’s worth thinking through carefully. Is name the only property of the item that you are interested in? In that case go ahead and map the element to just the name string. But, if you do that, you’ll have to change all the loop.exitDelayList.name to just loop.exitDelayList.

I actually did try that, but got an error and was trying not to ask again as I thought I could get away with just a simple MAP as I had been trying that out in a test and it looked like I could do what I needed:

Nested mappings are not allowed in compact mappings at line 125, column 20:

              map: ({"filter": items[loop.exitDelayCount.name.slice (0,10) + "_…

All I am using is the result of:

map: ='[loop.exitDelayItems.name.slice (0,10) + "_Partition_In_Exit_Delay"]'

So I have just used loop.exitDelayItems everywhere else. But that does not seem to work with the .split, which seems to be my last hurdle.

This is a yaml formatting issue and 99 times out of 100 this error just means you have : followed by a space somewhere in the value of the property. In this case, it’s actually my bad because I just typed the example up quickly and forgot to take that into account. There are two solutions: 1) just delete the spaces after the : in the object definition or 2) just put the whole expression inside the other quote type:

map: '({"filter": items[loop.exitDelayCount.name.slice (0,10) + "_Partition_In_Exit_Delay"].state, "title": items[loop.exitDelayCount.name].state + " Exit Delay"})'

A couple of things here:

The map property is special. You don’t want the = in front of the expression. There’s a technical explanation for this but the basic gist is that the repeater code needs to not have that expression evaluated by the initial pass of the expression parser and the parser is trigger by the = at the front.

The other issue is the brackets in the expression. split is a string method. loop.exitDelayItems.name.slice (0,10) + "_Partition_In_Exit_Delay" takes one string and concatenates it with another string for a string result; so far, so good. Putting that result inside of brackets now makes for a 1 element array with your string as the first element. So the final loop.exitDelayItems variable is an array and not a string which will then give an error if you try to use split.

You just want:

map: loop.exitDelayItems.name.slice (0,10) + "_Partition_In_Exit_Delay"
1 Like

I thought I should post the final working code here and the result:

Working 100%

Difficult to mark a specific reply as the solution, since almost every single one contributed to the end result and I think I ended up using most of the suggestions in some way as well…

I don’t think the Thread Title covers the details either - so happy to edit if there is a better description.

The final code is as follows, and I have merged this into the original widget and all working perfectly.
Test_OverlapBlocks.txt (8.9 KB)

Thank you Justin for your patience with explaining and guiding me.

1 Like