-- collect CC data and apply filter to correct PV generated power overlayed on top of mains. module(...,package.seeall) require("xap") require("xap.bsc") require("string") require("pl") info={ version="1.0 BETA", description="Adjusted Power Consumption", username="PEEKS" } -- Working Storage local TempGenerationW = 0 local TempMainsW = 0 local TempBoilerW = 0 local bscConsumedW -- Data we want to serialize to external storage -- and Constants - put these into storage so they persist over script upgrades. local pTbl = { AverageW = 0, BaseW = 1, BoilerW = 0, GenerationW = 0, ConsumedW = 0, DebugStatus = 0, Q={a=0,b=0,c=0,d=0,e=0}, -- 4th order quadratic hysteresis=5, post=1, HAHprefix="dbzoo.livebox.HAH11" } local pStorage = "/etc/plugboard/pv.data" -- START THE APP............. function init() -- If we have a local configuration file use that otherwise use the hardcoded default values. local pString = file.read(pStorage) if pString then pTbl = pretty.read(pString) end -- Set up a new End Point bscConsumedW = bsc.Endpoint{source=pTbl.HAHprefix..".plugboard:ConsumedW", direction=bsc.INPUT, type=bsc.STREAM} -- Check the initialisation level if pTbl.ConsumedW < pTbl.BaseW then pTbl.ConsumedW = pTbl.BaseW end -- and publish it bscConsumedW:setText( tostring(pTbl.ConsumedW) ) bscConsumedW:sendEvent() --*******************************************************************************************************************DATA READ --******************************************************************************************************************* -- Get the data ********************* vvvvvv CHANGE CHANNELS TO SUIT YOUR SETUP vvvvv -- GENERATED POWER FROM THE SOLAR PV local f = xap.Filter() f:add("xap-header","source",pTbl.HAHprefix..".CurrentCost:ch.2") f:add("xap-header","class","xAPBSC.event") f:callback(function() TempGenerationW = tonumber(xap.getValue("input.state","text")) end) -- CONSUMED POWER OFF THE MAINS f = xap.Filter() f:add("xap-header","source",pTbl.HAHprefix..".CurrentCost:ch.1") f:add("xap-header","class","xAPBSC.event") f:callback(function() TempMainsW = tonumber(xap.getValue("input.state","text")) end ) -- POWER DUMPED INTO THE BOILER ************************* MUST BE ON ITS OWN POWER LINE !!!! f = xap.Filter() f:add("xap-header","source",pTbl.HAHprefix..".CurrentCost:ch.3") f:add("xap-header","class","xAPBSC.event") f:callback(function() TempBoilerW = tonumber(xap.getValue("input.state","text")) end ) -- I've set my refresh to 6 secs..... change to suit xap.Timer(filterW, 6):start() end --******************************************************************************************************************* -- FILTER FUNCTION --******************************************************************************************************************* function filterW() local QUAD, EST, GEN, MainsW, CumulativeW pTbl.GenerationW = TempGenerationW -- SET THE SOLAR PV TO ZERO IF ITS BELOW THIS THRESHOLD ************************************************************** CHANGE TO SUIT if pTbl.GenerationW < 68 then pTbl.GenerationW = 0 end MainsW = TempMainsW pTbl.BoilerW = TempBoilerW --******************************************************************************************************************* -- POLYNOMIAL = 0.0000000000615*GEN^4 - 0.00000055*GEN^3 + 0.001805*GEN^2 - 1.565*GEN + 807.64 --******************************************************************************************************************* GEN = pTbl.GenerationW if GEN < 1050 then QUAD = pTbl.Q.a * math.pow(GEN , 4) QUAD = QUAD - (pTbl.Q.b * math.pow(GEN ,3)) QUAD = QUAD + (pTbl.Q.c * math.pow(GEN ,2)) QUAD = QUAD - (pTbl.Q.d * GEN) + pTbl.Q.e else QUAD = (0.9709 * GEN) - 450.67 end QUAD = math.floor(QUAD + 0.5) --QUAD = 0 -- HUH? Is this for debugging or something? if pTbl.GenerationW > 0 then if MainsW > pTbl.GenerationW then EST = MainsW + pTbl.GenerationW - pTbl.BaseW if pTbl.BoilerW > 1 then EST = EST - MainsW end else EST = math.abs(MainsW - QUAD) + pTbl.BaseW end else EST = MainsW end EST = MainsW EST = math.floor(EST + 0.5) CumulativeW = EST -- Basic upper and lower limit checks if CumulativeW < pTbl.BaseW then CumulativeW = math.floor(pTbl.BaseW + (MainsW - (math.floor(MainsW/10)*10))) end if CumulativeW < 1 or CumulativeW > pTbl.GenerationW + MainsW then CumulativeW = MainsW end --*******************************************************************************************************************PTBL.HYSTERESIS --******************************************************************************************************************* -- Do the moving average check and calc if CumulativeW < 1150 then if CumulativeW > pTbl.AverageW then CumulativeW = math.ceil(((pTbl.AverageW * pTbl.hysteresis) + CumulativeW) / (pTbl.hysteresis + 1)) else CumulativeW = math.ceil((pTbl.AverageW + CumulativeW) / 2) end end pTbl.ConsumedW = math.ceil(CumulativeW) if pTbl.ConsumedW < pTbl.BaseW then pTbl.ConsumedW = pTbl.BaseW end bscConsumedW:setText( tostring(pTbl.ConsumedW) ) bscConsumedW:sendEvent() pTbl.AverageW = pTbl.ConsumedW --*******************************************************************************************************************POST DATA TO FILE --******************************************************************************************************************* -- I've set the pTbl.post to 9 to reduce the amount of data collected..... lower the number to collect more, if needed.... if pTbl.post > 9 then file.write(pStorage,pretty.write(pTbl)) -- And the Debug Status ( 0=Off >1=On) WATCH OUT FOR THIS - THE FILE SIZE GETS VERY BIG, VERY QUICK. -- I PHYSICALLY COPY THE RunningW FILE TO MY PC THEN EMPTY THE -- FILE ON THE HAH EVERY 24 HOURS....A LITTLE CRONTAB ACTUALLY -- DOES THAT FOR ME. if pTbl.DebugStatus > 0 then local f, err = io.open("/etc/plugboard/RunningW","a") if not f then return utils.raise(err) end f:write(os.date("%d/%m/20%y %X")..", "..MainsW..", "..pTbl.GenerationW..", "..pTbl.BoilerW..", "..pTbl.ConsumedW..", "..EST..", "..QUAD..", "..GEN.."\n") f:close() end pTbl.post = 1 else pTbl.post = pTbl.post + 1 end if MainsW > 1 and pTbl.GenerationW > -1 and pTbl.ConsumedW > 0 then postToPachube(15,"RAW", MainsW) postToPachube(16,"EST", EST) postToPachube(17,"CALC", CumulativeData) postToPachube(18,"BoilerW", pTbl.BoilerW) end end -- Posting to PACHUBE function postToPachube(id, tag, value) xap.sendShort( string.format([[ xap-header { class=pachube.update target=%s.pachube } datastream { id=%d tag=%s Value=%s }]], pTbl.HAHprefix, id, tag, value) ) end