Energy Calculation - Total up array?

Hi

If anyone has time, please could they show me how to print each line and also calculate the total of a column in an array?

Below is my code, I seem to be able to do one of the other but not both…


local variables = {"Watts"}
local NRG_SER = "urn:micasaverde-com:serviceId:EnergyMetering1"
local total = 0

for lul_device, attr in pairs(luup.devices) do
	local readings = {}  -- create empty array called readings
	    for idx, variable in pairs(variables) do -- using the id and value in variables table above
		    local value, tstamp = luup.variable_get(NRG_SER, variable, lul_device)
			readings[idx] = {variable, tonumber(value)} -- populate readings table
	end
		for idx, reading in pairs(readings) do
			local Variable = reading[1]
			local Value = reading[2]
			if Value ~= nil then tonumber(Value)
				if Value ~= 0 then
				local total = total + Value
				print(Variable.. " - ".. Value)
                              --  return total
			end
		end
	end
end
print("Total watts = " ..total)

The current print out for me is as follows -

Print output
Watts - 5.9
Watts - 0.1
Watts - 1.5
Total watts = 0

OK, here’s a revision I’ve made, and I’ll go over each bit in comparison to yours:

local variables = {"Watts"}
local NRG_SER = "urn:micasaverde-com:serviceId:EnergyMetering1"
local total = 0

for lul_device in pairs(luup.devices) do
	local readings = {}  -- create empty array called readings
	for _, variable in ipairs(variables) do -- using the id and value in variables table above
		local value = luup.variable_get(NRG_SER, variable, lul_device)
		table.insert( readings, { variable, tonumber(value) } -- populate readings table
	end
	for _, reading in ipairs(readings) do
		local Variable = reading[1]
		local Value = reading[2] or 0
		total = total + Value
		print(Variable.. " - ".. Value)
	end
end
print("Total watts = " ..total)
  1. Since you don’t use attr in the loop, you can just drop it.
  2. variables is an array, so you should use ipairs() to iterate over it.
  3. In this particular usage, we don’t need to care about the array index looping over variables, so we use “_” as a placeholder for a discarded value (the array index).
  4. Use table.insert() to add elements to the (new) readings array rather than an index from an unrelated array.
  5. Since readings is an array, we’ll use ipairs() to iterate over it.
  6. By declaring total as local in the second loop interior, you excluded the global scope of the previous declaration of total so it could not total up properly. You don’t want to make a new local variable, so don’t use the local keyword/declaration.
  7. Since you’ve already tonumber()'d the state variable value before putting it into readings, you don’t need to do that again or dance around it with extra tests. The only thing needed is the or 0 to make sure that if the tonumber() failed ended up returning nil, you get 0 as a default/alternative value.

Finally, since you are only totaling one state variable, Watts, there’s a lot of gyration here for little benefit. You could eliminate the first loop entirely and put the fetch of Watts directly in the second loop. If you intend to add more variables in future, then you have a different problem: total will end up totaling all of those different variables, even though they may have nothing to do with one-another (e.g. totaling Watts and KWH together in one value makes no sense). So fix that, your code would need to look more like this:

local variables = {"Watts"}
local NRG_SER = "urn:micasaverde-com:serviceId:EnergyMetering1"
local total = {}

for lul_device, attr in pairs(luup.devices) do
	local readings = {}  -- create empty array called readings
	for _, variable in ipairs(variables) do -- using the id and value in variables table above
		local value = luup.variable_get(NRG_SER, variable, lul_device)
		table.insert( readings, { variable, tonumber(value) } -- populate readings table
	end
	for _, reading in pairs(readings) do
		local Variable = reading[1]
		local Value = reading[2] or 0
		total[Variable] = (total[Variable] or 0) + Value
		print(Variable.. " - ".. Value)
	end
end
for v,d in pairs(total) do
	print("Total "..v.." = "..d)
end

Consider this pseudo-code. There may be small errors that need to be fixed; I haven’t run it, I’m just winging it here.

Thanks so much for responding @rigpapa and also for the Lua lesson - every little bit of wisdom helps ! much appreciated!

1 Like

@rigpapa what would it take to turn the above into a simple “Watt Monitor” app ? A basic, real-time tile on the Vera dashboard that tells you how much energy (in watts) you are using ?

Would you create a separate ‘watch table’ of devices? Or would you add the device ID to the ‘variable’ list in the earlier code ?

Side Note - There is/was a plug-in that did something similar to that called Power Arithmetic, created in UI5, that seems to be a bit buggy under UI7.

Not difficult. You’re a good bit there with the code you have. If you stay away from building a UI and just use state variables for configuration, you can probably have something up and running in a couple of hours based on the code here.

It’s actually something you could do in Reactor with a little help from VirtualSensor.

Here’s show I would approach that target:

  1. Create a virtual generic sensor in VirtualSensor. Note its device number.

  1. In your ReactorSensor, create an expression/variable with a list of the devices you want to accumulate for:

  1. Use that list in an iterate() expression to get all the current watt measurements:

  1. That produces an array of the current Watts values for the listed devices, so then add them up:

  1. We’re done with Expressions, so now go to Conditions and add a condition that triggers whenever the watts_sum value changes:

  1. When this condition trips true momentarily, that means the sum of watts has updated, so write the new sum to our virtual generic sensor created in step 1 (on the Activities tab now):

Reactor will automatically watch all of the devices used in the expressions, so any time the Watts value changes on any of them, the whole thing springs to life, recalculates, and updates the virtual sensor.

You can use this same approach to handle any other variable you want to scan and sum. Just add new iterate and sum expressions, replacing the service ID and variable name to the new target, and the changes condition and activity for the new sum. And by the way, you can conflate the three expressions to one, I just kept them separate for clarity.

Remember that if you’re watching this on the Status tab of your ReactorSensor, the changes condition works too fast for the UI to see, so if you want to slow it down a little so you can see it, add a “delay reset” option of a couple of seconds on that condition.

Exercise for readers/Reactor gurus: you can build the watts_list dynamically from all devices that have the Watts state variable defined on them. PM me your solutions!

1 Like