Can YAML widgets support dynamic image mapping based on list values?

Hello OpenHAB Community,

I’m developing a complex swipeable card widget and facing a challenge with dynamic image mapping. I want to know if the following logic is supported in a YAML widget:

What I’m trying to achieve:

  1. Two lists as properties:
  • checkvalues : Comma-separated values (e.g., “1,2,3,4”)
  • ImgSourceURLs : Comma-separated image URLs (e.g., “/img/1.png,/img/2.png,/img/3.png,/img/4.png”)
  1. Dynamic mapping:
  • When the checkitem value is found in the checkvalues list
  • Then use the image from the same position in ImgSourceURLs
  • Example: checkitem = 2 → use /img/2.png

My YAML code snippet:


background: =(() => { if (!props.checkvalues || !props.ImgSourceURLs || !@@props.checkitem) { return 'linear-gradient(black, black), url(' + (props.ImgSourceURLs ? props.ImgSourceURLs.split(',')[0] : '') + ')'; } const checkValues = props.checkvalues.split(',').map(v => v.trim()); const imageUrls = props.ImgSourceURLs.split(',').map(v => v.trim()); const checkItemValue = @@props.checkitem; const index = checkValues.findIndex(val => val === checkItemValue); if (index >= 0 && index < imageUrls.length) { return 'url(' + imageUrls[index] + ')'; } else { return 'linear-gradient(black, black), url(' + imageUrls[0] + ')'; } })()

My questions:

  1. Is JavaScript-like logic supported in YAML expressions?
  • Functions with (() => { ... })()
  • Array methods like .split() , .map() , .findIndex()
  • Conditions and loops
  1. Variable and data access:
  • Is @@props.checkitem correct for accessing the item state?
  • Does accessing items[props.selectionItem].state work?
  • Are nested functions supported?
  1. Performance concerns:
  • How often are these complex expressions evaluated?
  • Are there performance limitations with many such widgets?
  1. Alternative approaches:
  • Are there simpler methods for list mappings?
  • Should I use a separate rule script instead?
  • What are the best practices for dynamic image mapping?

Complete use case:

I’m building a swipeable card with three panels:

  1. Main panel: Shows status with dynamic background image
  2. Selection panel: Dropdown for modes/scenes
  3. Action panel: Quick action buttons

The image mapping should depend on:

  • The current item state (ON/OFF)
  • The selected mode
  • A direct mapping configuration

Example configuration:

checkvalues: "ON,OFF,AUTO" ImgSourceURLs: "/img/on.jpg,/img/off.jpg,/img/auto.jpg" optionsList: "AUTO=Automatic,MANUAL=Manual,OFF=Off" bgImages: "AUTO=/img/bg_auto.jpg,MANUAL=/img/bg_manual.jpg"

Has anyone experience with such complex logic in YAML widgets?
Does this approach work or are there better alternatives?

Thank you for your help!

The short answer is that yes you can do almost all of what you want to do in widget expressions (most loops or other flow controls are not available as those are statements and not expressions). This sort of complexity is exactly why the oh-context exits.

You can define your lists as named keys in the constants property of the context and then access them in expressions like const.url[1]. You can create reusable functions in the functions context property and call those with fn.getUrl(someValue). Most javascript methods are available and the best way to find out if the one you want is one of those is to just test.

Just using widget expressions it is difficult to run into performance issues on most browsers and devices. The real bottleneck comes if you are making a lot of API requests via an oh-repeater.

Assuming that checkitem contains the name string of an item, yes. That is equivalent to items.checkitem.state.

There are some limitations inherent to the oh-context. Mostly, any one of the context properties cannot access the other things defined in that same context. But you can call nested functions in the widget expressions themselves fn.func1(fn.func2('apple')) and you can also next oh-context components if you really need to nest certain objects outside of the main widget expressions.

This is always another viable option. Which one I would recommend in any given situation depends on the specifics of that situation. The primary difference would be that if all the computation is restricted to the widget definition, then different instances of MainUI can be utilizing different results simultaneously. If, on the other hand, the computation is off-loaded to a script, that will most likely impact all currently running instances of the MainUI simultaneously.

I have a draft of a tutorial that will cover almost all of this but I’m in the middle of a very busy season at work, so I haven’t had a chance to polish it up and get it posted. With luck it’ll be ready in the next few weeks.

Thanks for the answer…is my yaml snippet correct?

background: =(() => { if (!props.checkvalues || !props.ImgSourceURLs || !@@props.checkitem) { return 'linear-gradient(black, black), url(' + (props.ImgSourceURLs ? props.ImgSourceURLs.split(',')[0] : '') + ')'; } const checkValues = props.checkvalues.split(',').map(v => v.trim()); const imageUrls = props.ImgSourceURLs.split(',').map(v => v.trim()); const checkItemValue = @@props.checkitem; const index = checkValues.findIndex(val => val === checkItemValue); if (index >= 0 && index < imageUrls.length) { return 'url(' + imageUrls[index] + ')'; } else { return 'linear-gradient(black, black), url(' + imageUrls[0] + ')'; } })()

No, you cannot use full javascript. Everything has to be an “expression” which is a specific level of complexity.

if (x == 20) {
  const newX = 15
  x = x - newX
  console.log('adjusted x')
}

An expression is basically something that can be reduced down to a value. The code above has several expressions in it (e.g., x == 20 or 'adjusted x') but if ... is a statement and const ... is a variable declaration; these are not expressions and will not be properly evaluated by the widget expression parser. A good rule of thumb is “can this code be what follows an = in javascript?”. If yes then it’s an expression:

var test = (x == 20)

In this case x == 20 is a valid expression because it evaluates to true or false and so the variable test is properly assigned the value true or false.

If the code does not make sense after an = then you can’t use it in the widget yaml.

var test = const newX = 15

is not valid javascript because const newX = 15 is not an expression. You can’t use const newX = 15 in widget yaml.