Number:Time formatting for display

A Number:Time type Item shares the characteristic of other Quantity Types in that it’s state is represented by a single number + unit, as a pair.

openHAB can freely work with a variety of units, and convert values like say “90 s” to “1.5 min”, but neither of these are particularly human-friendly - we tend to think in clock-like terms, more like “1 min 30 secs”.
There is no standard feature to perform this kind of formatting.

However, transformations are very versatile and meant for tasks like this.
I present here a demo javascript for the job. I have stolen heavily from scripts previously published here, but added the capability to deal with the units of the Quantity as supplied from the state.

It is only a demo, you should expect to rework it to get the format of your choice. It is more about showing methods than great efficiency, which can certainly be improved.

You would place this file clockfmt.js in your /transform folder, and make sure you have JS transformation service installed.

/*
Javascript transform function to format a Number:Time duration
of variable units into a more readable clock-like format
*/

(function(i) {
    if (i == 'NULL') { return i; }
    if (i == '-') { return 'Undefined'; }
    var raw = i.split(" ");  // split numeric and units
    var num = parseFloat(raw[0]); // The value sent by OH is a string so we parse
        // scale input to common seconds base from units
    var dur = 0;
    if (raw[1] =='s') { dur = num; }
    else if (raw[1] == 'min') { dur = num * 60; }
    else if (raw[1] == 'h') { dur = num * 60 * 60; }
    else if (raw[1] == 'day') { dur = num * 60 * 60 * 24; }
    else if (raw[1] == 'week') { dur = num * 60 * 60 * 24 * 7; }
    else {return 'Unhandled units supplied *' + raw[1] + '*'}  // chicken out
    dur = Math.floor(dur) // round to integer seconds
        // now let's sub-divide our big bucket of seconds
    var days = 0;
    var hours = 0;
    var minutes = 0;
    var seconds = 0;
    if (dur >= 60) { // 60 seconds in a minute
        minutes = Math.floor(dur / 60); // Number of minutes
        seconds = dur - (minutes * 60); // Remove minutes from seconds bucket
    }
    if (minutes >= 60) { // 60 minutes in an hour
        hours = Math.floor(minutes / 60); // Number of hours
        minutes = minutes - (hours * 60); // Remove hours from minutes bucket
    }
    if (hours >= 24) { // 24 hours in a day
        days = Math.floor(hours / 24); // Number of days
        hours = hours - (days * 24); // Remove days from hours bucket
    }
        // Let's format
    var stringDays = '';
    var stringHours = '';
    var stringMinutes = minutes;
    var stringSeconds = seconds;
    var stringDays = '';
    if (days > 0) { stringDays = days + ' days ' } // skip if nil
    if (hours > 0 || stringDays != '') {  // skip if nil, unless days present
        stringHours = hours + ':'
        if (hours < 10) { stringHours = '0' + stringHours; } // make twodigit
    }
    if (minutes < 10) { stringMinutes = '0' + minutes; }
    if (seconds < 10) { stringSeconds = '0' + seconds; }
        // now build the output
    var returnString =  stringDays + stringHours + stringMinutes + ':' +stringSeconds
    return returnString.trim(); // Removes the extraneous space at the end

})(input)

Normally you’d place this transform in your [state presentation] part of your Item label, for display in your UI

Number:Time testDuration "my duration [JS(clockfmt.js):%s]"

But just to demonstrate the various actions, here is a demo rule fragment and log results

var x = "90.5 s"
testDuration.postUpdate(x)
Thread::sleep(100)
logInfo("test","state " + testDuration.state.toString)
var styled = transform("JS", "clockfmt.js", testDuration.state.toString)
logInfo("test","90.5 s > " + styled)
x = "90.5 min"
testDuration.postUpdate(x)
Thread::sleep(100)
logInfo("test","state " + testDuration.state.toString)
styled = transform("JS", "clockfmt.js", testDuration.state.toString)
logInfo("test","90.5 m > " + styled)
x = "9.5 week"
testDuration.postUpdate(x)
Thread::sleep(100)
logInfo("test","state " + testDuration.state.toString)
styled = transform("JS", "clockfmt.js", testDuration.state.toString)
logInfo("test","9.5 weeks > " + styled)
x = "1.5 year"
testDuration.postUpdate(x)
Thread::sleep(100)
logInfo("test","state " + testDuration.state.toString)
styled = transform("JS", "clockfmt.js", testDuration.state.toString)
logInfo("test","1.5 year > " + styled)
2020-10-28 21:01:27.830 [INFO ] [.eclipse.smarthome.model.script.test] - state 90.5 s
2020-10-28 21:01:28.005 [INFO ] [.eclipse.smarthome.model.script.test] - 90.5 s > 01:30

2020-10-28 21:01:28.107 [INFO ] [.eclipse.smarthome.model.script.test] - state 90.5 min
2020-10-28 21:01:28.122 [INFO ] [.eclipse.smarthome.model.script.test] - 90.5 m > 01:30:30

2020-10-28 21:01:28.247 [INFO ] [.eclipse.smarthome.model.script.test] - state 9.5 week
2020-10-28 21:01:28.264 [INFO ] [.eclipse.smarthome.model.script.test] - 9.5 weeks > 66 days 12:00:00

2020-10-28 21:01:28.367 [INFO ] [.eclipse.smarthome.model.script.test] - state 1.5 year
2020-10-28 21:01:28.384 [INFO ] [.eclipse.smarthome.model.script.test] - 1.5 year > Unhandled units supplied *year*

A similar approach can used for those wanting to format Number:Length types to xx yards yy feet zz inches etc.

5 Likes

You can get a similar result by converting the QuantityType to use SmartHomeUnits.SECOND and then run it through the core.date.human_readable_seconds function in the Jython helper libraries. This function is very helpful for audio notifications.

from core.date import human_readable_seconds

test = QuantityType("1.5 week")
LOG.warn(test)
LOG.warn(test.toUnit(SmartHomeUnits.SECOND))
LOG.warn(human_readable_seconds(test.toUnit(SmartHomeUnits.SECOND).intValue()))
2020-10-28 22:28:30.224 [WARN ] [jython.TEST_3] - 1.5 week
2020-10-28 22:28:30.225 [WARN ] [jython.TEST_3] - 907200.0 s
2020-10-28 22:28:30.226 [WARN ] [jython.TEST_3] - 10 days and 12 hours
1 Like

It would help to give an example of how to get that into a UI

Speculation - javascript transformations are allegedly poor performers, I wonder if a Jython Transformation Service would be of value?

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.