Capturing Energy Usage with Lua Scripts

From Domoticz
Jump to navigation Jump to search

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