[{"id":"19f54c3b.bb52b4","type":"subflow","name":"Quiet Time","info":"","category":"","in":[{"x":50,"y":30,"wires":[{"id":"96040a89e21b4330"}]}],"out":[{"x":640,"y":20,"wires":[{"id":"96040a89e21b4330","port":0}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"86b24cd21caa0ca2","type":"trigger-state","z":"19f54c3b.bb52b4","name":"","server":"194abd6174f64b4a","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityid":"input_boolean.quiet_time","entityidfiltertype":"exact","debugenabled":true,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","comparatorType":"is","comparatorValueDatatype":"bool","comparatorValue":"true","propertyValue":"new_state.state"}],"inputs":0,"outputs":2,"customoutputs":[],"outputinitially":false,"state_type":"habool","enableInput":false,"x":170,"y":100,"wires":[["e25ec9b97e298991"],[]]},{"id":"95121aa66e6b9e40","type":"debug","z":"19f54c3b.bb52b4","name":"","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":740,"y":100,"wires":[]},{"id":"e25ec9b97e298991","type":"change","z":"19f54c3b.bb52b4","name":"","rules":[{"t":"change","p":"payload","pt":"msg","from":"true","fromt":"bool","to":"close","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"control","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":100,"wires":[["95121aa66e6b9e40","96040a89e21b4330"]]},{"id":"de05add1092a0fba","type":"inject","z":"19f54c3b.bb52b4","name":"Auto off every morning","props":[{"p":"payload"}],"repeat":"","crontab":"00 07 * * *","once":false,"onceDelay":0.1,"topic":"","payload":"Off","payloadType":"str","x":150,"y":260,"wires":[["3bf8d4cfc061e976"]]},{"id":"3bf8d4cfc061e976","type":"api-call-service","z":"19f54c3b.bb52b4","name":"","server":"194abd6174f64b4a","version":3,"debugenabled":false,"service_domain":"homeassistant","service":"turn_off","entityId":"input_boolean.quiet_time","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":440,"y":260,"wires":[[]]},{"id":"96040a89e21b4330","type":"gate","z":"19f54c3b.bb52b4","name":"","controlTopic":"control","defaultState":"open","openCmd":"open","closeCmd":"close","toggleCmd":"toggle","defaultCmd":"default","statusCmd":"status","persist":false,"storeName":"memory","x":330,"y":20,"wires":[[]]},{"id":"4db0fbe58f9a64cf","type":"change","z":"19f54c3b.bb52b4","name":"","rules":[{"t":"change","p":"payload","pt":"msg","from":"false","fromt":"bool","to":"open","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"control","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":200,"wires":[["96040a89e21b4330","a11de4f389884fe4"]]},{"id":"a11de4f389884fe4","type":"debug","z":"19f54c3b.bb52b4","name":"","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":620,"y":200,"wires":[]},{"id":"ecd7078c33026927","type":"trigger-state","z":"19f54c3b.bb52b4","name":"","server":"194abd6174f64b4a","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityid":"input_boolean.quiet_time","entityidfiltertype":"exact","debugenabled":true,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","comparatorType":"is","comparatorValueDatatype":"bool","comparatorValue":"false","propertyValue":"new_state.state"}],"inputs":0,"outputs":2,"customoutputs":[],"outputinitially":false,"state_type":"habool","enableInput":false,"x":170,"y":140,"wires":[["4db0fbe58f9a64cf"],[]]},{"id":"2d2a2c5d.038d44","type":"template","z":"19f54c3b.bb52b4","name":"","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"{\"data\": {\"entity_id\":\"{{data.entity_id}}\",\"brightness\":\"{{data.attributes.brightness}}\"}}","output":"str","x":640,"y":380,"wires":[["4f51c2cc.f32b4c"]]},{"id":"64c2e48e.e6747c","type":"inject","z":"19f54c3b.bb52b4","name":"","props":[{"p":"payload","v":"","vt":"date"},{"p":"topic","v":"","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":380,"wires":[["69dd54c.1e7beac"]]},{"id":"69dd54c.1e7beac","type":"api-current-state","z":"19f54c3b.bb52b4","name":"","server":"aad53eb0.732ad","version":2,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"light.computer_desk_lights","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":380,"y":380,"wires":[["2d2a2c5d.038d44"]]},{"id":"bc9af540.9b59e8","type":"api-call-service","z":"19f54c3b.bb52b4","name":"","server":"aad53eb0.732ad","version":3,"service_domain":"light","service":"turn_on","data":"","mergecontext":"","outputProperties":[{"value":"","valueType":"data"}],"queue":"none","x":1010,"y":380,"wires":[[]]},{"id":"4f51c2cc.f32b4c","type":"delay","z":"19f54c3b.bb52b4","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":820,"y":380,"wires":[["bc9af540.9b59e8"]]},{"id":"194abd6174f64b4a","type":"server","name":"Home Assistant View Road","version":2,"addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":true,"heartbeatInterval":"30"},{"id":"aad53eb0.732ad","type":"server","name":"Home Assistant","version":2,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30},{"id":"3212da9e.c051c6","type":"tab","label":"Dishwasher Notifications","disabled":false,"info":""},{"id":"68fff468.cb9b9c","type":"group","z":"3212da9e.c051c6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["6d4066c0.5b5538","87741f06.0faa","8a0faac0.ed3cd8","f0e339fb.403208","88c2f9e4.d3ec28"],"x":914,"y":59,"w":492,"h":202},{"id":"dea8f54d.3369c8","type":"group","z":"3212da9e.c051c6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["d3480d0a.710d3","358bc1e.6d2c73e","7052a141.149e1","fbec7b11.d0d328","38575b68.9b3244","935d98a2.3a5468","b3ee141.5daf5e8","2adab366.4832ac","c59243.42db9dc"],"x":76,"y":150,"w":452,"h":291},{"id":"1d2b1a6f.4b3166","type":"group","z":"3212da9e.c051c6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["3dcdac41.088784","e1674383.22646","5e930d05.148644","3cd8c307.1ccc0c","32d5972b.de9d38","1562cab4.3354e5","215c240e.b5786c","dc1a6bd0.6d2488","4dfd9b62.2956b4"],"x":74,"y":439,"w":712,"h":462},{"id":"504e1107.a0e36","type":"group","z":"3212da9e.c051c6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["e972ff01.04024","55e827da.5342c8","429e0dfc.c678e4","ce305db2.bec81","a22de05d.65bf","90514ff9.09371","4b1c02f3.764c8c"],"x":914,"y":273,"w":452,"h":302},{"id":"0a350ca167e67741","type":"group","z":"3212da9e.c051c6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["596f20ac20d9cca9","442441a1e06f7857","480ad834f7f75e15","18d9d820deb1a5ac","6c8e65e91a34251a","bf2277069cedc234"],"x":914,"y":939,"w":472,"h":202},{"id":"0e22dede256122ac","type":"group","z":"3212da9e.c051c6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["7a941f47.6ff1d","bbcb14aa.cbe258","ba67c6f6.b6f368","2f00785e.2a5578","ca7a78be.709ed8","ea7c801e.23686","394a6033.5fc6f","ac84e311ed5952ce"],"x":914,"y":599,"w":472,"h":322},{"id":"f199ce3.6b0433","type":"comment","z":"3212da9e.c051c6","name":"Settings","info":"","x":160,"y":60,"wires":[]},{"id":"51670239.85971c","type":"function","z":"3212da9e.c051c6","name":"Settings","func":"//see Setup tab\n","outputs":1,"noerr":0,"initialize":"// -----------------------------------------\n// Notify and store power use and cost for an appliance\n// FLOW VARIABLES\n// Full loop to calculate power average of appliance and \n// work out when it is operating. When operational\n// fill arrays with power used, and cost of power calculated.\n// -----------------------------------------\n// 2021-04-12 V1.3 - zorruno\n// - Fixed loop not detecting Finished-> Standby \n// - Moved some flow variables to context\n// - Changed csv filename to reflect appliance\n// -----------------------------------------\n\n// Debugging nodes\nflow.set(\"debugFlow\", false);\n\n// Appliance Names (for notifications)\nflow.set(\"applianceName\", \"Dishwasher\"); \nflow.set(\"applianceAction\", \"dishwashing\"); \n\n//flow.set(\"JSONPowerTopic\", msg.payload.ENERGY.Power);\n\n// Announcement text (for voice notifications)\nflow.set(\"voiceAnnouncements\", [\n \"Hey, the dishwasher is now complete\",\n \"Hey, your dishes are now washed\",\n \"The dishwasher is done\",\n \"The dishwasher has finished\",\n \"Dishwasher has now finished\",\n ]);\n\n// Electrical tariff info \n// Updated with Auckland Contact Energy Costs, Feb 2021\n// cost is in cents per kWh and start and end are the times\n// to change tariffs.\nvar tariff = {\"costDay\": 0.2023, \n \"costNight\": 0.0,\n \"start\": 0,\n \"end\": 21};\n\n//---------------------------------------\n// Appliance power settings\n//---------------------------------------\n\n// Average power in standby mode (it will show off if average\n// power is above this and below the operatingPowerMinimum)\n// Currently this just affects showing 'standby' vs 'off'\n// so actual value is not critical.\nflow.set(\"standbyPower\", 2); \n// Minimum power when actually operating.\n// This is averaged over 'resolution' values, so no problem\n// if it drops below this value at times during operation.\nflow.set(\"operatingPowerMinium\", 12); \n// How many times to do a power reading for rolling average.\nflow.set(\"resolution\", 10); \n// How often does the appliance report back (seconds)\nflow.set(\"metricFrequency\", 60); \n\n//---------------------------------------\n// No need to change these \n//---------------------------------------\nflow.set(\"tariff\", tariff);\n\nflow.set(\"recentPowerArray\", [0]);\nflow.set(\"cycleCostArray\", [0]);\nflow.set(\"cyclePowerArray\", [0]);\n\nvar cycle = { \"cycleTimeStart\": null,\n \"cycleTimeStop\": null,\n \"totalCycleCostFormatted\": 0,\n \"totalCycleCostDollars\": 0,\n \"totalCyclePowerFormatted\": 0,\n \"totalCyclePowerWattHours\": 0,\n }\n\nflow.set(\"currentApplianceCycle\",cycle);\n\n\n\n","finalize":"","libs":[],"x":160,"y":100,"wires":[[]]},{"id":"4e9c5c22.773f44","type":"mqtt in","z":"3212da9e.c051c6","name":"Appliance Data","topic":"tele/tasmo-wemoskogan-5020-powermon-2/SENSOR","qos":"0","datatype":"json","broker":"e28b763a.77bd98","nl":false,"rap":false,"inputs":0,"x":340,"y":100,"wires":[["16ba89fc.146f96","bb2283f1.52d2d"]]},{"id":"a5b33574.eb15c8","type":"link out","z":"3212da9e.c051c6","name":"Dishwasher Completed","mode":"link","links":["18d9d820deb1a5ac","a22de05d.65bf","ca7a78be.709ed8","f0e339fb.403208","6b76db7f89923e36"],"x":835,"y":240,"wires":[]},{"id":"16ba89fc.146f96","type":"debug","z":"3212da9e.c051c6","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload.ENERGY.Power","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":520,"y":51,"wires":[]},{"id":"beb8061b.a50e18","type":"comment","z":"3212da9e.c051c6","name":"Operation","info":"","x":700,"y":240,"wires":[]},{"id":"fd1c5eaa.3fee6","type":"comment","z":"3212da9e.c051c6","name":"Notifications","info":"","x":986,"y":35,"wires":[]},{"id":"bb2283f1.52d2d","type":"function","z":"3212da9e.c051c6","name":"Convert to Watts","func":"// We want the msg.payload to be the power now in Watts.\n\nmsg.payload = msg.payload.ENERGY.Power;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":100,"wires":[["f84f8b7c.ea9a98","5a5bc149.fa6bc"]]},{"id":"f84f8b7c.ea9a98","type":"debug","z":"3212da9e.c051c6","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":780,"y":100,"wires":[]},{"id":"6be568c.a040b98","type":"comment","z":"3212da9e.c051c6","name":"Power Input","info":"","x":330,"y":60,"wires":[]},{"id":"5a5bc149.fa6bc","type":"function","z":"3212da9e.c051c6","name":"Main Loop","func":"// -----------------------------------------\n// Notify and store power use and cost for an appliance\n// MAIN LOOP\n// Full loop to calculate power average of appliance and \n// work out when it is operating. When operational\n// fill arrays with power used, and cost of power calculated.\n// -----------------------------------------\n// 2021-04-12 V1.3 - zorruno\n// - Fixed loop not detecting Finished-> Standby \n// - Moved some flow variables to context\n// - Changed csv filename to reflect appliance\n// -----------------------------------------\n\n// The input message must be the current power in Watts\nvar power = msg.payload;\n\n// Get flow user settings variables into local variables\nvar res = flow.get(\"resolution\");\nvar tariff = flow.get(\"tariff\");\nvar metricsf = flow.get(\"metricFrequency\");\nvar standby = flow.get(\"standbyPower\");\nvar opPowerMin = flow.get(\"operatingPowerMinium\");\nvar currentCycle = flow.get(\"currentApplianceCycle\");\n\n// Get flow variables into local variables\nvar operation = context.get(\"operation\") || \"Off\";\nvar recentPowerArray = flow.get(\"recentPowerArray\") || [0];\nvar cycleCostArray = flow.get(\"cycleCostArray\") || [0];\nvar cyclePowerArray = flow.get(\"cyclePowerArray\") || [0];\n\n// Get date, seconds and hours\nvar date = new Date();\nvar dateS = date.getTime()/1000;\nvar hour = date.getHours();\n\n// -----------------------------------------\n// Fill power array, and calculate average \n// power. Do this on every loop.\n// -----------------------------------------\n\n// Function add for reduce array\nfunction add(accumulator, a) {\n return accumulator + a;\n}\n// Push power into TotalPower array for average\nrecentPowerArray.unshift(power);\n// Remove X element to get total resolution for average calc\nif(recentPowerArray[res] === undefined) {\n flow.set(\"recentPowerArray\", recentPowerArray);\n}\nelse {\n recentPowerArray.splice(res, 1);\n flow.set(\"recentPowerArray\", recentPowerArray);\n}\n// Calculate average power from array\nvar sum = recentPowerArray;\nvar average = (sum.reduce(add)/recentPowerArray.length); // Average the array\n\n\n// -----------------------------------------\n// Fill cycle power array, and calculate average power.\n// Only do this when cycle is occurring.\n// Note this method doesn't capture EVERY data point - \n// we'll miss the first few as we are calculating an \n// average. We could capture more, but that is less efficient.\n// -----------------------------------------\nif(operation === \"Operating\"){\n // Push watthours into cyclePowerArray for cycle\n var wattHoursNow = power / ( 60 * ( 60 / metricsf ));\n cyclePowerArray.push(wattHoursNow);\n flow.set(\"cyclePowerArray\", cyclePowerArray);\n\n // Calculate the cost of power\n var price;\n if( hour >= tariff.start && hour < tariff.end ){\n price = tariff.costDay; // Apply day tariff\n }\n if( hour < tariff.start || hour >= tariff.end ){\n price = tariff.costNight; // Apply night tariff\n }\n\n // Fill cycleCostArray\n var costPerMinute = power/1000 * price / (60* (60/metricsf));\n cycleCostArray.push(costPerMinute);\n flow.set(\"cycleCostArray\", cycleCostArray); // Add to cost array\n}\n\n// -----------------------------------------\n// Appliance is off\n// -----------------------------------------\n//if(average === 0){\nif(average < standby){\n context.set(\"operation\", \"Off\");\n}\n\n// -----------------------------------------\n// Appliance has gone into Standby from Off\n// -----------------------------------------\nif(average >= standby && operation === \"Off\"){\n context.set(\"operation\", \"Standby\");\n}\n\n// -----------------------------------------\n// Appliance has gone into Standby from Finished\n// -----------------------------------------\nif(average >= standby && operation === \"Finished\"){\n context.set(\"operation\", \"Standby\");\n}\n\n// -----------------------------------------\n// Appliance has started its Operating cycle \n// from Standby or Off\n// -----------------------------------------\nif((average > opPowerMin && operation === \"Standby\") || (average > opPowerMin && operation === \"Off\")){\n context.set(\"operation\", \"Operating\");\n currentCycle.cycleTimeStart = dateS;\n cycleCostArray = [0]; // Clear array to start cycle\n flow.set(\"cycleCostArray\",cycleCostArray);\n cyclePowerArray = [0]; // Clear array to start cycle\n flow.set(\"cyclePowerArray\",cyclePowerArray);\n}\n\n// -----------------------------------------\n// Appliance was in Operating cycle, \n// but now has Finished\n// -----------------------------------------\nif(average < opPowerMin && operation === \"Operating\"){\n context.set(\"operation\", \"Finished\");\n currentCycle.cycleTimeStop = dateS;\n\n // Calculate & format total cost of the entire cycle\n var sumCost = flow.get(\"cycleCostArray\");\n var costOfPower = sumCost.reduce(add);\n currentCycle.totalCycleCostDollars = costOfPower;\n \n // Format as $ or cents\n if ( costOfPower < 0.01 ){\n costOfPower = '<1c';\n } else if ( costOfPower < 1){\n costOfPower = costOfPower * 100;\n costOfPower = Math.round(costOfPower);\n costOfPower = costOfPower.toString() + 'c';\n } else {\n costOfPower = costOfPower.toFixed(2);\n costOfPower = '$' + costOfPower.toString();\n }\n currentCycle.totalCycleCostFormatted = costOfPower ;\n\n // Calculate & format total power use of the entire cycle\n var sumPower = flow.get(\"cyclePowerArray\");\n var sumOfPower = sumPower.reduce(add);\n currentCycle.totalCyclePowerWattHours = sumOfPower;\n\n // Format as wH or kWh\n if ( sumOfPower >= 1000 ){\n sumOfPower = sumOfPower / 1000;\n sumOfPower = sumOfPower.toFixed(1); // 1 decimal place\n sumOfPower = sumOfPower.toString() + 'kWh';\n } else if ( costOfPower < 1){\n costOfPower = '<1Wh';\n } else {\n sumOfPower = sumOfPower.toFixed(1); // 1 decimal place\n sumOfPower = sumOfPower.toString() + 'Wh';\n }\n\n currentCycle.totalCyclePowerFormatted = sumOfPower ;\n\n msg.payload = context.get(\"operation\") ;\n return [msg,msg] \n}\n\n\n// -----------------------------------------\n// Output debug stuff on each loop\n// -----------------------------------------\nif(flow.get(\"debugFlow\") === true){\n\n flow.set(\"debugAverage\",average) ;\n\n // Calculate total cost of the entire cycle so far\n var costSoFar = flow.get(\"cycleCostArray\").reduce(add);\n flow.set(\"debugCostSoFar\",costSoFar) ;\n\n // Calculate total power use of the entire cycle so far\n var powerSoFar = flow.get(\"cyclePowerArray\").reduce(add);\n flow.set(\"debugPowerSoFar\",powerSoFar) ;\n\n flow.set(\"debugCyclePowerArray\",cyclePowerArray) ;\n flow.set(\"debugCycleCostArray\",cycleCostArray) ;\n flow.set(\"debugRecentPowerArray\",recentPowerArray) ;\n\n msg.payload = context.get(\"operation\") ;\n return [null,msg]\n}\n\n\n \n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":280,"wires":[["a5b33574.eb15c8"],["1562cab4.3354e5"]]},{"id":"6d4066c0.5b5538","type":"pushover","z":"3212da9e.c051c6","g":"68fff468.cb9b9c","name":"Pushover Phone MiZor,Kim_iphone","device":"MiZor,Kim_iphone","title":"","priority":0,"sound":"pianobar","url":"","url_title":"","html":false,"x":1240,"y":220,"wires":[]},{"id":"87741f06.0faa","type":"function","z":"3212da9e.c051c6","g":"68fff468.cb9b9c","name":"Notification Text","func":"// Get Current Cycle Symmary Array\nvar currentCycle = flow.get(\"currentApplianceCycle\");\n\nvar date = new Date();\nmsg.payload = {};\n\n// Function to convert unix epoch to X hours and Y mins\n// and format as 'Xh Xmin'. Drop the hours if less than 1.\nfunction secondsToHms(d) {\n d = Number(d);\n var h = Math.floor(d / 3600);\n var m = Math.floor(d % 3600 / 60);\n if (h < 1 ) {\n return ('0' + m).slice(-2)+\"min\";\n }\n else {\n return ('0' + h).slice(-2) + \"h \" + ('0' + m).slice(-2)+\"min\";\n }\n}\n\n// Function to convert unix epoch to 00:00\nfunction epochToFormattedTime(d) {\n d = Date(d * 1000);\n var h = d.gethours();\n var m = \"0\" + d.getminutes();\n return h + ':' + m.substr(-2);\n}\n\n\n// Extract start/stop time based on the unix timestamp\n// First multiply by 1000 so that the argument is in milliseconds, not seconds.\nvar startDateTime = new Date(currentCycle.cycleTimeStart * 1000);\nvar stopDateTime = new Date(currentCycle.cycleTimeStop * 1000);\nvar startDateHours = \"0\" + startDateTime.getHours();\nvar stopDateHours = \"0\" + stopDateTime.getHours();\nvar startDateMinutes = \"0\" + startDateTime.getMinutes();\nvar stopDateMinutes = \"0\" + stopDateTime.getMinutes();\n\n// Format times\n// Will display time in 10:30 format (no seconds)\nvar formattedStartTime = startDateHours.substr(-2) + ':' + startDateMinutes.substr(-2)\nvar formattedStopTime = stopDateHours.substr(-2) + ':' + stopDateMinutes.substr(-2)\n\n// Put together a notification ready for sending to notifying tools\nmsg.topic = flow.get(\"applianceName\") + \" Notification\";\nmsg.payload = \"Your \" + flow.get(\"applianceAction\") + \n \" is complete.\\nIt started at \" + formattedStartTime + \n \", finished at \" + formattedStopTime + \n \", and used \" + currentCycle.totalCyclePowerFormatted + \n \", taking \" + secondsToHms(currentCycle.cycleTimeStop - currentCycle.cycleTimeStart) + \n \" at a cost of \" + currentCycle.totalCycleCostFormatted;\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1260,"y":160,"wires":[["6d4066c0.5b5538"]]},{"id":"8a0faac0.ed3cd8","type":"inject","z":"3212da9e.c051c6","g":"68fff468.cb9b9c","name":"Test Notification","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1260,"y":100,"wires":[["87741f06.0faa"]]},{"id":"f0e339fb.403208","type":"link in","z":"3212da9e.c051c6","g":"68fff468.cb9b9c","name":"Appliance Pushover Notification","links":["a5b33574.eb15c8"],"x":955,"y":160,"wires":[["87741f06.0faa"]]},{"id":"88c2f9e4.d3ec28","type":"comment","z":"3212da9e.c051c6","g":"68fff468.cb9b9c","name":"Pushover App","info":"","x":1010,"y":100,"wires":[]},{"id":"d3480d0a.710d3","type":"inject","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1800","payloadType":"num","x":172,"y":271,"wires":[["b3ee141.5daf5e8"]]},{"id":"358bc1e.6d2c73e","type":"debug","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":402,"y":331,"wires":[]},{"id":"7052a141.149e1","type":"inject","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"300","payloadType":"num","x":172,"y":311,"wires":[["b3ee141.5daf5e8"]]},{"id":"fbec7b11.d0d328","type":"inject","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"30","payloadType":"num","x":172,"y":351,"wires":[["b3ee141.5daf5e8"]]},{"id":"38575b68.9b3244","type":"comment","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"Inject Values For Testing Only","info":"Can be used to generate test power values into the input","x":222,"y":191,"wires":[]},{"id":"935d98a2.3a5468","type":"inject","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3265","payloadType":"num","x":172,"y":231,"wires":[["b3ee141.5daf5e8"]]},{"id":"b3ee141.5daf5e8","type":"change","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"Passthrough","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":392,"y":271,"wires":[["358bc1e.6d2c73e","5a5bc149.fa6bc"]]},{"id":"2adab366.4832ac","type":"inject","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"4","payloadType":"num","x":172,"y":391,"wires":[["b3ee141.5daf5e8"]]},{"id":"c59243.42db9dc","type":"inject","z":"3212da9e.c051c6","g":"dea8f54d.3369c8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":310,"y":400,"wires":[["b3ee141.5daf5e8"]]},{"id":"3dcdac41.088784","type":"debug","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"Cycle: Cost Array","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugCycleCostArray')\t","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":590,"y":800,"wires":[]},{"id":"e1674383.22646","type":"debug","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"Recent Power Readings","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugRecentPowerArray')\t","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":620,"y":860,"wires":[]},{"id":"5e930d05.148644","type":"debug","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"Cycle: Power Array","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugCyclePowerArray')\t","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":600,"y":740,"wires":[]},{"id":"3cd8c307.1ccc0c","type":"comment","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"If Debugging On","info":"","x":180,"y":480,"wires":[]},{"id":"32d5972b.de9d38","type":"debug","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"Debug: Cost So Far","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugCostSoFar')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":600,"y":620,"wires":[]},{"id":"1562cab4.3354e5","type":"change","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"Passthrough","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":230,"y":700,"wires":[["32d5972b.de9d38","215c240e.b5786c","dc1a6bd0.6d2488","5e930d05.148644","3dcdac41.088784","e1674383.22646","4dfd9b62.2956b4"]]},{"id":"215c240e.b5786c","type":"debug","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"Debug: Power So Far","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugPowerSoFar')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":610,"y":680,"wires":[]},{"id":"dc1a6bd0.6d2488","type":"debug","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"Debug: Average Now","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugAverage')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":610,"y":560,"wires":[]},{"id":"4dfd9b62.2956b4","type":"debug","z":"3212da9e.c051c6","g":"1d2b1a6f.4b3166","name":"Operation","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":570,"y":500,"wires":[]},{"id":"7a941f47.6ff1d","type":"cast-to-client","z":"3212da9e.c051c6","g":"0e22dede256122ac","name":"Notify A Google Home","url":"","contentType":"","message":"","language":"","ip":"","port":"","volume":"50","x":1260,"y":880,"wires":[[]]},{"id":"bbcb14aa.cbe258","type":"play audio","z":"3212da9e.c051c6","g":"0e22dede256122ac","name":"","voice":"36","x":1170,"y":760,"wires":[]},{"id":"ba67c6f6.b6f368","type":"function","z":"3212da9e.c051c6","g":"0e22dede256122ac","name":"Random Notification Selection","func":"// Get a set of statements and push one at random back to\n// the payload. An example for a washing machine is below\n//\n// var announce = [\n// \"The washing machine is now complete\",\n// \"Your washing is complete\",\n// \"The washing is done\",\n// \"The washing machine has finished\",\n// \"Washing has now finished\",\n// ];\n \nvar announce = flow.get(\"voiceAnnouncements\");\n \nvar random = announce[Math.floor(Math.random() * announce.length)];\nmsg.payload = random;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1210,"y":720,"wires":[["bbcb14aa.cbe258","2f00785e.2a5578"]]},{"id":"2f00785e.2a5578","type":"change","z":"3212da9e.c051c6","g":"0e22dede256122ac","name":"Google Home Notification Details","rules":[{"t":"set","p":"ip","pt":"msg","to":"192.168.2.210","tot":"str"},{"t":"set","p":"language","pt":"msg","to":"En-gb","tot":"str"},{"t":"set","p":"volume","pt":"msg","to":"80","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1220,"y":820,"wires":[["7a941f47.6ff1d"]]},{"id":"ca7a78be.709ed8","type":"link in","z":"3212da9e.c051c6","g":"0e22dede256122ac","name":"Appliance Audio Announcements","links":["a5b33574.eb15c8","82804458c4c01c96"],"x":955,"y":680,"wires":[["ac84e311ed5952ce"]]},{"id":"ea7c801e.23686","type":"inject","z":"3212da9e.c051c6","g":"0e22dede256122ac","name":"Test Notification","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Test","payloadType":"str","x":1280,"y":640,"wires":[["ac84e311ed5952ce"]]},{"id":"394a6033.5fc6f","type":"comment","z":"3212da9e.c051c6","g":"0e22dede256122ac","name":"Google Home Announce","info":"","x":1050,"y":640,"wires":[]},{"id":"e972ff01.04024","type":"file","z":"3212da9e.c051c6","g":"504e1107.a0e36","name":"Push to CSV File","filename":"","appendNewline":true,"createDir":true,"overwriteFile":"false","encoding":"none","x":1250,"y":474,"wires":[["ce305db2.bec81"]]},{"id":"55e827da.5342c8","type":"function","z":"3212da9e.c051c6","g":"504e1107.a0e36","name":"Data Manipulation for CSV File","func":"// Get Current Cycle Symmary Array\nvar currentCycle = flow.get(\"currentApplianceCycle\");\n\nvar date = new Date();\nmsg.payload = {};\n\n// Function to convert unix epoch to X hours and Y mins\n// and format as 'Xh Xmin'. Drop the hours if less than 1.\nfunction secondsToHms(d) {\n d = Number(d);\n var h = Math.floor(d / 3600);\n var m = Math.floor(d % 3600 / 60);\n if (h < 1 ) { \n return ('0' + m).slice(-2)+\"min\"; \n }\n else {\n return ('0' + h).slice(-2) + \"h \" + ('0' + m).slice(-2)+\"min\"; \n }\n}\n\n// Function to convert unix epoch to 00:00\nfunction epochToFormattedTime(d) {\n d = Date(d * 1000);\n var h = d.gethours();\n var m = \"0\" + d.getminutes();\n return h + ':' + m.substr(-2);\n}\n\n\n// Extract start/stop time based on the unix timestamp\n// First multiply by 1000 so that the argument is in milliseconds, not seconds.\nvar startDateTime = new Date(currentCycle.cycleTimeStart * 1000);\nvar stopDateTime = new Date(currentCycle.cycleTimeStop * 1000);\nvar startDateHours = startDateTime.getHours();\nvar stopDateHours = stopDateTime.getHours();\nvar startDateMinutes = \"0\" + startDateTime.getMinutes();\nvar stopDateMinutes = \"0\" + stopDateTime.getMinutes();\n\n// Format times\n// Will display time in 10:30 format (no seconds)\nvar formattedStartTime = startDateHours + ':' + startDateMinutes.substr(-2)\nvar formattedStopTime = stopDateHours + ':' + stopDateMinutes.substr(-2)\n\ncsvPayload={\n \"date/time\": date,\n \"Appliance\": flow.get(\"applianceName\"), \n \"Start Time\": formattedStartTime, \n \"Finish Time\": formattedStopTime,\n \"Cycle Time Formatted\": secondsToHms(currentCycle.cycleTimeStop - currentCycle.cycleTimeStart), \n \"Cycle Time (s)\": currentCycle.cycleTimeStop - currentCycle.cycleTimeStart, \n \"Cycle Power Use Formatted\": currentCycle.totalCyclePowerFormatted, \n \"Cycle Power Use (Wh)\": currentCycle.totalCyclePowerWattHours, \n \"Cycle Cost Formatted\": currentCycle.totalCycleCostFormatted,\n \"Cycle Cost ($)\": currentCycle.totalCycleCostDollars,\n}\n\nmsg.payload=csvPayload;\nvar filename = flow.get(\"applianceName\"); \nmsg.filename = \"/data/csvlogs/\" + filename.replace(/\\s+/g, '') + \"_poweruse.csv\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1210,"y":374,"wires":[["429e0dfc.c678e4"]]},{"id":"429e0dfc.c678e4","type":"json","z":"3212da9e.c051c6","g":"504e1107.a0e36","name":"Convert to JSON","property":"payload","action":"","pretty":false,"x":1250,"y":424,"wires":[["e972ff01.04024"]]},{"id":"ce305db2.bec81","type":"debug","z":"3212da9e.c051c6","g":"504e1107.a0e36","name":"debug","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":1220,"y":534,"wires":[]},{"id":"a22de05d.65bf","type":"link in","z":"3212da9e.c051c6","g":"504e1107.a0e36","name":"Appliance History CSV File Creation","links":["a5b33574.eb15c8"],"x":955,"y":374,"wires":[["55e827da.5342c8"]]},{"id":"90514ff9.09371","type":"inject","z":"3212da9e.c051c6","g":"504e1107.a0e36","name":"Test Notification","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1260,"y":314,"wires":[["55e827da.5342c8"]]},{"id":"4b1c02f3.764c8c","type":"comment","z":"3212da9e.c051c6","g":"504e1107.a0e36","name":"Data to CSV File","info":"","x":1020,"y":314,"wires":[]},{"id":"596f20ac20d9cca9","type":"comment","z":"3212da9e.c051c6","g":"0a350ca167e67741","name":"MQTT Feed Notification","info":"","x":1040,"y":980,"wires":[]},{"id":"442441a1e06f7857","type":"inject","z":"3212da9e.c051c6","g":"0a350ca167e67741","name":"Test Notification","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1280,"y":980,"wires":[["bf2277069cedc234"]]},{"id":"480ad834f7f75e15","type":"mqtt out","z":"3212da9e.c051c6","g":"0a350ca167e67741","name":"","topic":"viewroad-status/activityfeed/dishwashercomplete","qos":"","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"e28b763a.77bd98","x":1180,"y":1100,"wires":[]},{"id":"18d9d820deb1a5ac","type":"link in","z":"3212da9e.c051c6","g":"0a350ca167e67741","name":"Appliance MQTT Report","links":["a5b33574.eb15c8"],"x":955,"y":1040,"wires":[["bf2277069cedc234"]]},{"id":"6c8e65e91a34251a","type":"moment","z":"3212da9e.c051c6","g":"0a350ca167e67741","name":"Date Format","topic":"","input":"","inputType":"msg","inTz":"Pacific/Auckland","adjAmount":0,"adjType":"days","adjDir":"add","format":"LT","locale":"en-US","output":"","outputType":"msg","outTz":"Pacific/Auckland","x":1290,"y":1040,"wires":[["480ad834f7f75e15"]]},{"id":"bf2277069cedc234","type":"change","z":"3212da9e.c051c6","g":"0a350ca167e67741","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":1040,"wires":[["6c8e65e91a34251a"]]},{"id":"ac84e311ed5952ce","type":"subflow:19f54c3b.bb52b4","z":"3212da9e.c051c6","g":"0e22dede256122ac","name":"","x":1070,"y":680,"wires":[["ba67c6f6.b6f368"]]},{"id":"e28b763a.77bd98","type":"mqtt-broker","name":"Panda","broker":"192.168.3.200","port":"1883","clientid":"","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":4,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]