Calculate the average value and assign it to a virtual device

I have three temperature sensors, and I want to calculate the average value. A virtual device should receive this average value. Am I on the right track? How do I assign a variable to a virtual device?

I'm doing the same with three indoor temperature sensors, but in a slightly easier way just by using the Average Temperatures found at official example-apps code: HubitatPublic/example-apps/averageTemp.groovy at master Ā· hubitat/HubitatPublic Ā· GitHub

1 Like

Very much so on the right track! Of course, should you ever add more sensors, your rule will grow geometrically, so you may want to consider using WebCore instead (which lets you perform complex calculations in a single line, and includes built-in math functions that take multiple inputs such as average, variance, etc.).

As for assigning the result from RM into a virtual temperature device, use "Custom Action":

2 Likes


I've spent way too much time on this, but it finally works! There's just a small correction left. Does anyone have any suggestions on how to improve this routine? I'm eager to learn everything about RuleMachine
Thanks to everyone who helped me!

2 Likes

Just something to consider. Depending on the importance of that average, I would also suggest that you check for valid temperatures and either ignoring an out of bound value sensor or exiting the rule without a new average. I have a 30+ devices that send temperature, and at times of teetering on low battery or other unknown reasons, a ridiculous value gets sent, such as 45727 degrees. The high average temperature in the house then ends up triggering the air conditioning and fans and emergency alerts to our phones. :roll_eyes:

It will definitely add length to your rule. Once you get one long rule down, then you can simply clone it for other averages.

I've used BPTworld's Averaging Plus. No longer maintained, but my understanding is you can still manually download the bundle and install it. Works great and very simple to use.

3 Likes

Everything looks correct, but if you're aiming for "coding compactness" (hardly necessary, since the per-line execution or storage overhead of Rule Machine rules is negligible), I'd only comment:

  • The variables in use here could be local RM vars instead of Hub Variables;
  • You only need a single variable (e.g. AvgTemp) and let it do all the work... set = 0, iteratively sum itself plus the three other values, divide / 3, voilĆ !
  • As @neerav.modi correctly advises above, especially when "living environment" real-world settings are involved (such as HVAC), it always pays to check values for valid lower and upper bounds.
2 Likes

There are a few apps out there that will do that for you. I realize your are trying to learn and I actually created a RM rule to do that myself at one point, but an app allows you to setup multiple averaging devices much easier.

The app posted below is one @bravenel created some time ago that works well. I believe there is one in HPM by @csteele as well. Others as well, as posted above. The app below along with averaging lets you assign more weight to one device over another. So I ended up switching to it. I also have a version that does humidity sensors, I modified the one below

definition(
    name: "Average Temperatures",
    namespace: "hubitat",
    author: "Bruce Ravenel",
    description: "Average some temperature sensors",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
	page(name: "mainPage")
}

def mainPage() {
	dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
		section {
			input "thisName", "text", title: "Name this temperature averager", submitOnChange: true
			if(thisName) app.updateLabel("$thisName")
			input "tempSensors", "capability.temperatureMeasurement", title: "Select Temperature Sensors", submitOnChange: true, required: true, multiple: true
			paragraph "Enter weight factors and offsets"
			tempSensors.each {
				input "weight$it.id", "decimal", title: "$it ($it.currentTemperature)", defaultValue: 1.0, submitOnChange: true, width: 3
				input "offset$it.id", "decimal", title: "$it Offset", defaultValue: 0.0, submitOnChange: true, range: "*..*", width: 3
			}
			input "useRun", "number", title: "Compute running average over this many sensor events:", defaultValue: 1, submitOnChange: true
			if(tempSensors) paragraph "Current sensor average is ${averageTemp()}Ā°"
			if(useRun > 1) {
				initRun()
				if(tempSensors) paragraph "Current running average is ${averageTemp(useRun)}Ā°"
			}
		}
	}
}

def installed() {
	initialize()
}

def updated() {
	unsubscribe()
	initialize()
}

def initialize() {
	def averageDev = getChildDevice("AverageTemp_${app.id}")
	if(!averageDev) averageDev = addChildDevice("hubitat", "Virtual Temperature Sensor", "AverageTemp_${app.id}", null, [label: thisName, name: thisName])
	averageDev.setTemperature(averageTemp())
	subscribe(tempSensors, "temperature", handler)
}

def initRun() {
	def temp = averageTemp()
	if(!state.run) {
		state.run = []
		for(int i = 0; i < useRun; i++) state.run += temp
	}
}

def averageTemp(run = 1) {
	def total = 0
	def n = 0
	tempSensors.each {
		def offset = settings["offset$it.id"] != null ? settings["offset$it.id"] : 0
		total += (it.currentTemperature + offset) * (settings["weight$it.id"] != null ? settings["weight$it.id"] : 1)
		n += settings["weight$it.id"] != null ? settings["weight$it.id"] : 1
	}
	def result = total / (n = 0 ? tempSensors.size() : n)
	if(run > 1) {
		total = 0
		state.run.each {total += it}
		result = total / run
	}
	return result.toDouble().round(1)
}

def handler(evt) {
	def averageDev = getChildDevice("AverageTemp_${app.id}")
	def avg = averageTemp()
	if(useRun > 1) {
		state.run = state.run.drop(1) + avg
		avg = averageTemp(useRun)
	}
	averageDev.setTemperature(avg)
	//log.info "Average sensor temperature = ${averageTemp()}Ā°" + (useRun > 1 ? "    Running average is $avgĀ°" : "")
}
3 Likes

Hi, it's me again! Can someone help me? Iā€™ve been trying to search and test, but I canā€™t figure out how to do this. I want to assign sum = Variable1 + Variable2 + Variable3 in a single line, not as I currently have written in a three-line routine. I hope you understand what I mean. It's the same routine Iā€™ve done, but I want to make it cleaner and more compact. Iā€™ve received some answers already, and Iā€™m very grateful for them, but itā€™s a bit hard to find in RuleMachine.

I'm not aware where you can do all of that in one line, in Rule Machine. And that's ok. Optimizing that much doesn't have any beneficial effect on hub performance.

If you want to save a line, instead of setting SumValue to 0, set it to one of the sensor temps.

Another optimization could be to combine the last two lines of your original Actions by setting Bedroom average to Sumvalue / 3 instead of going through the Average Temp local variable.

Edit: Ignore the following because variable math can't add a sensor value to the existing value: Further optimization is to set the SumValues to each sensor instead of first assigning them to the local temp1, temp2, and temp3 variables.

1 Like