[SOLVED] Format uptime System Info binding

The System Info binding returns CPU uptime in minutes. I would like to show this as something like:

  • 1 hour, 6 minutes
  • 2 hours, 12 minutes
  • 1 day, 3 hours, 18 minutes
  • 2 days, 1 hour, 1 minute

I could use a rule for this, but that would trigger every second (the update interval of the binding).

Is there a clean way to do this with a transformation, javascript or otherwise?

Or is there another better or easier solution.

I donā€™t mind an extra item, but do not like a rule every second.

BTW, the texts need to be variable, so the solution can be used in any local language.

(The old 1.x way with SIGAR libraries returned a nice string, but that always was in english)

2 Likes

I imagine you could use a javascript transform since you really need to do some math here to get at the real values you want.

But that too will be running every second so Iā€™m not sure why that would be any better in your mind than doing it in a rule.

I have created a duration.js file that does the trick.
The advantage over the js file as opposed to a rule that creates an additional item is:

  • you can call it in both items, sitemaps and (still in) rules
  • you can easily modify it for your local language

my.things

systeminfo:computer:local         [ interval_high=1, interval_medium=60 ]

system.items

Number System_CPU_Uptime       "Uptime [JS(duration.js):%s]"       <clock> (System) { channel="systeminfo:computer:local:cpu#uptime" }

duration.js

// computes nicely formatted duration from given minutes
(function(i){ 

	var d = Math.floor(i / (24 * 60));
	var h = Math.floor((i / 60) - (24 * d));
	var m = Math.round(i - 60 * (24 * d + h));
	var result = '';

	// days
	if (d > 0) { 
		result = result + d;
		if (d == 1) {
			result = d + " day";
		} else {
			result = d + " days";
		}
	}

	// hours
	if (h > 0) {
		if (result != '') {
			result = result + ', ';
		}
		result = result + h;
		if (h == 1) {
			result = result + ' hour';
		} else {
			result = result + ' hours';
		}
	}
	
	// minutes
	if (m > 0) {
		if (result != '') {
			result = result + ', ';
		}
		result = result + m;
		if (m == 1) {
			result = result + ' minute';
		} else {
			result = result + ' minutes';
		}
	}

	return result;
})(input)
7 Likes

Too bad that this issue still is around. After an update of the thing/item, I only see the float again.

There must be something about what you are doing that I donā€™t understand because I still donā€™t see how implementing this in JS is any better or worse, lets you do anything or prevents you from doing anything that you also couldnā€™t do in a rule.

Not that it matters. There are lots of ways to do what you want in OH.

I always strive for generic solutions. And I assume(d) that running rules and creating items is more cpu intensive than transformations.

That is probably a false assumption. Both the JS transform and the Rules DSL require spinning up an interpreter but the actual calculation is trivial so they are probably the same CPU wise.

Secondly, in this case I dint see how putting it into a JS transform is any more generic than using a rule. You will need to edit/change something somewhere to change language anyway so putting it in a transform just moved where you need to make the edits, it didnā€™t really make them generic or easier to make.

I can see some arguments fit why you would want to do this in a transform. It is a good place to centralize your internationalization changes and I do like separating stuff like that. So from an organizational perspective I totally see advantages. But from a performance and genericization perspective I see no advantage.

Finally, even on a Pi, once per second is forever. I wouldnā€™t worry about optimizing such a set of rules until you actually see a performance problem.

Thanks for your thoughts. You are right about centralization.

One more question. Would the transformation be executed every update of the item or only when the page in the UI is loaded?

If the former, then performance wise they are similar. Otherwise the number of calls would be significantly lower. I guess. :wink:

Transforms on Items are run on every update of the Item. The result of the transform becomes the Itemā€™s state.

So how does it look in a rule, then? :slight_smile:
(I like the centralized approach)

At a high level, the full text gets set to a String Item. There is a Rule that triggers when this String Item changes. In this rule parse out the data needed and postUpdate those values to the Items that represent that value.

Thanks, @rlkoshak,

I thought there is already a solution available to save some time :wink:
I will start formatting it myself then the next days.

@NCO did you come up with the formatting that you can share?

I am using habpanel as UI and my current uptime related settings are:
.items file

Number CPU_Uptime                                { channel="systeminfo:computer:work:cpu#uptime" }

.widget file

<div class="valueGroup"><div class="value">{{itemValue('CPU_Uptime') | number:0}} min</div></div>

Assuming there is a javascript that does the conversion, the .widget file will have to change to show the itemā€™s string, but I donā€™t know how to do thatā€¦

I tried changing the .item file to the line below but the uptime is still reported in minutes
updated .item file

Number CPU_Uptime  "Uptime [JS(duration.js):%s]" <clock> (gSystem) { channel="systeminfo:computer:work:cpu#uptime" }

Should I place the javascript file in /etc/openhab2/transform or /etc/openhab2/scripts or ā€¦
What will be the settings in the .items .widget files?

Thanks

The file goes in transform folder.

Make sure you have the JS transform installed.

For the record, you could use the .js file in a rule something like this:

val String uptime  = '' + transform("JS", "duration.js", System_CPU_Uptime.state.toString)

Thanks @rlkoshak and @rtvb . I am not seeing any difference when using the js script (Iā€™m still seeing the upstart in minutes)
@rtvb I donā€™t have a rule for displaying the upstart. It basically refreshes every e.g. 30 secs based on systeminfo.things

cat /etc/openhab2/things/systeminfo.things
systeminfo:computer:work [interval_high=30, interval_medium=60]

Maybe my duration.js script is not called at all?
The only place I have it mentioned is in the .items file

cat /etc/openhab2/items/systeminfo.items
...
Number CPU_Uptime  "Uptime [JS(duration.js):%s]" <clock> (gSystem) { channel="systeminfo:computer:work:cpu#uptime" }

and in the habpanel widget I have

...
<div class="valueGroup"><div class="value">{{itemValue('CPU_Uptime') | number:0}} min</div></div>

Would itemValue(ā€˜CPU_Uptimeā€™) in the widget, trigger the item state (i.e. ā€œUptime [JS(duration.js):%s]ā€) that will trigger the js script?

@rlkoshak, I saw your recommendation to use designer to flush out any syntax errors in rules. Iā€™ll start using it when I have first chance (I havenā€™t used it before and I don;t want to open another front here)


Iā€™m trying to add log statements within it to see what is going on there but I donā€™t see anything in openhab.logā€¦
I did the following:

  • Turned on DEBUG logging for package org.openhab.io.net.exec in karaf (per the guidelines for openhab1 here (Iā€™m using openhab2 but did not find documentation for openhab2))
log:set DEBUG org.openhab.io.net.exec
log:list 
...
org.openhab.io.net.exec                                     | DEBUG
  • Added a log statement in the duration.js
logger.error( "foo2" );

But I donā€™t see anything in /var/log/openhab2/openhab.logā€¦

You donā€™t need too. My code was just an example how to use it when needed. I use this to send myself a daily email with some general system statistics.

I dontā€™t have any experience with HABpanel, so I canā€™t help you with this one.

In general, if you use the duration javascript, the actual item value is still kept as a Number by opennHAB but only transformed at runtime when displayed in the sitemap.

In a karaf console you could use smarthome:status CPU_Uptime to show the actual value.

A dummy rule to test your javascript could be (untested):

rule "Test Duration Script"
when
    Item CPU_Uptime received update
then
    val String duration  = '' + transform("JS", "duration.js", CPU_Uptime.state.toString)
    logInfo('Test', 'Number ==> ' + CPU_Uptime.state.toString)
    logInfo('Test', 'String ==> ' + duration)
end

Just watch your log file to see whatā€™s listed. If your log file is flooded, replace the event based trigger for the rule with a a time based trigger.

I am not aware that you can log from javascript transformations, so canā€™t help you with this one, either.

Your example rule helped me solve the problem. The conversion showed ok in openhab.log.
I ended up updating habpanel from a string item, which is updated automatically via the rule every time the uptime number item is updated (thanks @rlkoshak for your advice).
See my solution here

1 Like

sure:

rule "format uptime"
when
    Time cron "32 0/5 * * * ?"
then	
	var int days = (uptime.state as DecimalType).intValue / (24 * 60)
	var int hours = ((uptime.state as DecimalType).intValue / 60) - (24 * days)
	var int minutes = ((uptime.state as DecimalType).intValue - 60 * (24 * days + hours))
	var String result = ""
// days
	if (days > 0) {
		result = result + days + " Tage / "
	}
// hours
	if (hours > 0) {
		result = result + hours + " Std. / "
	}
// minutes
	if (minutes > 0) {
		result = result + minutes + " Min."
	}	uptimeFormatted.postUpdate(result)
	logInfo("system.rules", "Uptime updated to " + uptimeFormatted.state)
end
2 Likes

@rtvb

thanks for the code - exactly what i needed :slight_smile: