[{"id":"1725dce14c2040f4","type":"subflow","name":"Voltage2Watt PV","info":"**No commercial usage or reselling without my explicit written agreement.**\nI can't be accountable for any damage or discomfort expierenced due to usage of this code.\nThis _buiding block_ watches the voltage used by the inverter to and if to high then the shadow management (MPPT) is activated and reconnect is transmitted to the inverter. After a while the MPPT is deactivated again. In the meantime the inverter should have archieved a better working grade and output more than before this control mechanism was activated.\nAll settings and needed values are changeable to your own setup. There is a main config and sub config.\nIn the main config general values are defined that can be used to control several inverters. The sub config is dedicated to one inverter at a time.\nAll values are stored in msg. and transported from one block to the next one.\nThe setting in this example are for a GoodWe 2000-XS with eight panels dat operate the best at volgatages lower than 291 Volt. I also have a Wattage setting below which the can act.\nI use a Dusk setting in which control is not permited.","category":"","in":[{"x":40,"y":120,"wires":[{"id":"18677443f0c0b55a"},{"id":"a1897d3deba7e0c3"}]}],"out":[],"env":[],"meta":{"module":"Control Voltage2Watt","type":"subfow","version":"1.00","author":"Msatter on tweakers.nl forum","desc":"Correcting high voltage to increase output","keywords":"GoodWe XS","license":"Non commercial usage and not reselling"},"color":"#3FADB5","icon":"node-red-contrib-sun-position/switch-white.svg","status":{"x":560,"y":140,"wires":[{"id":"11a96b8b9d4a0148","port":0}]}},{"id":"a9f4d6fecac37199","type":"group","z":"1725dce14c2040f4","name":"Correct Voltage2Watt >291V & <301W","style":{"stroke":"#ff7f7f","fill":"#ff7f7f","label":true,"color":"#ffffff"},"nodes":["4940ffcc63a95619","f3e0a0e173312fd7","5655a6e994f5eeca"],"env":[],"x":304,"y":21.5,"w":272,"h":97},{"id":"873eb318cec8944c","type":"group","z":"1725dce14c2040f4","name":"Main cfg. Voltage2Watt","style":{"stroke":"none","fill":"#ff7f7f","label":true,"color":"#ffffff"},"nodes":["0ca84e2ea5159dfe","18677443f0c0b55a","d176998f4ee47e79"],"env":[],"x":84,"y":29,"w":192,"h":82},{"id":"6af2a536add75616","type":"group","z":"1725dce14c2040f4","name":"Send correction","style":{"stroke":"none","fill":"#ff7f7f","label":false},"nodes":["5b9da08eaa5c7d28","e26af658a97add00","f913b956d546c3af","8fddf651f5016be8","a76268a8048d3df7"],"env":[],"x":604,"y":-1,"w":152,"h":142},{"id":"b8390a3f49df51cd","type":"group","z":"1725dce14c2040f4","name":"Dusk -75 minutes.","style":{"stroke":"none","fill":"#addb7b","label":true,"label-position":"n","color":"#ffffff"},"nodes":["a1897d3deba7e0c3","6803dd9095fb3715","11060c93d1a91864"],"env":[],"x":104,"y":129,"w":172,"h":82},{"id":"4940ffcc63a95619","type":"function","z":"1725dce14c2040f4","g":"a9f4d6fecac37199","name":"Sub config file Voltage2Watt","func":"// Config node correct Voltage2Watt\n// general names and logging names and lines\n\n// When running this as sub-flow then use \"$parent.\" for \"flow.\"\n// set subflow to true or subflow to false if this is not a subflow\nsubflow = true \nif ( subflow ) { subFlow = \"$parant.\" } else { subFlow = \"\" }\n\ninvertername = 'PV' // general usable short name (string)\ninvShortName = '' // indentify a separate inverter [1-99] (number)\n\n// Values UDPsend node for this inverter\nmsg.ip = '10.10.100.253' // The IP on which the inverter is listening [10.10.100.253] (IP address)\nmsg.port = 8899 // The port on which the inverter is listening [8899] (port number)\n\n// Log values\nmsg.logline = 'PVstatus Inverter was force to waiting mode + delayed MPPT: ' + invertername\nmsg.logtype = subFlow+'statlog'\nmsg.logtime = 'logtime'\n\n// general storage GLOBAL names\nmsg.PVvolt = 'PValues' + invShortName + '.PVvolt' // Current DC voltage coming from the panels (number)\nmsg.PVwatt = 'PValues' + invShortName + '.PVwattAC' // Current wattage stated by the inverter (number)\nmsg.allowcontrol = 'PValues' + invShortName + '.control' // Controls if this control feature is allowed to intervene (true/false)\n\n// general storage FLOW names\nmsg.dusk = subFlow+'status' + invShortName + '.dusk' // Period in which this control feature is NOT allowed to intervene (true/false)\nmsg.sunVisible = subFlow+'status' + invShortName + '.sun' // Period in which this control feature is allowed to intervene (true/false)\nmsg.eventTime = subFlow+'dateTime.time' // Current time (string)\n\n// Define the message send out, makes the code in voltage control more readable\nmsg.stringMPPTon = {ip:msg.ip,port:msg.port,payload:msg.MPPTon}\nmsg.stringReconnect = {ip:msg.ip,port:msg.port,payload:msg.reconnect}\nmsg.stringMPPToff = {ip:msg.ip,port:msg.port,payload:msg.MPPToff,allowcontrol:msg.allowcontrol,delay:msg.MPPTonDelay,logtime:msg.logtime,logtype:msg.logtype}\n\n// Array with storage values needed in voltage control.\nmsg.globStorage_Array = [\n msg.PVvolt,\n msg.PVwatt,\n msg.allowcontrol\n ]\n \nmsg.flowStorage_Array = [\n msg.dusk,\n msg.sunVisible,\n msg.eventTime\n ]\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":345,"y":70,"wires":[["5655a6e994f5eeca"]],"icon":"node-red/debug.svg","l":false},{"id":"f3e0a0e173312fd7","type":"function","z":"1725dce14c2040f4","g":"a9f4d6fecac37199","name":"Volt corr. PV","func":"// When voltage is over xxx the first a MPPT then a reconnect and x minutes later MPPT OFF\n\n// only check during the day\nif ( flow.get(msg.sunVisible) ) {\n \n// show status line \"expected valtage values and if the voltage is high then display the values \n if ( global.get(msg.PVvolt) > msg.volt) {\n node.status({fill:\"yellow\",shape:\"dot\",text:(global.get(msg.PVvolt) + \"V\") + \" / \"+ (global.get(msg.PVwatt) + \"W\") + \" - \" + (context.get('delayCounter')) })\n } else { context.set('delayCounter', msg.delaycount); // reset delayCounter\n node.status({fill:\"green\",shape:\"dot\",text:\"DC voltage: good\"}) } // Control allowed\n\n// Check if storage dependencies are fullfilled\n\n check_globDepend = true; missing_globDepend = \"GlobStorage: \"\n msg.globStorage_Array.forEach(function (value,index) { \n if ( (global.get(value) != undefined) && check_globDepend) {} else { check_globDepend = false; missing_globDepend += value} } ) //end foreach\n if ( missing_globDepend == \"GlobStorage: \") { missing_globDepend = \"\" }\n \n check_flowDepend = true; missing_flowDepend = \"FlowStorage: \" ;msg.flowStorage_Array.forEach(function (value,index) { \n if ( (flow.get(value) != undefined) && check_flowDepend) {} else { check_flowDepend = false; missing_flowDepend += value} } ) //end foreach\n if ( missing_globDepend == \"flowStorage: \") { missing_flowbDepend = \"\" }\n \n if ( check_globDepend && check_flowDepend ) {\n \n if ( global.get(msg.allowcontrol) ) {\n if ( flow.get(msg.dusk) == false) { // only check if not in dusk period\n if ( global.get(msg.PVvolt) > msg.volt && global.get(msg.PVwatt) < msg.watt ) {\n \n context.set('delayCounter', ((context.get('delayCounter') || msg.delaycount ) - 1)) // delayCounter is not needed to be configured because it is a context storage an so unique\n if ( context.get('delayCounter') <= 0 ) { context.set('delayCounter', msg.delaycount) // reset delayCounter\n global.set(msg.allowcontrol, false)\n eventTime = flow.get(msg.eventTime)\n // Do some logging\n logdatetime = flow.get(msg.logtime)\n if (flow.get(msg.logtype) != undefined ) { statlogText = flow.get(msg.logtype) } else { statlogText = \"\" }\n flow.set(msg.logtype , statlogText + logdatetime + msg.logline + \" (\" + msg.invertername + \") \\n\") \n node.status({fill:\"yellow\",shape:\"dot\",text:\"Started correction: \" + flow.get(msg.eventTime) }) // monitorstatus line\n return [msg.stringMPPTon, msg.stringReconnect, msg.stringMPPToff] // short version being defined in node config\n } // delayount\n } // PVvolt PVwatt\n } else { node.status({fill:\"gray\",shape:\"dot\",text:\"Not active, Dusk.\"}) } // flow.get(msg.dusk)\n } else { node.status({fill:\"gray\",shape:\"dot\",text:\"No control allowed\"}) } // global.get(allowcontrol)\n } else { node.status({fill:\"red\", shape:\"dot\",text:\"Missing: \" + missing_globDepend + missing_flowDepend }) }// check_depend\n} else { node.status({fill:\"gray\",shape:\"dot\",text:\"Not active, no sun.\"}) }//msg.sun","outputs":3,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":70,"wires":[["a76268a8048d3df7"],["5b9da08eaa5c7d28"],["e26af658a97add00"]],"inputLabels":["trigger"],"outputLabels":["MPPT ON","reconnect","MPPT OFF"],"icon":"font-awesome/fa-code"},{"id":"5655a6e994f5eeca","type":"trigger","z":"1725dce14c2040f4","g":"a9f4d6fecac37199","name":"Repeating the whole configuration","op1":"","op2":"0","op1type":"pay","op2type":"str","duration":"-25","extend":false,"overrideDelay":true,"units":"hr","reset":"","bytopic":"topic","topic":"topic","outputs":1,"x":385,"y":70,"wires":[["f3e0a0e173312fd7"]],"icon":"font-awesome/fa-repeat","l":false,"info":"Repeats the interval given by the inject node."},{"id":"0ca84e2ea5159dfe","type":"function","z":"1725dce14c2040f4","g":"873eb318cec8944c","name":"Main config Voltage2Watt","func":"// Main config node correct Voltage2Watt\n// general names and logging names and lines\n\n// define variables \nmsg.volt = 291 // below this number no checking is done\nmsg.watt = 301 // above this number no checking is done\nmsg.delaycount = 5 // how may times this has to be the case before correction is made\nmsg.MPPTonDelay = 6 // in minutes\nmsg.MPPTonDelay = (msg.MPPTonDelay * 60000) // delay.overwrite in miliseconds before MPPT is being disable\n\n// control strings GoodWe XS inverters\nmsg.reconnect = new Buffer.from(\"7F069D8A00008D92\",\"hex\") // reconnect string\nmsg.MPPTon = new Buffer.from(\"7F069D8600018C51\",\"hex\") // MPPT ON string\nmsg.MPPToff = new Buffer.from(\"7F069D8600004D91\",\"hex\") // MPPT OFF string\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":235,"y":70,"wires":[["4940ffcc63a95619"]],"icon":"font-awesome/fa-reorder","l":false},{"id":"18677443f0c0b55a","type":"delay","z":"1725dce14c2040f4","g":"873eb318cec8944c","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"5","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":195,"y":70,"wires":[["0ca84e2ea5159dfe"]],"l":false},{"id":"d176998f4ee47e79","type":"link in","z":"1725dce14c2040f4","g":"873eb318cec8944c","name":"Correct Voltage to Watt","links":[],"x":125,"y":70,"wires":[["18677443f0c0b55a"]]},{"id":"5b9da08eaa5c7d28","type":"delay","z":"1725dce14c2040f4","g":"6af2a536add75616","name":"Reconnect","pauseType":"delay","timeout":"30","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":645,"y":70,"wires":[["f913b956d546c3af"]],"icon":"node-red-contrib-bigtimer/timer.png","l":false},{"id":"e26af658a97add00","type":"delay","z":"1725dce14c2040f4","g":"6af2a536add75616","name":"MPPT OFF","pauseType":"delayv","timeout":"6","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":645,"y":100,"wires":[["8fddf651f5016be8","f913b956d546c3af"]],"icon":"node-red-contrib-bigtimer/timer.png","l":false},{"id":"f913b956d546c3af","type":"udp out","z":"1725dce14c2040f4","g":"6af2a536add75616","name":"send correction","addr":"","iface":"","port":"","ipv":"udp4","outport":"","base64":false,"multicast":"false","x":715,"y":60,"wires":[],"l":false},{"id":"8fddf651f5016be8","type":"function","z":"1725dce14c2040f4","g":"6af2a536add75616","name":"Allow control again","func":"global.set(msg.allowcontrol,true)\n\nlogdatetime = flow.get(msg.logtime) // Do some logging\nif ( flow.get(msg.logtype) != undefined ) { statlogText = flow.get(msg.logtype) } else { statlogText = \"\" }\nflow.set(msg.logtype, statlogText + logdatetime + msg.loglabel + \" Allow control: enabled (PV2). \\n\") \n","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":715,"y":90,"wires":[],"icon":"font-awesome/fa-history","l":false},{"id":"a76268a8048d3df7","type":"delay","z":"1725dce14c2040f4","g":"6af2a536add75616","name":"MPPT ON","pauseType":"delay","timeout":"1","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":645,"y":40,"wires":[["f913b956d546c3af"]],"icon":"font-awesome/fa-arrow-right","l":false},{"id":"a1897d3deba7e0c3","type":"time-range-switch","z":"1725dce14c2040f4","g":"b8390a3f49df51cd","name":"Dawn to dusk -75 minutes","lat":"52.512","lon":"6.251","startTime":"sunset","endTime":"sunset","startOffset":"-75","endOffset":"1","x":145,"y":170,"wires":[["11060c93d1a91864"],["6803dd9095fb3715"]],"l":false},{"id":"6803dd9095fb3715","type":"function","z":"1725dce14c2040f4","g":"b8390a3f49df51cd","name":"Dusk","func":"// NEW dependencies\nglobal.set('node.statusDusk', true)\nglobal.set('node.statusDusk2', true)\n\n flow.set('status.dusk', false)\n flow.set('status2.dusk', false)","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":235,"y":170,"wires":[],"icon":"node-red-contrib-sun-position/SunSet-white.svg","l":false},{"id":"11060c93d1a91864","type":"function","z":"1725dce14c2040f4","g":"b8390a3f49df51cd","name":"Day","func":"// NEW dependencies\nglobal.set('node.statusDay', true)\nglobal.set('node.statusDay2', true)\n\nif (flow.get('status.sun')) {\n flow.set('status.dusk', true)\n flow.set('status2.dusk', true)\n} else { \n flow.set('status.dusk', false)\n flow.set('status2.dusk', false)\n}","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":195,"y":170,"wires":[],"icon":"font-awesome/fa-sun-o","l":false},{"id":"11a96b8b9d4a0148","type":"status","z":"1725dce14c2040f4","name":"Grab status lines","scope":["f3e0a0e173312fd7"],"x":380,"y":140,"wires":[[]]}]