Show openHAB uptime


(Robert) #1

Show openHAB uptime

This is an update to the example I posted earlier as a reply in someone else’s thread:

Purpose

The System Info binding provides the ‘CPU uptime’, which is the time in minutes since the last reboot of the machine running openHAB as a Number item.

I would also like to be able to see the time that openHAB is running. This is usually shorter, since I tend to restart the sudo systemctl restart openhab2.service every now and then, because I experience instability due to some memory leaks.

Using similar values would make it possible to compare them, so the openHab uptime should also be stored as minutes in a Number item.

The system uptime and openhab uptime should both be shown as the number of elapsed days, hours en minutes in the compact format “5 day(s) 05:59”. Localization should be possible.

Prerequisites

  • Exec binding is already installed
  • System Info binding installed
  • Javascript transformation installed
  • openHAB is running on a Linux based host operating system (the example uses a bash script and the bc utility)

I have tested this setup on a Raspberry Pi 3 running openHabianPi on Debian Stretch.

Please note that I refer to $OPENHAB_CONF, which is the root of my configuration folder in openHabian.

Configuration

Create a script

Create a bash script that gets the time that the openHAB process is running. I store the script in the scripts folder in my configuration.

$OPENHAB_CONF/scripts/uptime.sh

#!/usr/bin/env bash

PID=`ps aux --sort=start_time | grep openhab.*java | grep -v grep | awk '{print $2}' | tail -1`
UPTIME=`ps -o etimes= -p "${PID}"`
echo $UPTIME/60 | bc

Don’t forget to make the script executable

$ chmod a+x $OPENHAB_CONF/scripts/uptime.sh

Schedule the script

Schedule the script. I use the Exec binding, but you could also create a cron based rule. If you want get fancy you could also run a cron job on your machine that posts the value using the REST api.

$OPENHAB_CONF/things/uptime.things

Thing exec:command:uptime [command="/full/path/to/uptime.sh", interval=60, timeout=2]

The script will run every 1 minute and will timeout after 2 seconds if needed.
Unfortunately, the command does not accept the $OPENHAB_CONF variable. This raises an error. You need the absolute path (for openHABian this would be /etc/openhab2/scripts/uptime.sh).

Convert exec output to Number

The new Exec binding does not output Number, but String only. A rule will take care of the conversion.

$OPENHAB_CONF/rules/exec.rules

val String _logger = "rules.exec"

// This rule is used to update the Number out of the returned string from the command line. 
// Only Works since openhab 2.2.
rule "Convert String to Item Type"
  when
    Item System_openHAB_Uptime_output changed
  then

    // Get name of Number Item by removing "_output" from the name of the String Item
    val toUpdate = triggeringItem.name.toString.split("_output").get(0)
    
    // post the new value to the Number Item
    postUpdate( toUpdate.toString , triggeringItem.state.toString )

    logDebug(_logger, "Item '" + toUpdate + "' state: " + triggeringItem.state) 
 end

Credits for this rule:

Transform the output for the sitemap

Create a transformation

$OPENHAB_CONF/transform/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 + ' day(s) ';
	}
	
	result = result + ("00" + h).substr(-2,2) + ":" + ("00" + m).substr(-2,2);

	return result;
})(input)

Define items

$OPENHAB_CONF/items/uptime.items

Number System_CPU_Uptime "Uptime server [JS(duration.js):%s]"           <clock> (gSystem) { channel="systeminfo:computer:local:cpu#uptime" }
String System_openHAB_Uptime_output "Uptime openHAB [%s]" <clock> (gSystem) { channel="exec:command:uptime:output" }
Number System_openHAB_Uptime "Uptime openHAB [JS(duration.js):%s]" <clock> (gSystem) // updated in exec.rules

Afterwards you can add the 2 Number items to your sitemap


(Vincent Regaud) #2

Hello,
I think that this can be simplified a bit.
The value passed to the Javascript transform can be a string
So we can do away with the Number item and the rule altogether if we add
a parseInt function to the Javascript transform:

(function(i){ 
    var value = parseInt(i);
    var d = Math.floor(value / (24 * 60));
    var h = Math.floor((value / 60) - (24 * d));
    var m = Math.round(value - 60 * (24 * d + h));

Regards

Vincent


(Robert) #3

Nice alternative if you don’t need a Number item. I do want it though, to have it exactly the same as the Number item from the system info binding.


(Johfeu) #4

I am just using this rule and it works quite well for me:


rule "Openhab Uptime"
when
	System started
then
	postUpdate(OH_Uptime, new DateTimeType(now.toString))
end


rule "Openhab Uptime readable"
when
    Item OH_Uptime changed or
	Time cron "1 1 * * * ?"
then

	var String tmp

	if(OH_Uptime != NULL) {
		var DateTime dateTime_OH_Uptime = new DateTime((OH_Uptime.state as DateTimeType).calendar.timeInMillis)
		var diff = now.millis - dateTime_OH_Uptime.millis

		//http://stackoverflow.com/questions/13018550/time-since-ago-library-for-android-java
		val Number SECOND_MILLIS = 1000;
		val Number MINUTE_MILLIS = 60 * SECOND_MILLIS;
		val Number HOUR_MILLIS   = 60 * MINUTE_MILLIS;
		val Number DAY_MILLIS    = 24 * HOUR_MILLIS;

		if (diff < MINUTE_MILLIS) {
			tmp = "just now";
		} else if (diff < 2 * MINUTE_MILLIS) {
			tmp = "a minute";
		} else if (diff < 50 * MINUTE_MILLIS) {
			tmp = String::format("%.2f", diff / MINUTE_MILLIS) + " Minutes";
		} else if (diff < 90 * MINUTE_MILLIS) {
			tmp = "an hour ago";
		} else if (diff < 24 * HOUR_MILLIS) {
			tmp = String::format("%.2f", diff / HOUR_MILLIS) + " Hours";
		} else if (diff < 48 * HOUR_MILLIS) {
			tmp = "since yesterday";
		} else {
			tmp = String::format("%.1f", diff / DAY_MILLIS) + " Days";
		}

		postUpdate(OH_Uptime_HumanReadable, tmp)
	}

end

(Robert) #5

This rule will reset the uptime every time you update an items or rules file, too. I don’t think this is what you want. Not me, at least, because I want to know the actual time since the service was started.


(Johfeu) #6

Actually only if I change the file containing this exact rule or the items file containing only OH_Uptime_HumanReadable and OH_Uptime definition https://docs.openhab.org/configuration/rules-dsl.html#system-based-triggers (no other items involved)
This is what the docs say and how it seems to behave for me. So this is >99% what I want and way less complexe :slight_smile:


(Kim Skatun) #7

Maybe we could add some logging(persitance), to figure out how many times OH went down in last year and so on… What would be brilliant would be to find out which binding made it crash… I have a feeling that my system goes down every two months or so…


(Robert) #8

You should check this, since I have a separate rules file with only one rule that triggers a notification when the system is restarted, and it notifies my (iirc) on every save of any rules file and (not 100% sure) items file.

If so, keep it, just noticing the functional differences with my setup.


(Johfeu) #9

Just check by changing some comments in another rules file and saving via sftp. Log tells me it did a “Refreshing model” and “Loading model” of the changed rule but uptime is not changed. Works for me.


(Kjetil Asdal) #10

To really check this, you need to touch an “.items” file and not a “.rules” file.

My experience is similar to yours, thougf, that only the modified file will execute the “system started rule”. As I read the docs, this is how it is expected to work, but I think there is a caveat in case you have defined groups that in some way spans multiple .items files. For your purpose, I guess this can simply be avoided.


(Johfeu) #11

Yes, thats what i tested before. Doublechecked it, resets only if OH_Uptime definition gets touched.


(Dror) #12

Thank you for the hard work and detailed explanation.
I tried placing the number items in the sitemap however I see only aN:aN as values.
My sitemap frame has:

Default item=System_CPU_Uptime
Default item=System_openHAB_Uptime

I can see thisupdate in the log:

018-03-11 21:29:03.338 [vent.ItemStateChangedEvent] - System_openHAB_Uptime_output changed from 3194 to 3196


(Vincent Regaud) #13

Try:

Text item=System_CPU_Uptime
Text item=System_openHAB_Uptime

(Dror) #14

Tried that already and now again.
Same result.

Uptime server   aN:aN
Uptime openHAB  aN:aN

Notice also I am only seeing System_openHAB_Uptime_output updating.

EDIT (update):
After rebooting server, items seems to update correctly.

EDIT 2:
Only “Uptime openHAB” shows updates.
“Uptime server” still shows aN:aN


(Vincent Regaud) #15

Have you installed the system info binding?


(Dror) #16

Yes. Followed the guide above to the letter.

EDIT:
I had to add the thing below to the uptime.things file.
Is this needed? Aren’t these things automatically added to “things” after installing the systeminfo binding?

Thing systeminfo:computer:local [interval_high=2, interval_medium=60]

(Vincent Regaud) #17

Yes, they are.

According to the toturial at the beginning of the thread, the uptime.things files only contains:

Thing exec:command:uptime [command="/full/path/to/uptime.sh", interval=60, timeout=2]

Check that you items file contains:

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

The binding info { channel=“systeminfo:computer:local:cpu#uptime” } is important

then restart OH

If it doesn’t work, remove the system info binding and re-install it

change the file and restart OH


(Ruben) #18

hello, i’ve tried to make work Uptime openHAB but doesn’t work.
i ha ve this result.


can anyone give some minutes of his time to help me?
here are my files:

uptime.items

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

String System_openHAB_Uptime_output "Uptime openHAB [%s]" { channel="exec:command:uptime:output" }
Number System_openHAB_Uptime "Uptime openHAB [JS(duration.js):%s]"

exec.rules

val String _logger = "rules.exec"

// This rule is used to update the Number out of the returned string from the command line. 
// Only Works since openhab 2.2.
rule "Convert String to Item Type"
  when
    Item System_openHAB_Uptime_output changed
  then

    // Get name of Number Item by removing "_output" from the name of the String Item
    val toUpdate = triggeringItem.name.toString.split("_output").get(0)
    
    // post the new value to the Number Item
    postUpdate( toUpdate.toString , triggeringItem.state.toString )

    logDebug(_logger, "Item '" + toUpdate + "' state: " + triggeringItem.state) 
 end

scripts/uptime.sh (i had to remove " | bc" because it was retrieving me an error in the script.

#!/usr/bin/env bash

PID=`ps aux --sort=start_time | grep openhab.*java | grep -v grep | awk '{print $2}' | tail -1`
UPTIME=`ps -o etimes= -p "${PID}"`
echo $UPTIME/60

reboot.sitemap

sitemap reboot label="Controllo Sistema" {

	Frame label="openHAB" {
                Text item=System_CPU_Uptime
                Text item=System_openHAB_Uptime_output
                Text item=System_openHAB_Uptime
	}

}

uptime.things

Thing exec:command:uptime [command="/etc/openhab2/scripts/uptime.sh", interval=60, timeout=2]
Thing systeminfo:computer:local [interval_high=2, interval_medium=60]

transform/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 + ' day(s) ';
	}
	
	result = result + ("00" + h).substr(-2,2) + ":" + ("00" + m).substr(-2,2);

	return result;
})(input)

this is the only openhab.log info that could help:

2018-10-08 13:07:29.674 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '798/60' to a state type which item 'System_openHAB_Uptime' accepts: [DecimalType, QuantityType, UnDefType].

thanks a lot


(Vincent Regaud) #19

echo $UPTIME/60 returns a String with the number and /60 attached at the end
Therefore the transformation fails
divide by 60 inside the transformation instead


(Vincent Regaud) #20

bc returned an error because it is not installed
bc stands for basic calculator, a command line calculator tool
Install it on your system. then it should work without modification. don’t forget to add the | bc to the end of the last line of the sh script