System Uptime Info and an Automatic Reboot

I’ve just added a rule that allows OpenHAB uptime to be displayed in a human readable format, and also reboot the system in the middle of the night once it has been up for over 7 days. As I’m doing a lot of work on the system I’ve steered away from a simple cron style reboot, and intend extending the time between reboots once I know how long it can go before it has problems. Before updating to OH2 it needed to reboot twice a week to prevent the user interface from throwing can’t read sitemap type errors, after about 6 days, and usually at an inconvenient time. I decided not to use the system info binding to save on clock cycles

Items:

String OHStartedAt
String OHFriendlyUptime

sitemap:

Text item=OHFriendlyUptime label="System Uptime [%s]" icon="clock"

rules:

var Long Uptime
val Boolean RebootDue = false

rule "Calculate Uptime"
when Time cron "0 * * * * ?" then 
if (OHStartedAt.state == NULL)	
{	var DateTime StartedTime = (now)
OHStartedAt.sendCommand(StartedTime.toString)
logInfo("Testing", "OHStartedAt " +OHStartedAt.state)
}	else	{
var DateTime BegunAt = parse(OHStartedAt.state.toString)
Uptime = (now.getMillis() - BegunAt.getMillis())/60000
//	logInfo("Testing", "Uptime in minutes is " +Uptime)
if (Uptime > 10080)	{	RebootDue = true	}
var String UT
var days = Uptime/1440
Uptime = Uptime - days*1440
var hour = Uptime/60
Uptime = Uptime - hour*60
var min = Uptime
UT = days+ "d "+ hour + "h "+ min + "m"
OHFriendlyUptime.sendCommand(UT)
}	
end

rule "Reboot downstairs to prevent it getting snotty"
when Time cron "0 50 3 * * ?" then	if (RebootDue)	{	executeCommandLine('"sudo" "reboot"', 5000)	}
end

The above rule works on OH2.0, to get the rule to run on OH 1.8 change the line

 if (OHStartedAt.state == NULL)	

to read

 if (OHStartedAt.state == Uninitialized)
3 Likes

or just make one line:

if ((OHStartedAt.state == NULL) || (OHStartedAt.state == Unitialized))

I am curious how you would (automatically) retrieve the uptime of the openHAB (java) process, since the SystemInfo binding does not let you determine a process id. The SystemInfo binding does give you the computer uptime, however.

For my purposes (display only, no scheduled reboots) I have both the computer uptime through the SystemInfo binding and the uptime of the openHAB process (usually shorter, since I tend to restart the openhab2.service every now and then) through the Exec binding.

uptime.sh

#!/bin/sh

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

demo.things

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

duration_seconds.js

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

	// seconds to minutes
	i = Math.floor(i / 60)

	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)

demo.items

// Number type does not yet work for exec binding
String System_openHAB_Uptime "openHAB uptime [JS(duration_seconds.js):%s]" <clock> (System) { channel="exec:command:uptime:output" }

The rule sets an item - OHStartedAt- at startup with the current time, and then once this is set, every minute it subtracts that from the current time to give the uptime. As I always reboot the whole system rather than restarting the service it gives me an approximate runtime.

There are of course several other ways to do this, and as I couldn’t find one I wrote my own and posted it.

Nice alternative way - I have yet to tame the new exec binding so everything I do is via a rule so far.

1 Like

Seems to be working great! Only set up today so haven’t had the the system restart yet, I’ll wait to see how that performs. But other than that I love the new formatted appearance of Uptime

1 Like

This is working great for me, too!
The only addonI have is the comment above - it computes duration from given seconds…
The script is nearly the same as for the uptime in the systeminfo binding (Community post) (provided also by Robert)- but only nearly:
The systeminfo binding script really calculates system uptime from given minutes (provided by the binding).
It is missing the

	// seconds to minutes
	i = Math.floor(i / 60)

So you have to use a different script for openhab runtime than for system runtime.
Maybe that helps others not to stumble into the same problem as me…
.

When using the 4 files from Robert I get the message :

2017-09-10 21:51:02.554 [WARN ] [.core.transform.TransformationHelper] - Cannot get service reference for transformation service of type REGEX
2017-09-10 21:51:02.555 [WARN ] [hab.binding.exec.handler.ExecHandler] - Couldn’t transform response because transformationService of type ‘REGEX’ is unavailable

in the logs…

Okay, my mistake; the RegEx Transformation was missing… But I only see the uptime in seconds :frowning:

-> Update: The JS must be placed in folder ‘transform’ and not in ‘scripts’. Now its working

1 Like

Thanks for this, nice and simple. Although your cron format is wrong. Unsure what time of the day 50 o’clock is :slight_smile:

...cron "0 50 3 * * ?"...

I also get this when saving the rules file. Still seems to work though.

2018-01-31 11:16:44.982 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model ‘home.rules’, using it anyway:
Assignment to final field
Constant condition is always false.

THe Validation error can be fixed by changing

val Boolean RebootDue = false

to this

var Boolean RebootDue = false

I had this rule by @kevin and it was working great until I added dbmap Persistence and now on every reboot the system maintains the original “OHStartedAt” value…there for I would get in to a constant reboot scenario if I hadnt commented out the final part of the rule that does the reboot… Anyone know why or how to reset the “OHStartedAt” value on a reboot?

My version of the Rule is here:

var Long Uptime
var Boolean RebootDue = false

rule "Calculate Uptime"
when Time cron "0 * * * * ?" then
if (OHStartedAt.state == NULL)
{	var DateTime StartedTime = (now)
OHStartedAt.sendCommand(StartedTime.toString)
logInfo("Testing", "OHStartedAt " +OHStartedAt.state)
}	
else	
{
var DateTime BegunAt = parse(OHStartedAt.state.toString)
Uptime = (now.getMillis() - BegunAt.getMillis())/60000
//	logInfo("Testing", "Uptime in minutes is " +Uptime)
if (Uptime > 10080)	{	RebootDue = true	}
var String UT
var days = Uptime/1440
Uptime = Uptime - days*1440
var hour = Uptime/60
Uptime = Uptime - hour*60
var min = Uptime
UT = days+ "d "+ hour + "h "+ min + "m"
OHFriendlyUptime.sendCommand(UT)
}
logInfo("Is a Reboot Due?", "RebootDue Status Flag = " +RebootDue)
logInfo("OHStartedAt", "Last Rebooted on " +OHStartedAt.state)
end
// rule "Reboot PiServer"
// when RebootDue = true 
// then executeCommandLine("sudo /sbin/reboot", 60000)
// end

Thanx for any advice.

Don’t persist the item OHStartedAt, this does not make any sense.

Hi job,

I have not added any grouping for that String, my items file entry looks like this:

String   OHStartedAt
String   OHFriendlyUptime
Switch   openhabBackup                       "RaspberryPi Image Back-up"   <floppy>           { expire="3s, command=OFF" }
Switch   system_PIRESTART_sw                 "Restart openHabian Server"   <switch>
Switch   Alarm_Fire                          "Fire Alarm Monitoring (On/Off)"       <switch>
Switch   GF_Dining_Light1                    "Dining Room Ceiling"         <light>            (GF_Dining, gLights, sPersist)           [ "Lighting"   ]

It looks like you are persisting every item.

Create some groups for your persistence strategy e.g.:

  • gRestoreOnStartup
  • gEvery5Minutes
  • gEveryChange

And push the items to persist in these groups according.

Don’t put OHStartedAt in the restore on startup strategy

Another possibility would be to create a System started triggered rule which resets the OHStartedAt date.

Thanx job,

I added in false (gGroups) after each of the string and other items that did not have them previously but this did not change anything and after a reboot the OHStartedAt string still persisted.

String   OHStartedAt                                                                          (gOhstartedat)
String   OHFriendlyUptime                                                                     (gOhfriendlyuptime)
Switch   openhabBackup                       "RaspberryPi Image Back-up"   <floppy>           (gOhbackup)                              { expire="3s, command=OFF" }
Switch   system_PIRESTART_sw                 "Restart openHabian Server"   <switch>           (gPirestart)
Switch   Alarm_Fire                          "Fire Alarm Monitoring (On/Off)"       <switch>  (gFirealarmsw)

I have been playing with additional rule entry to reset the String each restart…that appears to be working and I will monitor the output for a week to see if it works longer term.

 rule "Reset Clock"
 when
   System started
 then
{	var DateTime StartedTime = (now)
OHStartedAt.sendCommand(StartedTime.toString)
logInfo("Testing", "OHStartedAt " +OHStartedAt.state)
}
 end

I would still like to know how to over ride the dbmap persistence on those items not requiring that, as I had done it for another default.items and that works OK:

Switch   FF_Hallway_Light12                  "Hall Ceiling"                <light>            (FF_Hallway, gLights)                    [ "Lighting"   ]   { mqtt=">[broker:cmnd/kitchen/power2:command:*:default], <[broker:stat/kitchen/POWER2:state:default]", expire="2s,command=OFF" }

EDIT:

I dont think that i have set up my dbmap.persist file correctly and presently it is maintaining all items:

// mapdb.persistence

Strategies {
	default = everyChange
}
Items {
	* : strategy = everyChange, restoreOnStartup
}

Am’I allowed to enter multiple lines to the mapdb.persist file like this?

// mapdb persistence

Strategies {
	default = everyChange
}
Items {
	sPersist* : strategy = everyChange, restoreOnStartup
	gLights* : strategy = everyChange, restoreOnStartup
	gPower* : strategy = everyChange, restoreOnStartup
}

I’m not sure, but i think so.

I’m using the persistence groups as kind of tags for items. Every item that gets persisted, is in the gPersist group. Every item which should be restored on startup is in gRestoreOnStartup. I do not use gLights or gPower in persistence, a persisted lights item is in both groups.

I would like to share my setup for tracking uptime, adapted from the posts here and extended using java specific functionality.

I am using the following for tracking PC uptime (“CPU uptime”) and openHAB uptime. The solution uses java’s ManagementFactory to access the uptime of the openHAB java process, and systeminfo addon to access PC uptime (“cpu uptime”). The rules are setup such that they do not rely on persistence, and the values are restored even if items are re-created, e.g. when modifying the items file.

import java.lang.management.ManagementFactory
import java.time.ZonedDateTime
import java.time.Instant
import java.time.ZoneOffset

val formatDurationToText = [ Number 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;

	var String tmp;
	if (millis < MINUTE_MILLIS) {
		tmp = "less than a minute";
	} else if (millis < 2 * MINUTE_MILLIS) {
		tmp = "couple of minutes";
	} else if (millis < 50 * MINUTE_MILLIS) {
		tmp = String::format("%.2f", millis / MINUTE_MILLIS) + " minutes";
	} else if (millis < 90 * MINUTE_MILLIS) {
		tmp = "an hour ago";
	} else if (millis < 24 * HOUR_MILLIS) {
		tmp = String::format("%.2f", millis / HOUR_MILLIS) + " hours";
	} else if (millis < 48 * HOUR_MILLIS) {
		tmp = "since yesterday";
	} else {
		tmp = String::format("%.1f", millis / DAY_MILLIS) + " days";
	}
	return tmp
]


rule "Openhab Uptime"
when
	Time cron "0/10 * * * * ?"
then
	val runtime = ManagementFactory::getRuntimeMXBean();
	val long startedMillis = runtime.getStartTime()
	if(Openhab_Started.state == NULL || Openhab_Started.state == UNDEF) {
		Openhab_Started.postUpdate(new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochMilli(startedMillis), ZoneOffset.UTC)))
	}
	var uptimeAsText = formatDurationToText.apply(now.millis - startedMillis)
	postUpdate(Openhab_Uptime, uptimeAsText)
end


rule "CPU Uptime readable"
when
    Item CPU_Uptime changed or
	Time cron "0/10 * * * * ?"
then
	if(CPU_Uptime.state != NULL && CPU_Uptime.state != UNDEF) {
		// CPU_Uptime is in minutes, convert it to millis
		var cpuUptimeMillis = (CPU_Uptime.state as Number).longValue * 60 * 1000
		var uptimeAsText = formatDurationToText.apply(cpuUptimeMillis)
		postUpdate(CPU_Uptime_Text, uptimeAsText)
	} else {
		postUpdate(CPU_Uptime_Text, "-")
	}
end

items:


DateTime Openhab_Started "openHAB started [%1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS]" <status> 
String Openhab_Uptime "openHAB uptime [%s]"                                    <status> 

String CPU_Uptime_Text "Raspi uptime [%s]" <status>


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

EDIT 2023-11-12: I noticed that runtime.getStartTime() can return the time in local epoch milli… I worked around these issues using uptime:

// We calculate JVM start time using uptime
// runtime.startTime epoch milli can be in UTC millis or local millis, making
// it less convenient
var Instant = Java.type("java.time.Instant");
var startedZoned = ZonedDateTime.ofInstant(
  Instant.now().minusMillis(runtime.getUptime()),
  ZoneId.systemDefault())
		Openhab_Started.postUpdate(new DateTimeType(startedZoned )
9 Likes

This is great
Could you post this into its own topic in the tutorial and examples category, please?

Hi
The first code window, is this done by putty or should i past that into a file? If so what kind of file and where.?

Best regards Jerry

It’s a rules file