-- This script monitors power data from an Envi CurrentCost meter with 2 power clamps, one on the incoming power supply -- and the other on the Solar PV supply. It then switches on/off one or two 500W water heaters if there is sufficient -- power available. module(...,package.seeall) require ("xap") require ("xap.bsc") require ("string") info={ version="1.1", description="PV power switcher" } -- Define global variables here. last_Using = 0 last_Generating = 0 last_time = os.time() time_since_last_invoked = 0 last_time_off = last_time switch_count = 0 load_Details={} load_Details[0]="000" load_Details[1]={450,0,2} load_Details[2]={400,0,3} load_Details[3]={9999,0,0} function init() local f = xap.Filter() f:add("xap-header", "source", "dbzoo.livebox.CurrentCost:ch.1") f:add("xap-header","class","xAPBSC.event") f:callback(pvwatts) local f = xap.Filter() f:add("xap-header", "source", "dbzoo.livebox.CurrentCost:ch.2") f:add("xap-header","class","xAPBSC.event") f:callback(gridwatts) end function pvwatts(frame) --print ("Generation reading received") local pwatts -- If no real data for using and generating, display "inactive message" and keep sending "OFF" messages for safety. -- The "time_since_last_invoked>2" is used to prevent duplicate triggering by .info messages received for Ch.1 and 2. if (last_Using ==0 or last_Generating ==0) and (os.time()-time_since_last_invoked) > 60 then -- print ("Data for using/generating not available - system inactive") bsc.sendState("dbzoo.livebox.Controller:rf.2", "off") bsc.sendState("dbzoo.livebox.Controller:rf.3", "off") time_since_last_invoked=os.time() end pwatts = tonumber(frame:getValue("input.state","text")) -- Evaluate message received and update variables for power generated and used if pwatts < 100 then --print (" NO power being generated - has it gone dark?") last_Generating = 0 else last_Generating=pwatts end -- Determine what can be switched on/off. If power used exceeds generating, perform immediate OFF, a wait is imposed for -- powering back on to ensure it isn't a momentary blip. --print("Generating : "..last_Generating) if last_Using>0 and last_Generating>0 then evalRFDevicesOn() end -- print(string.format('Currently generating %i, using %i', last_Generating, last_Using)) -- end end function gridwatts(frame) --print ("Usage reading received") local gwatts -- If no real data for using and generating, display "inactive message" and keep sending "OFF" messages for safety. -- The "time_since_last_invoked>2" is used to prevent duplicate triggering by .info messages received for Ch.1 and 2. if (last_Using ==0 or last_Generating ==0) and (os.time()-time_since_last_invoked) > 60 then -- print ("Data for using/generating not available - system inactive") bsc.sendState("dbzoo.livebox.Controller:rf.2", "off") bsc.sendState("dbzoo.livebox.Controller:rf.3", "off") time_since_last_invoked=os.time() end gwatts = tonumber(frame:getValue("input.state","text")) -- Evaluate message received and update variables for power generated and used last_Using=gwatts --print("Using : "..last_Using) -- Determine what can be switched on/off. If power used exceeds generating, perform immediate OFF, a wait is imposed for -- powering back on to ensure it isn't a momentary blip. if last_Using>0 and last_Generating>0 then --print("evaluating") evalRFDevicesOn() end -- print(string.format('Currently generating %i, using %i', last_Generating, last_Using)) -- end end function evalRFDevicesOn() -- print ("In loop for working out what to switch off/on") -- print ("Last switch pattern " .. load_Details[0]) local i=0 local totalPVload=0 local switchPattern = "" local time_since_last_off = os.difftime(os.time(), last_time_off) -- Calculates whether there is sufficient power being generated to switch on devices specified in load_Details. -- These are processed in order, so any dependancies need to come first. repeat i=i+1 totalPVload = totalPVload + ((load_Details[i][1])*(load_Details[i][2])) until (load_Details[i][1]==9999) --print("Current Load " .. totalPVload) i=0 repeat i=i+1 if ((last_Using - totalPVload) + load_Details[i][1]) < last_Generating then -- Checks to see if a minute has passed since last off. If it hasn't a load won't be turned on, however an existing -- load will not be turned off if (os.time()-last_time_off) > 60 then load_Details[i][2]=1 switchPattern=switchPattern.."1" else -- print ("Waiting for a minute before switching loads back on " .. os.time()-last_time_off) switchPattern=switchPattern..tostring(load_Details[i][2]) end else load_Details[i][2]=0 switchPattern=switchPattern.."0" end totalPVload=totalPVload-load_Details[i][1] --print(load_Details[i][1]) until (load_Details[i][1]==9999) -- print ("Last Switch Pattern: ".. load_Details[0].. " New Switch Pattern: ".. switchPattern) -- if things are being switched off, reset timer so delay before switching on. if tonumber(switchPattern) < tonumber(load_Details[0]) then -- print ("switching something off, last_time_off set") last_time_off = os.time() end -- finally apply the settings, do it 3 times if not (switchPattern == load_Details[0]) then switch_count = 3 load_Details[0] = switchPattern end local i=0 if (switch_count > 0) then -- print ("Switch settings need to change") repeat i=i+1 if load_Details[i][2]==1 then bsc.sendState("dbzoo.livebox.Controller:rf."..load_Details[i][3], "on") else bsc.sendState("dbzoo.livebox.Controller:rf."..load_Details[i][3], "off") end until (load_Details[i][1]==9999) switch_count=switch_count-1 end -- print ("----------") -- update_LCD(string.format('G%i U%i P%s%i', last_Generating, last_Using,switchPattern,switch_count)) end