Using vars in custom widgets: when are vars updated?

I’m trying to figure out how to simplify my YAML code for a couple Main UI widgets.

Widgets are configured by means of props. There’s also a vars collection that holds variables.

Does somebody know when the vars collection is updated?

For instance, if an entry in vars depends on the state of an item, is it safe to assume that state changes will be picked up?

Here’s a naive example. Consider a weather widget which also renders info about wind and gust speed:

  1. Wind speed and gust speed are rendered in km/h and on the Beaufort scale
  2. On occasions, the gust speed is not set. E.g., when the weather is not gusty
  3. The Beaufort reading will be rendered as “min - max Bf” if min < max and as “val Bf” if val = min = max.
  4. The wind and gust speed ranges will be rendered with a linear gradient depending on the Beaufort values

I know that I can define new Items that “follow” the original weather forecast items, e.g. through a SCALE transform. Unfortunately, this pollutes persistence.

What if I could compute the Beaufort values for wind and gust speeds and store them as vars in the widget, so I can use them in the YAML definitions to simplify the code (and to reduce errors)?

If I can assume that vars are updated when their dependent item states change, this could work, as in:

vars:
  currentWindSpeedBf: >
    = (
      items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state === '-'
      || ! Number.isFinite(
          Number.parseFloat(
            (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state
          )
        )
      ) ?
      '-' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 118 ? '12' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 103 ? '11' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 89 ? '10' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 75 ? '9' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 62 ? '8' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 50 ? '7' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 39 ? '6' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 29 ? '5' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 20 ? '4' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 12 ? '3' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 6 ? '2' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_WindSpeed' ].state ) >= 2 ? '1' : '0'
  currentGustSpeedBf: >
    = (
      items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state === '-'
      || ! Number.isFinite(
          Number.parseFloat(
            (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state
          )
        )
      ) ?
      '-' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 118 ? '12' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 103 ? '11' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 89 ? '10' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 75 ? '9' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 62 ? '8' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 50 ? '7' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 39 ? '6' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 29 ? '5' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 20 ? '4' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 12 ? '3' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 6 ? '2' :
        Number.parseFloat( items[ (props.itemPrefix ? props.itemPrefix : '') + 'Current_GustSpeed' ].state ) >= 2 ? '1' : '0'

One big ‘hidden’ benefit of this approach (if it works), is that I can dramatically simplify my Item state validation by checking if vars.currentWindSpeedBfdiffers from '-' in case of valid current wind speed item state. That’s because of the way vars.currentWindSpeedBf has been defined…

Further on, I could then write:

- component: Label
  config:
    text: >
      = 
        vars.currentWindSpeedBf === '-' ?
          '-' : 
          (
            Math.round(
              Number.parseFloat(
                items[ (props.itemPrefix ? props.itemPrefix : '') + 'ForecastToday_WindSpeed' ].state
              )
            )
            + (
                (
                  vars.currentGustSpeedBf === '-'
                  || Number.parseFloat(
                      items[ (props.itemPrefix ? props.itemPrefix : '') + 'ForecastToday_GustSpeed' ].state
                    )
                    <= Number.parseFloat(
                      items[ (props.itemPrefix ? props.itemPrefix : '') + 'ForecastToday_WindSpeed' ].state
                    )
                ) ?
                  '' : 
                  (
                    ' - '
                    + Math.round(
                        Number.parseFloat(
                          items[ (props.itemPrefix ? props.itemPrefix : '') + 'ForecastToday_GustSpeed' ].state
                        )
                      )
                  )
              )
               + ' km/h'
          )

and so forth.

Likewise, checking if the Beaufort values of wind speed and gust speed differ, is now trivial:

- component: Label
  config:
    text: >
      = 
        vars.currentWindSpeedBf === '-' ?
          '-' : 
          vars.currentWindSpeedBf
            + (
                (
                  vars.currentGustSpeedBf === '-'
                  || vars.Current_GustSpeedBf <= vars.currentWindSpeedBf
                ) ?
                  '' : 
                  (
                    ' - '
                    + vars.Current_GustSpeedBf
                  )
              )
               + ' Bf'
          )

This is far from obvious without using vars (if it works), as whenever you see vars.currentWindSpeedBf or vars.currentGustSpeedBf you would otherwise have to replace them with their respective (lengthy) definitions…

Finally, getting the colors for the gradient label text could easily be inferred from the computed Beaufort values in an array with 13 entries (0→12 Bf) containing the color definitions and stored as a custom variable in vars (without depending on item states), as in:

vars:
  windBfColor: >
    = [ 'white',
        '#AEF1F9',
        '#96F7DC',
        '#96F7B4',
        '#6FF46F',
        '#73ED12',
        '#A4ED12',
        '#DAED12',
        '#EDC212',
        '#ED8F12',
        '#ED6312',
        '#ED2912',
        '#D5102D'
      ]

 . . .
    style:
      text-align: center
      padding-top: 5px
      font-size: var(--weather-normal-font-size)
      background-image: >
        = vars.currentWindSpeedBf === '-' ?
            'gray' :
            (
              vars.currentGustSpeedBf === '-'
              || vars.currentWindSpeedBf == vars.currentGustSpeedBf
            ) ?
              vars.windBfColor[ vars.currentWindSpeedBf ] :
              (
                'linear-gradient(90deg, '
                + vars.windBfColor[ vars.currentWindSpeedBf ]
                + ','
                + vars.windBfColor[ vars.currentGustSpeedBf ]
                + ')'
              )
      background-size: 100%
      background-clip: text
      --webkit-background-clip: text
      --moz-background-clip: text
      text-fill-color: transparent
      --webkit-text-fill-color: transparent
      --moz-text-fill-color: transparent
      white-space: nowrap
      overflow: hidden
      text-overflow: ellipsis
      z-index: 100

Can somebody tell me whether this might possibly work? My main issue right now, is to know if state changes are adequately picked up in dependent vars

Off-topic

But … you don’t have to persist stuff. If you’re just letting it default, what else unnecessary is already there?

I noticed that everything which I dont persist to Influx is persisted in RRD, even though I only have Influx persistence service defined.

rrd4j persistence is there by default for all items. If you don’t have rrd4j.persist, create it with no entries that should disable rrd4j persistence.

Thanks!

The following indeed stops the default RRD persistence:

File: rrd4j.persist

Strategies {
}
Items {
}

To answer your original question, the variables are not expressions so they are not dynamically updated. The only ways to update them is documented here:

You can predefine them to a default value (and I realize this doesn’t seem to be documented) at the page level by setting them with the defineVars config option - expressions are not evaluated either here.

config:
  defineVars:
    var1: 5
    var2: abc

What you’re asking is an ability to define named expressions to reuse them in others, it’s not a bad idea, this could be implemented in the future.

1 Like