[Release] Raspberry Pi Stats


#1

This simple DH communicates with a Java Monitor that you compile on your Rpi to produce a JSON formated reply that the DH parses to pull your PI's stats.

You need to download and compile and install the Systemd script.

Then install this DH

You can then display the stats to Dashboard.

Thanks for help with the DH from @ogiewon and the original SmartThings developer cl0udninja.

Updated: April 14, 2019
Changes:
Version 1.0.7

  • Added attributes ipAddresses and nameservers and java code for future enhancements

Version 1.0.3

  • Added auto logging off after 30 minutes (Royski)

Version 1.0.2

  • Added debug switch
  • Added version update check (Cobra)
  • Removed Tiles section HE doesn't use them
  • Lots of code cleanup

Version 1.0.0 - Initial port

***** Please report bugs @Github. *****


Iris V1 Camera
Show off your 'Super Tiles'
#2

I have made some changes to booting the java app at boot time.

Instead of editing rc.local I have created a Systemd script instead. You will need to

cd /root/raspberrypi.monitor
git pull

and read the new instructions at

If you have already added the app in /etc/rc.local please deleted it first and after you installed the Systemd script you will need to reboot the Rpi.


#3

I have added attribute freeMemoryPercent so you can display the percentage instead of the freememory value or both if you wish.

image


#4

Updated GitHub - sgrayban/raspberrypi.monitor: Pi4j system information over Rest API

Supports boards up to Raspberry Pi 3A+ now.


#5

@sgrayban,

This is very awesome. For a long while I've wondered how my little RPi's are doing, but then having to VNC, or stick a monitor on, I never bother. This has given me just that, along with @Cobra's Super Tile, this is very, very neat!!! Many thanks for this :+ :slight_smile:


#6

WOW that looks good ! Glad my time was well spent !


#7

New version uploaded 1.0.2


#8

New version 1.0.4

  • Added correct importURL (@Royski)

New version 1.0.3

  • Added auto logging off after 30 minutes (@Royski)

#9

Here it is mate, I updated the name (royski) so I didnt overwrite yours while testing. Anything I added has //RK at the end :+1:

/**
 *
 * Hubitat Import URL: https://raw.githubusercontent.com/sgrayban/Hubitat-Ports/master/Drivers/rpi/Raspberry-Pi-Stats.groovy
 *
 *  Raspberry Pi Monitor
 *
 *  Copyright Janos Elohazi
 *  Copyright Scott Grayban
 *
 *  Ported to Hubitat by Scott Grayban with help from ogiewon
 *
 *  Monitor your Raspberry Pi using SmartThings and Raspberry Pi Monitor <https://github.com/cl0udninja/raspberrypi.monitor>
 *
 *  *************************************************************************
 *  Licensed under the GNU v3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *  *************************************************************************
 *
 *  !!WARNING!! This driver does NOT auto refresh your PI Stats. You will need to create a RM to refresh them.
 * 
 *  Changes:
 *   1.0.3 - Added auto logging off after 30 minutes (Royski)
 *
 *   1.0.2 - Added debug switch
 *         - Added version update check (Cobra)
 *         - Removed Tiles section HE doesn't use them
 *         - Lots of code cleanup
 *
 *   1.0.1 - Supports boards up to Raspberry Pi 3A+
 *         - Added attribute freeMemoryPercent
 *
 *   1.0.0 - Initial port
 *
 */

import groovy.json.*
	
metadata {
	definition (name: "Raspberry Pi Stats (Royski)", namespace: "sgrayban", author: "Scott Grayban", importUrl: "https://raw.githubusercontent.com/sgrayban/Hubitat-Ports/master/Drivers/rpi/Raspberry-Pi-Stats.groovy"){
	capability "Polling"
	capability "Refresh"
	capability "Temperature Measurement"
	capability "Sensor"
		
	attribute "cpuFrequency", "number"       
	attribute "freeMemory", "number"
	attribute "freeMemoryPercent", "number"
	attribute "cpuCoreVoltage", "number"
	attribute "modelName", "string"
	attribute "boardType", "string"
	attribute "javaVersion", "string"
	attribute "hostname", "string"
	attribute "serialNumber", "string"
	attribute "DriverVersion", "string"
	attribute "DriverAuthor", "string"
	attribute "DriverStatus", "string"
//	attribute "DriverUpdate", "string"
	}

preferences {		
	input("ip", "string", title:"IP Address", description: "cpuTemperature", defaultValue: "" ,required: true, displayDuringSetup: true)		
	input("port", "string", title:"Port", description: "8080", defaultValue: "8080" , required: true, displayDuringSetup: true)		
	input "refreshEvery", "enum", title: "Enable auto refresh every XX Minutes", required: false, defaultValue: false, //RK
			options: [5:"5 minutes",10:"10 minutes",15:"15 minutes",30:"30 minutes"] //RK
	input name: "debugOutput", type: "bool", title: "Enable debug logging?", defaultValue: true
	input name: "txtEnable", type: "bool", title: "Enable descriptionText logging", defaultValue: true //RK
}
}

def installed() {
	log.debug "installed"
	initialize();
}

// App Version   *********************************************************************************
def setVersion(){
	state.Version = "1.0.3"
	state.InternalName = "RaspberryPiStats"

	sendEvent(name: "DriverAuthor", value: "sgrayban")
	sendEvent(name: "DriverVersion", value: state.Version)
	sendEvent(name: "DriverStatus", value: state.Status)
}

def updated() {
	log.info "Preferences updated..."
	log.warn "Debug logging is: ${debugOutput == true}"
	unschedule()
	if (debugOutput) runIn(1800,logsOff)
	"runEvery${refreshEvery}Minutes"(autorefresh) //RK
	log.info "Refresh set for every ${refreshEvery} Minutes" //RK
	version()
	initialize();
}

def ping() {
	logDebug "ping"
	poll()
}

def initialize() {
	if (txtEnable) log.info "initialize" //RK
	refresh()
}

def logsOff(){
	log.warn "debug logging disabled..."
	device.updateSetting("debugOutput",[value:"false",type:"bool"])
}

// parse events into attributes
def parse(description) {
	logDebug "Parsing '${description}'"
	
	def msg = parseLanMessage(description)
	def headerString = msg.header
	def bodyString   = msg.body

	logDebug "received body:\n${bodyString}"
	
	if(bodyString.trim() == "ok")
	return
	
	def json = null;
	try{
	json = new groovy.json.JsonSlurper().parseText(bodyString)
		logDebug "${json}"
		
		if(json == null){
	logDebug "body object not parsed"
			return
		}
	}
	catch(e){
	log.error("Failed to parse json e = ${e}")
		return
	}

	logDebug "JSON '${json}'"
	
	if (json.containsKey("cpuTemperature")) {
	if (getTemperatureScale() == "C") {
		sendEvent(name: "temperature", value: json.cpuTemperature)
		} else {
	def fahrenheit = json.cpuTemperature * 9 / 5 + 32
		sendEvent(name: "temperature", value: fahrenheit)
		}
	}
	if (json.containsKey("freeMemory")) {
		sendEvent(name: "freeMemory", value: (json.freeMemory/1024/1024).toDouble().round(2))
		if (json.containsKey("totalMemory")) {
		sendEvent(name: "freeMemoryPercent", value: (json.freeMemory/json.totalMemory*100).toDouble().round())
		}
	}
	if (json.containsKey("cpuCoreVoltage")) {
	sendEvent(name: "cpuCoreVoltage", value: json.cpuCoreVoltage)
	}
	if (json.containsKey("modelName")) {
	sendEvent(name: "modelName", value: json.modelName)
	}
	if (json.containsKey("boardType")) {
	sendEvent(name: "boardType", value: json.boardType)
	}
	if (json.containsKey("javaVersion")) {
	sendEvent(name: "javaVersion", value: json.javaVersion)
	}
	if (json.containsKey("hostname")) {
	sendEvent(name: "hostname", value: json.hostname)
	}
	if (json.containsKey("serialNumber")) {
	sendEvent(name: "serialNumber", value: json.serialNumber)
	}
}

// handle commands
def poll() {
	if (txtEnable) log.info "Executing 'poll'" //RK
	getPiInfo()
}

def refresh() {
	if (txtEnable) log.info "Executing 'refresh'" //RK
//	version() // used for testing
	getPiInfo()
}

// RK
def autorefresh() {//RK
	if (txtEnable) log.info "Executing 'auto refresh'" //RK
	getPiInfo()//RK
}//RK

private getPiInfo() {
	def iphex = convertIPtoHex(ip)
	def porthex = convertPortToHex(port)
	
	def uri = "/api/pi"
	def headers=[:]
	headers.put("HOST", "${ip}:${port}")
	headers.put("Accept", "application/json")

	def hubAction = new hubitat.device.HubAction(
		method: "GET",
		path: uri,
		headers: headers,
		"${iphex}:${porthex}",
		[callback: parse]
	)
	logDebug "Getting Pi data ${hubAction}"
	hubAction 
}

private String convertIPtoHex(ipAddress) {
	logDebug "convertIPtoHex ${ipAddress} to hex"
	String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
	return hex
}

private String convertPortToHex(port) {
	logDebug "convertPortToHex ${port} to hex"
	String hexport = port.toString().format( '%04x', port.toInteger() )
	return hexport
}

private Integer convertHexToInt(hex) {
	return Integer.parseInt(hex,16)
}

private String convertHexToIP(hex) {
	return [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
}
def sync(ip, port) {
	logDebug "sync ${ip} ${port}"
	def existingIp = getDataValue("ip")
	def existingPort = getDataValue("port")
	if (ip && ip != existingIp) {
		updateDataValue("ip", ip)
	}
	if (port && port != existingPort) {
		updateDataValue("port", port)
	}
	def ipHex = convertIPToHex(ip)
	def portHex = convertPortToHex(port)
	device.deviceNetworkId = "${ipHex}:${portHex}"
}

private logDebug(msg) {
	if (settings?.debugOutput || settings?.debugOutput == null) {
	log.debug "$msg"
	}
}

private dbCleanUp() {
	unschedule()
	state.remove("version")
	state.remove("Version")
}

// Check Version   ***** with great thanks and acknowlegment to Cobra (github CobraVmax) for his original code **************
def version(){
	updatecheck()
	schedule("0 0 18 1/1 * ? *", updatecheck) // Cron schedule
//	schedule("0 0/1 * 1/1 * ? *", updatecheck) // Test Cron schedule
}

def updatecheck(){
	setVersion()
	 def paramsUD = [uri: "https://sgrayban.github.io/Hubitat-Public/version.json"]
	  try {
			httpGet(paramsUD) { respUD ->
				  //  log.warn " Version Checking - Response Data: ${respUD.data}"   // Troubleshooting Debug Code - Uncommenting this line should show the JSON response from your webserver
				  def copyrightRead = (respUD.data.copyright)
				  state.Copyright = copyrightRead
				  def newVerRaw = (respUD.data.versions.Driver.(state.InternalName))
				  def newVer = (respUD.data.versions.Driver.(state.InternalName).replace(".", ""))
				  def currentVer = state.Version.replace(".", "")
				  state.UpdateInfo = (respUD.data.versions.UpdateInfo.Driver.(state.InternalName))
				  state.author = (respUD.data.author)
				  if(newVer == "NLS"){
					   state.Status = "<b>** This driver is no longer supported by $state.author  **</b>"       
					   log.warn "** This driver is no longer supported by $state.author **"      
				  }           
				  else if(currentVer < newVer){
					   state.Status = "<b>New Version Available (Version: $newVerRaw)</b>"
					   log.warn "** There is a newer version of this driver available  (Version: $newVerRaw) **"
					   log.warn "** $state.UpdateInfo **"
				 } 
				 else if(currentVer > newVer){
					   state.Status = "<b>You are using a Test version of this Driver (Version: $newVerRaw)</b>"
				 }
				 else{ 
					 state.Status = "Current"
					 log.info "You are using the current version of this driver"
				 }
			} // httpGet
	  } // try

	  catch (e) {
		   log.error "Something went wrong: CHECK THE JSON FILE AND IT'S URI -  $e"
	  }

	  if(state.Status == "Current"){
		   state.UpdateInfo = "Up to date"
		   sendEvent(name: "DriverUpdate", value: state.UpdateInfo)
		   sendEvent(name: "DriverStatus", value: state.Status)
	  }
	  else {
		   sendEvent(name: "DriverUpdate", value: state.UpdateInfo)
		   sendEvent(name: "DriverStatus", value: state.Status)
	  }

	  sendEvent(name: "DriverAuthor", value: "sgrayban")
	  sendEvent(name: "DriverVersion", value: state.Version)
}

#10

So this auto refreshes the PI stats ?


#11

Yes mate, it calls autorefresh every XX mins, which is just "refresh()" :+1:


#12

Ok Im going to merge your edit into 1.0.4 and bump the version


#13

Sorry mate line 67 should be this without the 1, 2 mins.

			options: [5:"5 minutes",10:"10 minutes",15:"15 minutes",30:"30 minutes"] //RK

#14

Hopefully no more edits anytime soon... Brain is getting silly on groovy code

Version 1.0.5 released

  • Merged edits from @Royski for auto refresh of pi stats

#15

@sgrayban

Feature request:

  • Any way to display the average CPU utilization?

#16

You mean like a graph ? Or the load averages?


#17

Probably just the load averages.


#18

New version 1.0.6

  • Merge code for LastRefresh attribute @Royski

#19

I looked at the project and it doesn't offer that in its java library.

These are the settings its does.

----------------------------------------------------
PLATFORM INFO
----------------------------------------------------
Platform Name     :  Raspberry Pi
Platform ID       :  raspberrypi
----------------------------------------------------
HARDWARE INFO
----------------------------------------------------
Serial Number     :  000000000000000
CPU Revision      :  4
CPU Architecture  :  7
CPU Part          :  0xd03
CPU Temperature   :  38.6
CPU Core Voltage  :  1.375
CPU Model Name    :  ARMv7 Processor rev 4 (v7l)
Processor         :  3
Hardware          :  BCM2835
Hardware Revision :  a020d3
Is Hard Float ABI :  true
Board Type        :  RaspberryPi_3B_Plus
----------------------------------------------------
MEMORY INFO
----------------------------------------------------
Total Memory      :  918183936
Used Memory       :  332713984
Free Memory       :  114487296
Shared Memory     :  19263488
Memory Buffers    :  470712320
Cached Memory     :  502120448
SDRAM_C Voltage   :  1.25
SDRAM_I Voltage   :  1.25
SDRAM_P Voltage   :  1.225
----------------------------------------------------
OPERATING SYSTEM INFO
----------------------------------------------------
OS Name           :  Linux
OS Version        :  4.14.98-v7+
OS Architecture   :  arm
OS Firmware Build :  2e98b31d18547962e564bdf88e57b3df7085c29b (clean) (release) 
(start_x)
OS Firmware Date  :  Mar 27 2019 15:48:59
----------------------------------------------------
JAVA ENVIRONMENT INFO
----------------------------------------------------
Java Vendor       :  Oracle Corporation
Java Vendor URL   :  http://java.oracle.com/
Java Version      :  1.8.0_65
Java VM           :  Java HotSpot(TM) Client VM
Java Runtime      :  Java(TM) SE Runtime Environment
----------------------------------------------------
NETWORK INFO
----------------------------------------------------
Hostname          :  office
IP Addresses      :  192.168.0.10
IP Addresses      :  2602:3f:e686:a600:230:5c90:4d0f:d88c
IP Addresses      :  fd00::a34c:7944:6f06:de07
FQDN              :  office.borgnet.us
FQDN              :  office.borgnet.us
FQDN              :  office.borgnet.us
Nameserver        :  192.168.0.36
Nameserver        :  8.8.8.8
Nameserver        :  2001:428::1
Nameserver        :  2001:428::2
----------------------------------------------------
CODEC INFO
----------------------------------------------------
H264 Codec Enabled:  true
MPG2 Codec Enabled:  true
WVC1 Codec Enabled:  true
----------------------------------------------------
CLOCK INFO
----------------------------------------------------
ARM Frequency     :  1400000000
CORE Frequency    :  400000000
H264 Frequency    :  0
ISP Frequency     :  300000000
V3D Frequency     :  0
UART Frequency    :  48000000
PWM Frequency     :  0
EMMC Frequency    :  200000000
Pixel Frequency   :  65000000
VEC Frequency     :  0
HDMI Frequency    :  163683000
DPI Frequency     :  0

#20

@sgrayban

Bummer...was hoping to see how much the processor was being utilized over time. I am using @corerootedxb's solution with NodeRed to gather long term statistics. Having the processing utilization would enable me to know when to add more RPi's. :slight_smile: