[Solved] Number String Conversion in YAML

Hi there,

I finished my first Custom Widget for MainUI - last thing to do is a time conversion.

I added the duration of the actual playing track, but this is shown in seconds, what is not very useful.
previously in my sitemap I converted this via a tiny javascript file while defining the item, but this is not recognized by the widget… so I guess I have to add the js to the line which loads the item state.

can someone help me out how this actually would look like?

                - component: Label
                  config:
                    class:
                      - text-align-right
                    text: =items[props.prefix+items[props.selected].state+props.duration].state

the javascript “mmss.js” which converts the seconds is in the “transform” folder.
or do I have to figure out another way without the conversion script?

thanks for your thoughts!

What type is the duration Item? If it’s a Number:Time you can use DateTime formatting on the Item’s State Description metadata to show hours:minutes:seconds (or what ever you prefer) and in your widget you’ll use .stateDescription instead of .state. Note that .stateDescrption only exists when the Item has a State Description metadata defined.

As an example here is the runtime of my UPS which is a Number:Time.

1 Like

Actually it is defined as Number without the :Time - is this important?
I guess my item definitions are somewhat old

Yes because if it’s just a plain old number what I presented above won’t work. If you can convert it to a Number:Time that would probably be easiest as then you can use the state description as written above.

If not, you’ll need to create a JavaScript transform to do the operations to split the seconds out into hours:minutes:seconds (there are lots of examples on the forum). Then in the State Description metadata you’ll call the transformation just like you would do in a label on the sitemap.

JS(fromseconds.js):%s

Overall, the tl;dr is you can’t do this inline in the widget. You have to do it in the State Description metadata for the Item and then in your widget used the formatted state by calling .stateDescription instead of .state.

Hmm…

now nothing is shown…

what I did:
changed the item file definition from Number to Number:Time
changed the items State Description via UI to JS(mmss.js):%s (the item itself is defined in a file)
changed .state to stateDescription in my yaml code

any ideas?

In the widgets you do have access to the dayjs libraries. If you build your duration seconds into a string that that dayjs can parse you then have a fairly full suite of formatting options in the widget.

sorry Justin, but can you explain this a little more to someone who’s new to the yaml coding…
which steps do I have to follow to achive this?

Wait, that was an either/or.

Either you use a Number:Time with a State Description of %1$tH:%1$tM:%1$tS

Or you keep the Number and use a State Description of JS(mmss.js)%s

You’ve mixed the two approaches. Also if you follow the second approach you’ll need to make sure that the JavaScript transformation is installed.

See Building Pages - Components & Widgets | openHAB. To start an expression you use =.

And now that I look there I’ve told you wrong. It’s displayState, not stateDescription.

I don’t see anywhere that dayjs supports durations in which case the same work to divide out a number of seconds to populate a dayjs function call would be required to just generate the String directly.

1 Like

Ok guys,

it’s much easier than we thought…

I sniffed the generated yaml from the item’s State Description from UI…

and added it to the config of the component… so I don’t have to edit every items metadata :wink:

now the component looks like this:

          - component: f7-col
            slots:
              default:
                - component: Label
                  config:
                    pattern: JS(mmss.js):%s
                    class:
                      - text-align-right
                    text: =items[props.prefix+items[props.selected].state+props.duration].displayState

awesome… thanks for your help!!

edit:
use of

pattern: %1$tM:%1$tS

instead of the javascript would also do the trick…

1 Like

There is a duration extension (Durations · Day.js), but it isn’t available in the widget editor, though perhaps that warrants a feature request in the long run.

You’re right absolutely right though, I was just thinking a quick and dirty couple of conversions and a partial iso string would be one option if the metadata route wasn’t satisfactory from some reason and this had to be done in the yaml.

As rich points out, this is a bit of a kludge and you’ll still have to do a little math and also fake an ISO formatted string. The dayjs library will parse a string that has a date ISO date format and then has many of the standard date display formatting options. So if your duration item is just in seconds it might look something like this:

text: =dayjs("2000-01-01T00:" + Math.trunc(146/60).toString() + ":" + (146 - 60*Math.trunc(146/60)).toString()).format("m:ss")

The “2000-01-01T00:” at the front is just a dummy date to give the string the correct formatting, then you can use the Math.trunc method to get the minutes from your seconds value (in this case a randomly chosen 146 seconds) and conversely extract the seconds after that. The dayjs .format method would then output 2:26.

Rich’s method is more elegant and you should really only resort to this in one of two cases. In the first case you modify your transform to put the duration into the iso format because then you can skip right to dayjs(item state). If you keep the simple seconds duration string then you would have to substitute into that line above your call to the items object and (as that gets you a string) use Number to parse it. So the final line gets pretty convoluted:

text: =dayjs("2000-01-01T00:" + Math.trunc(Number(items[props.prefix+items[props.selected].state+props.duration].state)/60).toString() + ":" + (Number(items[props.prefix+items[props.selected].state+props.duration].state) - 60*Math.trunc(Number(items[props.prefix+items[props.selected].state+props.duration].state)/60)).toString()).format("m:ss")

The only other reason to go this way over Rich’s pattern would be if you want a different format in the item’s pattern for some other display elsewhere.

1 Like

thanks for the additional info :slight_smile:

It’s actually .displayState, not .stateDescription.

2 Likes

Rich corrected himself already …but thanks anyway :wink:

I caught myself but didn’t go back and fix my previous replies.

1 Like

actually it doesn’t matter if the itemtype is Number or Number:Time for this to operate.
the problem that nothing was displayed was because of the wrong definition .stateDescription
instead of .displayState

1 Like