[{"id":"2738bb3f.d92734","type":"realtime","z":"1499a1db.3b74ae","name":"Real-time","device":"40ecb416.94a69c","x":360,"y":360,"wires":[["b714b3f4.f962f"]]},{"id":"b714b3f4.f962f","type":"function","z":"1499a1db.3b74ae","name":"Solar excess","func":"const context = context\nconst flow = flow\nconst msg = msg\n\n// Value to base charge level thresholds on\nconst thresholds = [\n  [0, 80], // From 00:00 to the next\n  [12, 50], // From 12:00 to the next\n  [18, 80] // From 18:00 to end of day\n]\n\n// Smappee values\nconst production = msg.totalExportEnergy\nconst consumption = msg.totalImportEnergy\nconst excessProduction = production > consumption\n\n// State variables\nconst lastUpdate = context.get('lastUpdate') || 0\nconst chargeLevel = flow.get('chargeLevel') || undefined\nconst chargingState = flow.get('chargingState') || false\nconst chargingMode = flow.get('chargingMode') || 'auto'\n\n// Update state every 5 minutes if necessary\nconst shouldUpdate = (Date.now() - lastUpdate) > (5 * 60 * 1000)\n\n// Message variables\nlet solarMsg = null\nlet evMsg = null\n\n// Check if charge level should be updated\nif (excessProduction && shouldUpdate) {\n  solarMsg = {payload: true}\n  context.set('lastUpdate', Date.now())\n}\n\n// Use the first value as the default\nlet threshold = thresholds[0][1]\n\n// Dynamically determine appropriate threshold value\nconst hours = new Date().getHours()\nfor (let i = 1; i < thresholds.length; i++) {\n  let condition = thresholds[i][0]\n  \n  // Always pick last applicable condition\n  if (hours <= condition) {\n    threshold = thresholds[i][1]\n  }\n}\n\n// Determine if EV should be turned on\nif (chargingMode === 'auto') {\n  if (excessProduction && !chargingState && chargeLevel > threshold) {\n    evMsg = {payload: true}\n  } else if (!excessProduction || (chargingState && chargeLevel <= threshold)) {\n    evMsg = {payload: false}\n  }\n} else if ((chargingMode === 'on' && !chargingState) || (chargingMode === 'off' && chargingState)) {\n  evMsg = {payload: chargingMode === 'on'}\n}\n\nreturn [solarMsg, evMsg, {payload: chargingMode}]\n","outputs":3,"noerr":0,"x":530,"y":360,"wires":[["da13b7cf.fd60d8"],["79bf6bac.86e7e4"],["56039770.8f1fb8"]]},{"id":"da13b7cf.fd60d8","type":"solaredge","z":"1499a1db.3b74ae","name":"Current power","site":"933a81eb.c1c66","endpoint":"currentPowerFlow","x":720,"y":300,"wires":[["a1b96ef8.74ac6"]]},{"id":"560be394.1ac29c","type":"inject","z":"1499a1db.3b74ae","name":"Manually fetch charge level","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":430,"y":300,"wires":[["da13b7cf.fd60d8"]]},{"id":"79bf6bac.86e7e4","type":"switch-state","z":"1499a1db.3b74ae","name":"Switch for EV","device":"40ecb416.94a69c","plug":"635b3d11.1187e4","x":720,"y":360,"wires":[["d135f0e2.ba8bb"]]},{"id":"a1b96ef8.74ac6","type":"function","z":"1499a1db.3b74ae","name":"Update charge level","func":"const payload = msg.payload;\nconst siteCurrentPowerFlow = payload.siteCurrentPowerFlow;\nconst storage = siteCurrentPowerFlow ? siteCurrentPowerFlow.STORAGE : undefined;\nconst chargeLevel = storage ? storage.chargeLevel : undefined;\n\nflow.set('chargeLevel', chargeLevel);","outputs":0,"noerr":0,"x":940,"y":300,"wires":[]},{"id":"d135f0e2.ba8bb","type":"function","z":"1499a1db.3b74ae","name":"Update EV state","func":"const chargingState = msg.payload.value === 'ON';\n\nflow.set('chargingState', chargingState);","outputs":0,"noerr":0,"x":960,"y":360,"wires":[]},{"id":"56039770.8f1fb8","type":"ui_dropdown","z":"1499a1db.3b74ae","name":"","label":"Select charging mode","place":"Select charging mode","group":"2c0fa1bf.fede4e","order":0,"width":0,"height":0,"passthru":true,"options":[{"label":"Charge vehicle","value":"on","type":"str"},{"label":"Do not charge vehicle","value":"off","type":"str"},{"label":"Automatic","value":"auto","type":"str"}],"payload":"","topic":"","x":740,"y":420,"wires":[["949a4189.0ba74"]]},{"id":"949a4189.0ba74","type":"function","z":"1499a1db.3b74ae","name":"Update mode","func":"const chargingMode = msg.payload;\n\nflow.set('chargingMode', chargingMode);","outputs":1,"noerr":0,"x":960,"y":420,"wires":[[]]},{"id":"bd73abc1.2e2998","type":"comment","z":"1499a1db.3b74ae","name":"Turn Switch on/off based on solar excess (with manual override)","info":"","x":530,"y":240,"wires":[]},{"id":"40ecb416.94a69c","type":"device-config","z":"","name":"Smappee Pro","serial":"2004000974","host":"192.168.0.82"},{"id":"933a81eb.c1c66","type":"solaredge-site-config","z":"","name":"","siteId":"295594","apiKey":"VRREZ6J87YD1G8RHW5W7KFBE7BQOGY06"},{"id":"635b3d11.1187e4","type":"switch-config","z":"","name":"EV","uuid":"1"},{"id":"2c0fa1bf.fede4e","type":"ui_group","z":"","name":"EV charging","tab":"50df206c.3668","order":1,"disp":true,"width":"6"},{"id":"50df206c.3668","type":"ui_tab","z":"","name":"Tom Nijsen","icon":"dashboard","order":2}]
