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.