Capturing Energy Usage with Lua Scripts
The Owl Energy Monitor and Domoticz
The Owl CM113 energy monitor has 3 sockets into which standard Owl current clamps can be attached. In my house I have seperate electrical circuits for the general household usage, the ground source heatpump usage and my solar photovoltaic generation, with household on line 1, heatpump on line 2 and solar on line 3 of the Owl CM113.
Domoticz is very smart and plots graphs of all three current sources as watts power on a single log graph from the utility page. The graph is smart, so clicking on the legends for each individual power usage will hide that plot, allowing any combination of plots to be produced.
Generating Additional Information
However, it would nice to be able to see how much energy we are using in a number of different ways, solar energy that was generated yesterday, household energy that was used yesterday and the amount of energy the heatpump used yesterday. Of course the other two things that mater is the cost i.e how much energy we are importing (household + heatpump - solar) and how smug we should be feeling by donating green energy to everybody else i.e. how much energy we are exporting (solar - household + heatpump). Looking at daily figures doesn't give me these figures, as if I only use 20kWhs in the morning while generating nothing and generate 20kWhs in the afternoon while using nothing, my overall energy usage will be nothing, but I will be billed for 20kWhs and have donated 20kWhs. So this needs to be an instaneous calculation which will record the instaneous import or export of energy.
Setting Up Domoticz
In Domoticz under Setup / Hardware having created a Dummy hardware then using Create Virtual Sensor - create 3 Electric (Instant + Counter) meters (Household, Heatpump, Solar) and 2 Counters (Imported, Exported). Once all these devices have been set-up then go to the Setup / Devices page and make a record of each id to change the script below so it will work on your machine.
Calculating Energy Usage with Lua
The two Domoticz meters expect to be fed a cumulative value of energy. So the approach I have taken is to take the current readings and multiply that by a set voltage to give the instaneous power. I then calculate the energy used the last reading as the multiple of the current power times the time since the last energy reading was returned. Then adding this new energy used to the cumulative energy usage returned from the virtual meters, gives a new total energy usage.
As the time between Owl current readings is not uniform, I can not gaurentee that energy calculated will be entirely accurate as it will not fit up short time fluctuations and by not averaging between the previous and current power levels. However any error is relatively small and will not stop the various meters from giving a good handle on energy usage and generation.
Updating Energy Usage with Lua
The simplest method is purely to send the current power and the cumulative energy via commandArray['UpdateDevice'], but only 1 value per script can be sent this way:
commandArray['UpdateDevice'] = id .. "|0|" .. power .. ";" .. energy
Since version 1.700 of Domoticz it has been possible to send multiple updates via this route:
commandArray[1] = {['UpdateDevice'] = houseid .. "|0|" .. housepower .. ";" .. houseenergy}
commandArray[2] = {['UpdateDevice'] = pumpid .. "|0|" .. pumppower .. ";" .. pumpenergy}
commandArray[3] = {['UpdateDevice'] = solarid .. "|0|" .. solarpower .. ";" .. solarenergy}
See the Events page for the full context.
For simpler meter then the command is:
commandArray[4] = {['UpdateDevice'] = exportedid .. "|0|" .. exportenergy}
A Script to Process Energy Usage
The script ~/domoticz/scripts/lua/script_device_owl_log.lua is activated everytime the Owl energy meter transmits a new set of current readings.
The scripts read in the old cumulative energy for each of the 5 channels from the dummy energy meters.
The currents from the Owl device are then converted to instanteous power by multiplying by the fixed voltage and the time since the last update is also returned. Multiplying the time since the last update by the instaneous power gives the energy used since last accumlation. The total energy can now be calculate by adding the new energy to the old energy. The instaneous power and the cumulative energy are then sent back to Domoticz.
-- /home/pi/domoticz/scripts/lua/script_device_owl_log.lua
-- Takes the instaneous current values from an Owl power monitor
-- For each current calculates instaneous power, checks when it was last called to work
-- out energy used over that time
-- Add current energy usage to previous energy usage and accumulates energy in devices
-- Updates energy monitor dummy devices in Domoticz - Household, Heat Pump, Solar
-- Plus two caculated values - Exported and Imported
function timedifference(s)
year = string.sub(s, 1, 4)
month = string.sub(s, 6, 7)
day = string.sub(s, 9, 10)
hour = string.sub(s, 12, 13)
minutes = string.sub(s, 15, 16)
seconds = string.sub(s, 18, 19)
t1 = os.time()
t2 = os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}
difference = os.difftime (t1, t2)
return difference
end
function timeCount(numSec)
local nSeconds = numSec
if nSeconds == 0 then
return "0seconds"
else
local sTime = ""
local nHours = math.floor(nSeconds/3600)
local nMins = math.floor(nSeconds/60 - (nHours*60))
if(nHours > 0) then
sTime = sTime .. nHours
if(nHours == 1) then
sTime = sTime .. "hour "
else
sTime = sTime .. "hours "
end
end
if(nMins > 0) then
sTime = sTime .. nMins
if(nMins == 1) then
sTime = sTime .. "minute "
else
sTime = sTime .. "minutes "
end
end
local nSecs = math.floor(nSeconds - nHours*3600 - nMins *60)
if(nSecs > 0) then
sTime = sTime .. nSecs
if(nSecs == 1) then
sTime = sTime .. "second"
else
sTime = sTime .. "seconds"
end
end
return sTime
end
end
function update(device, id, power, energy, index)
if (index < 4) then
commandArray[index] = {['UpdateDevice'] = id .. "|0|" .. power .. ";" .. energy}
else
commandArray[index] = {['UpdateDevice'] = id .. "|0|" .. energy}
end
return
end
commandArray = {}
local voltage = 243
local houseid = 300
local heatpumpid = 301
local solarid = 298
local usedid = 306
local generatedid = 307
if devicechanged['Owl'] then
owl = otherdevices_svalues['Owl']
print('Owl : '..owl)
words = {}
for w in string.gmatch(owl, "%d+%.?%d*") do
words[#words + 1] = w
end
nhousePower, houseEnergy = string.match(otherdevices_svalues['Household'], "(%d+%.*%d*);(%d+%.*%d*)")
nheatpumpPower, heatpumpEnergy = string.match(otherdevices_svalues['Heat Pump'], "(%d+%.*%d*);(%d+%.*%d*)")
nsolarPower, solarEnergy = string.match(otherdevices_svalues['Solar'], "(%d+%.*%d*);(%d+%.*%d*)")
usedEnergy = string.match(otherdevices_svalues['Imported'], "%d+%.*%d*")
generatedEnergy= string.match(otherdevices_svalues['Exported'], "%d+%.*%d*")
interval = timedifference(otherdevices_lastupdate['Owl Switch'])
-- convert internal in seconds into hours
interval = interval / 3600
-- calculate power (voltage * current) and cumulative energy (current energy + power * time)
-- for each monitored line
housePower = voltage * tonumber(words[1])
houseEnergy = tonumber(houseEnergy) + (housePower * interval)
heatpumpPower = voltage * tonumber(words[2])
heatpumpEnergy = tonumber(heatpumpEnergy) + (heatpumpPower * interval)
solarPower = voltage * tonumber(words[3])
solarEnergy = tonumber(solarEnergy) + (solarPower * interval)
currentPower = heatpumpPower + housePower - solarPower
if (currentPower > 0) then
usedEnergy = tonumber(usedEnergy) + (interval * currentPower)
else
generatedEnergy = tonumber(generatedEnergy) - ( interval * currentPower)
end
-- Turn on a dummy switch so I know when power was last recorded
commandArray['Owl Switch'] = 'On'
-- Single call to commandArray for Household only
update("Household", houseid, housePower, houseEnergy, 1)
-- Heat Pump
update("Heat Pump", heatpumpid, heatpumpPower, heatpumpEnergy, 2)
-- Solar only update when the sun is out means the device shows as red in Domoticz overnight, but silly to keep sending the same value back
if(solarPower~=0) then
update("Solar", solarid, solarPower, solarEnergy, 3)
end
-- Only update import or export
if (currentPower > 0) then
update("Imported", usedid, 0, usedEnergy, 4)
else
update("Exported", generatedid, 0, generatedEnergy, 4)
end
-- Power Cut Bit
PowerCut = tonumber(uservariables["PowerCut"])
if(PowerCut == 0)then
if(housePower < 10)then
commandArray['Variable:PowerCut'] = '1'
os.execute('/home/pi/telegram/scripts/generic/telegram.sh msg My_Name "Household Power Off"')
print("Household Power Off")
end
else
if(housePower > 10)then
interval = timedifference(uservariables_lastupdate['PowerCut'])
os.execute('/home/pi/telegram/scripts/generic/telegram.sh msg My_Name "Household Power Back On - after ' .. timeCount(interval) .. '"')
print("Household Power Back On - after " .. timeCount(interval))
commandArray['Variable:PowerCut'] = '0'
end
end
end
return commandArray