Sorting by

×
zorruno wikki: Oven Reminder Notifications

Oven Reminder Notifications


Changes

2022-06-06 V1.0 - zorruno
- First Upload

Summary

Node red flow to
-- Monitor power from an Oven (done directly at the DB)
-- check when it is operating
-- Notify (voice) when it is up to temperature (first time element turns off)
-- Remind that the oven is on (voice) every X minutes (15 by default)
-- calculate cost of use based on 2 power tariffs
-- notify when complete (via various methods, and with cost/power data)
-- formats notifications nicely, eg $, c, wH, kWH etc
-- store power/cost/runtime in files (csv)
-- Allow the oven to be turned off completely (using a NC contactor)

Goals

Use Node red flow and a simple power monitoring setup to give me Oven notifications. Also... be able to turn the oven off if it has been on for a long time (eg it has been forgotten about, OR there is smoke detection)

This expands on some previous power monitoring and appliance notification, but the code is tidied up significantly.


Hardware

-- Sonoff Basic running ESPHome
-- PZEM-004T, connected to serial UART of sonoff (and current transformer around oven supply phase). Setup with a resistor mod as per https://zorruno.com/w/EnergyMonitoringPZEM004T
-- 25A contactor, connected to the Oven supply (phase) and driven by the sonoff relay. Contactor is set as Normally Closed (NC) and driven to break power to oven. Not fail safe, but will at least ensure the oven is useable if the sonoff or this setup fails.

text

ESPHome Setup code

esphome:
  name: mainovenmonitor

esp8266:
  board: sonoff_basic

# Enable logging
# baudrate set to 0 as we are using the UART elsewhere
logger:
  baud_rate: 0

# Enable Home Assistant API
api:
  encryption:
	key: "/hhrEOoxDbsbwT1Q2FyGjk312345678pkx7p6cw1eCU="

ota:
  password: ";ghd;kjhgds;kjgahk;dsjgsd;kjsad;kjng"


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
	ssid: "Mainovenmonitor Fallback Hotspot"
	password: "lgkasdgdskskh"

# fallback captive portal
captive_portal:
	
# Web server for monitoring and control
web_server:
  port: 80
  auth:
	username: admin
	password: !secret web_server_password
	
# MQTT Monitoring
mqtt:
  broker: 192.168.1.257
  topic_prefix: esphome/mainovenmonitor
  username: livingroom
  password: !secret mqtt_password

###################################
# PEZEM4 Power Monitoring
# https://esphome.io/components/sensor/pzemac.html
###################################

uart:
  rx_pin: GPIO3
  tx_pin: GPIO1
  baud_rate: 9600

modbus:

sensor:
  - platform: pzemac
	current:
	  name: "Main Oven Current"
	voltage:
	  name: "Main Oven Voltage"
	energy:
	  name: "Main Oven Energy"
	power:
	  name: "Main Oven Power"
	frequency:
	  name: "Main Oven Mains Frequency"
	power_factor:
	  name: "Main Oven Power Factor"
	update_interval: 10s
	
###################################
###################################
	
# Relay Output (on sonoff basic)    
switch:
  - platform: gpio
	name: "Main Oven Disable"
	pin: GPIO12

# LED Flashes on errors or warnings
# https://esphome.io/components/status_led.html
status_led:
  pin:
	number: GPIO13
	inverted: true



Node Red Flow

text

What does it do

The main crux of the code monitors the power from Home Assistant and a Sonoff basic with a PZEM-004T connected. The sonoff is running and takes an average over a few readings.
-- If it looks like the appliance has started up, it will start calculating the power usage and cost of power (based on $/kWh, and can use peak/offpeak pricing). --
If the power usage average looks like the appliance is finished (i.e. has been turned off), it notifies (via various methods such as a google home random voice message, pushover phone push message or what have you) of completion, and the cost of power used.
-- The first time the Oven drops to low power (i.e. the Element switched off) it announces that the Oven has reached preheat temperature.
-- The runtime is monitored, and voice announcements are made every 15 mins, as a reminder that it is still on... and also as a help so you know how long your baking has been in.
-- Plenty of debug nodes and inject nodes for testing.

Push Notifications

An example notification (using Pushover) on Android and iphone:
Pushover: https://pushover.net/ (non free, but only a one off cost for each device)

"Oven Notification
Your oven cooking is complete.
It started at 21:20, finished at 21:41, and used 856.3Wh, taking 51min at a cost of 37c"


Data to CSV File

An example of how it pushes to a CSV file (which is added to each time)

{"date/time":"2021-04-08T03:01:54.027Z","Appliance":"Oven","Start Time":"15:01","Finish Time":"15:01","Cycle Time Formatted":"00min","Cycle Time (s)":13.295000076293945,"Cycle Power Use Formatted":"179.3Wh","Cycle Power Use (Wh)":179.33333333333331,"Cycle Cost Formatted":"3c","Cycle Cost ($)":0.033282}


Configuration Example

// -----------------------------------------------------------------------
// OVEN APPLIANCE MONITOR AND REMINDER
// -----------------------------------------------------------------------
// Notify and store power use and cost for an OVEN appliance
// Full loop to calculate power average of appliance and
// work out when it is operating.  When operational,
// fill arrays with power used, and cost of power calculated.
//
// Announce when oven likely is up to temperature and also
// Every X minutes as a reminder that it is still on.
// -----------------------------------------------------------------------
// 2022-05-28 V1 - zorruno
// - Taken from previous appliance script and modified for Oven use.
// -----------------------------------------------------------------------

// Debugging nodes on (true/false)
flow.set("debugFlow", true) ;

// Appliance Names (for notifications)
flow.set("applianceName", "Oven") ;
flow.set("applianceAction", "oven cooking") ;

//flow.set("JSONPowerTopic", msg.payload.ENERGY.Power);

// Announcement text (for voice notifications)
// It is an array, and the announcement will be randomised.
flow.set("voiceAnnouncements", [
    "Hey, the oven is up to temperature",
    "Notice, the oven has reached temperature",
    "The oven is up to temperature",
    "The oven is now pre heated",
    "Hey, the oven has reached preheat temperature",
    ]) ;

// -----------------------------------------------------------------------
// Electrical tariff info  
// -----------------------------------------------------------------------
// Updated with Auckland Contact Energy Costs, Feb 2022
// cost is in cents per kWh and start and end are the times
// to change tariffs.
// Yes, free power between 9pm and 12am
var tariff = {"costDay": 0.2023,
              "costNight": 0.0,
              "start": 0,
              "end": 21} ;

// -----------------------------------------------------------------------
// applianceAction OPERATION Settings
// -----------------------------------------------------------------------
// offPower : Below this value, the applianceAction is "Off"
// standbyPower : between this and offPower, the applianceAction is "Standby"
// operatingPowerMinimum : between this and standbyPower, it is "Waiting"
//                       : above this, the appliance is "Operating"
// -----------------------------------------------------------------------

// applianceAction shows "Off" if average power is below offPower
flow.set("offPower", 0.2) ;
// applianceAction shows "Standby" if average power is below standbyPower
// It will show "Waiting" if average power is between standbyPower
// and operatingPowerMinimum
flow.set("standbyPower", 5) ;
// Minimum power when fully "Operating".
// This is averaged over 'resolution' values, so no problem
// if it drops below this value at times during operation.
flow.set("operatingPowerMinium", 35) ;
// How many times to do a power reading for rolling average.
flow.set("resolution", 6) ;
// How often does the appliance report back (seconds)
flow.set("metricFrequency", 10) ;

// -----------------------------------------------------------------------
// For Oven Specifically
// -----------------------------------------------------------------------
flow.set("applianceUpToTemperature", "No");  // Is the Oven up to temperature?
flow.set("reminderFrequency", 15);           // How often, in Mins to voice remind that it is on

// -----------------------------------------------------------------------
// No need to change these.  
// Set up the Arrays, with their default values.
// -----------------------------------------------------------------------
flow.set("tariff", tariff);

flow.set("recentPowerArray", [0]);
flow.set("cycleCostArray", [0]);
flow.set("cyclePowerArray", [0]);

var cycle = {   "cycleTimeStart": null,
                "cycleTimeStop": null,
                "totalCycleCostFormatted": 0,
                "totalCycleCostDollars": 0,
                "totalCyclePowerFormatted": 0,
                "totalCyclePowerWattHours": 0,
            }

flow.set("currentApplianceCycle",cycle);


Main javascript Function Code

All of this code (and the configuration above) is in the Node Red Code below, but is here for reference
// ---------------------------------------------------
// Modified code from the other appliance monitoring
// Specifically to measure and notify re an electric oven
// MAIN LOOP
// Full loop to calculate power average of appliance and
// work out when it is operating or up to temperature.  
// When operational it
// fill arrays with power used, and cost of power calculated.
// ---------------------------------------------------
// 2022-02-27 V1.0 - zorruno
// - First version from the previous appriance monitoring script
// ---------------------------------------------------

// The input message must be the current power in Watts
var power = parseInt(msg.payload) ;

// Get context variables into local variables
var operation = context.get("operation") || "Off" ; // current Operation
var applianceUpToTemperature = context.get("applianceUpToTemperature") || "No" ;

var reminderDue = context.get("reminderDue") ; // can't use the || trick for booleans
if (reminderDue === undefined) { reminderDue = true }

// Get flow user settings variables into local variables
var res   = flow.get("resolution") ;
var tariff = flow.get("tariff") ;
var metricsf = flow.get("metricFrequency") ;
var standby = flow.get("standbyPower") ;
var applianceName = flow.get("applianceName") ;
var opPowerMin = flow.get("operatingPowerMinium") ;
var currentCycle = flow.get("currentApplianceCycle") ;
var reminderFrequency = flow.get("reminderFrequency") ;  // How often, in Mins to voice remind that it is on

// Get flow arrays into local variables
var recentPowerArray = flow.get("recentPowerArray") || [0];
var cycleCostArray  = flow.get("cycleCostArray") || [0];
var cyclePowerArray  =  flow.get("cyclePowerArray") || [0];

// Get date, seconds and hours
var date = new Date();
var dateS = date.getTime()/1000;
var hour = date.getHours();

// ---------------------------------------------------
// AVERAGE POWER
// Fill power array, and calculate average
// power.  Do this on every loop.
// ---------------------------------------------------
//
// Function add for reduce array
function add(accumulator, a)
    {
    return accumulator + a;
    }

// Push power into TotalPower array for average
recentPowerArray.unshift(power);

// Remove X element to get total resolution for average calc
if(recentPowerArray[res] === undefined)
    {
    flow.set("recentPowerArray", recentPowerArray);
    }
else
    {
    recentPowerArray.splice(res, 1);
    flow.set("recentPowerArray", recentPowerArray);
    }

// Calculate average power from array
var sum = recentPowerArray;
var average = (sum.reduce(add)/recentPowerArray.length);   // Average the array


// ---------------------------------------------------
// OFF
// Appliance is "Off"
// 0.2 is just an arbitrary low value, in case not exactly 0
// ---------------------------------------------------
if (average < 0.2)
    {
    context.set("operation", "Off") ;
    }
// ---------------------------------------------------


// ---------------------------------------------------
// STANDBY
// Appliance is "Standby" (i.e. powered up but no element
// ---------------------------------------------------
if (average >= 0.2 && average <= standby)
    {
    context.set("operation", "Standby") ;
    }
// ---------------------------------------------------

// ---------------------------------------------------
// OPERATING
// Appliance has just started its Operating cycle
// from Standby or Off
// ---------------------------------------------------
//if((average > standby && operation === "Standby")
//  || (average > standby && operation === "Off")  ) {
 
if (average > standby && ( operation === "Standby" || operation === "Off"))
    {
    context.set("operation", "Operating");
 
    // Put the start time into the currentCycle array
    currentCycle.cycleTimeStart = dateS;  
    flow.set("currentApplianceCycle",currentCycle);
 
    // Clear the power calc array to start calculating for new cycle
    cyclePowerArray = [0]; // Clear array to start cycle
    flow.set("cyclePowerArray",cyclePowerArray);
 
    // Clear the cost array to start calculating for new cycle
    cycleCostArray = [0];
    flow.set("cycleCostArray",cycleCostArray);
    }
// ---------------------------------------------------

// ---------------------------------------------------
// OPERATING
// Appliance is now Operating
// ---------------------------------------------------
if (average >= opPowerMin && operation !== "Operating")
    {
    context.set("operation", "Operating") ;
    }
// ---------------------------------------------------

// ---------------------------------------------------
// WAITING
// Appliance is "Waiting" (i.e. is on or just up to temp)
// If has just switched to Waiting and was prevously "Operating"
// the it is "UpToTemperature"
// ---------------------------------------------------
if (average >= standby && average <= opPowerMin)
    {
    if (operation === "Operating" && applianceUpToTemperature != "Yes" )
        {
        context.set("operation", "UpToTemperature") ;
        context.set("applianceUpToTemperature", "Yes") ;
        }
    else
        {
        context.set("operation", "Waiting") ;  
        }
    }
// ---------------------------------------------------

// ---------------------------------------------------
// POWER CALCULATION
// Calculate power and cost and put into array.
// Only do this when cycle is occurring (Operating, OR Waiting).
// Note this method doesn't capture EVERY data point -
// eg we'll miss the first few as we are calculating an
// average.  We could capture more, but that is less efficient.
// ---------------------------------------------------
if( (operation === "Operating") || (operation === "Waiting")  )
    {
    // Push watthours into cyclePowerArray for cycle
    var wattHoursNow = power / ( 60 * ( 60 / metricsf )) ;
    cyclePowerArray.push(wattHoursNow) ;
    flow.set("cyclePowerArray", cyclePowerArray) ;

    // Calculate the cost of power
    var price ;
    if ( hour >= tariff.start && hour < tariff.end )
        {
        price = tariff.costDay ;         // Apply day tariff
        }
    if ( hour < tariff.start || hour >= tariff.end )
        {
        price = tariff.costNight ;       // Apply night tariff
        }

    // Fill cycleCostArray
    var costPerMinute = power/1000 * price / (60* (60/metricsf)) ;
    cycleCostArray.push(costPerMinute) ;
    flow.set("cycleCostArray", cycleCostArray) ; // Add to cost array
    }
// ---------------------------------------------------

// ---------------------------------------------------
// FINISHED
// Appliance was in Operating cycle OR Waiting,
// but now has now finished (i.e. switched off or into Standby)
// ---------------------------------------------------
//if ((average < standby && operation === "Operating")
//    || (average < standby && operation === "Waiting")  )

if (average <= standby && ( operation === "Operating" || operation === "Waiting"))
    {
    context.set("operation", "Finished") ;          // we are Finished
    context.set("applianceUpToTemperature", "No") ; // reset this for next operation
    currentCycle.cycleTimeStop = dateS ;            // log the end time

    // Calculate & format total cost of the entire cycle
    var sumCost = flow.get("cycleCostArray") ;
    var costOfPower = sumCost.reduce(add) ;
    currentCycle.totalCycleCostDollars = costOfPower ;
   
    // Format as $ or cents
    if ( costOfPower < 0.01 ){
        costOfPower = '<1c' ;
    } else if ( costOfPower < 1){
        costOfPower = costOfPower * 100 ;
        costOfPower = Math.round(costOfPower) ;
        costOfPower = costOfPower.toString() + 'c' ;
    } else {
        costOfPower = costOfPower.toFixed(2) ;
        costOfPower = '$' + costOfPower.toString() ;
    }
    currentCycle.totalCycleCostFormatted = costOfPower ;

    // Calculate & format total power use of the entire cycle
    var sumPower = flow.get("cyclePowerArray") ;
    var sumOfPower = sumPower.reduce(add) ;
    currentCycle.totalCyclePowerWattHours = sumOfPower ;

   // Format as wH or kWh
    if ( sumOfPower >= 1000 ){
        sumOfPower = sumOfPower / 1000 ;
        sumOfPower = sumOfPower.toFixed(1); // 1 decimal place
        sumOfPower = sumOfPower.toString() + 'kWh' ;
    } else if ( costOfPower < 1){
       costOfPower = '<1Wh' ;
    } else {
        sumOfPower = sumOfPower.toFixed(1); // 1 decimal place
        sumOfPower = sumOfPower.toString() + 'Wh' ;
    }
    currentCycle.totalCyclePowerFormatted = sumOfPower ;
   
    flow.set("currentApplianceCycle",currentCycle);  // Store in flow variable
}

// ---------------------------------------------------
// REMINDER
// Appliance is in Operating cycle OR Waiting,
// and is still operating (e.e above standby power)
// Send reminder (to second output asynchrously)
// ---------------------------------------------------

if (average >= standby && ( operation === "Operating" || operation === "Waiting"))
    {
    //  Calculate how long has passed in minutes from cycle start
    var msg2 ;
    var diffMilliseconds = (date - currentCycle.cycleTimeStart * 1000) ; // milliseconds between now & currentCycleStart
    var diffMinsFromStart = Math.round(((diffMilliseconds % 86400000) % 3600000) / 60000) ; // minutes between now & currentCycleStart
    if ( diffMinsFromStart > 0 && diffMinsFromStart % reminderFrequency == 0 )  // use modulus to see if a reminder is due
        {
        if ( reminderDue )
            {
            msg2 = {payload: "A reminder, the" + applianceName
                + " has been on for " + diffMinsFromStart
                + " Minutes."} ;
            node.send([null, msg2]) ;
            reminderDue = false ;
            }  
        }
        else  
        {
        reminderDue = true ;
        }
    context.set("reminderDue", reminderDue) ;  // set to node context for next round
    }
   
// ---------------------------------------------------
// Output debug stuff on each loop
// ---------------------------------------------------
if (flow.get("debugFlow") === true)
    {
    flow.set("debugAverage",average) ;
    flow.set("debugHours",hour) ;

    // Calculate total cost of the entire cycle so far
    var costSoFar = flow.get("cycleCostArray").reduce(add);
    flow.set("debugCostSoFar",costSoFar) ;

    // Calculate total power use of the entire cycle so far
    var powerSoFar = flow.get("cyclePowerArray").reduce(add);
    flow.set("debugPowerSoFar",powerSoFar) ;

    flow.set("debugCyclePowerArray",cyclePowerArray) ;
    flow.set("debugCycleCostArray",cycleCostArray) ;
    flow.set("debugRecentPowerArray",recentPowerArray) ;
   
    flow.set("debugReminderMins",diffMinsFromStart) ;
   
    flow.set("debugRemindNow",reminderDue) ;
    }

msg.payload = context.get("operation") ;
return [msg,null];


Node Red Code (Oven Notifications )

[{"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":"aa69d6219a52c4d6","type":"tab","label":"Oven Notifications","disabled":false,"info":"","env":[]},{"id":"bda712586b1c1964","type":"group","z":"aa69d6219a52c4d6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["49b0a15dac84f9a2","472646659f5b3541","4db1356efdbf8e83","ced8fa197a585a62","888283abdf05dfd2"],"x":1054,"y":99,"w":672,"h":142},{"id":"36f5580ec5a5551d","type":"group","z":"aa69d6219a52c4d6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["b0e9c1325134b458","823fbf8560c0df9c","14d4112db16ca0c9","fb7753450c4b3132","415bf3df68ea6ca3","5247ccb50a2ffacd","c77102c66558cc55","17456ff6bfb1095f","7c190130fb6a1741","8d97da4a79207b83"],"x":234,"y":179,"w":452,"h":302},{"id":"0b34903c8e63b79d","type":"group","z":"aa69d6219a52c4d6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["a9a87bc5b7df1bca","ee13a099d6298a2c","efb9fc99cd666fac","b8e4a4108b58c5fb","f2acdfe5244354f5","6ce923a42f474217","5741033e44de8bed","ba0ee44279504f29","ddaed4f024da5b26","71688b9d2a860ae0","cb6f03b66278c507","6da7350026aa133a"],"x":234,"y":499,"w":452,"h":662},{"id":"8c7c84bbd6c31889","type":"group","z":"aa69d6219a52c4d6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["d7d1471bb7935da6","5708e51658172aec","e12c42c010bd4c7e","f1ad20250c777baf","3c48b99c62c6a2ab","c25aa22035ee424b","fba7ddbe2f38c520"],"x":1054,"y":253,"w":812,"h":148},{"id":"7bd67f3d1bbf642f","type":"group","z":"aa69d6219a52c4d6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["a76bfc4296dde59e","73bcc387377a7ab7","581956480486be71","a61be1f1f40c0d08","a2d060253e0ca448","503c235049463e64"],"x":1054,"y":419,"w":792,"h":122},{"id":"d41e169c3fafbcb0","type":"group","z":"aa69d6219a52c4d6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["a3798d3c47f70b68","f91c02ec71070e1c","94ddb30f1a9e2ef1","d8d2d40ba5535537","2f3f13ccc12f6973","15a57bd93e503ac1"],"x":1054,"y":759,"w":512,"h":182},{"id":"272dc020b5c61328","type":"group","z":"aa69d6219a52c4d6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["c3735a8b230d334c","8c5d9b28bcc48e49","d3c79c4e60e0bc29","d14020bf21a7aced","4f97afa041176700","0d5408d4dcfb62c1"],"x":1054,"y":559,"w":572,"h":182},{"id":"3f27ad3e4ea74f1d","type":"group","z":"aa69d6219a52c4d6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["c124edfdb44faea1","403ab9925b217b6d","bf4334572404760b","ef8f3ea21d61bd24","d0465695d7ab7bf8","96b08f3f098196e9"],"x":1054,"y":959,"w":832,"h":142},{"id":"6e627c347b8c5096","type":"comment","z":"aa69d6219a52c4d6","name":"Settings","info":"","x":200,"y":100,"wires":[]},{"id":"b5efc0a0c8dfb177","type":"function","z":"aa69d6219a52c4d6","name":"Settings","func":"//see Setup tab\n","outputs":1,"noerr":0,"initialize":"// -----------------------------------------------------------------------\n// OVEN APPLIANCE MONITOR AND REMINDER\n// -----------------------------------------------------------------------\n// Notify and store power use and cost for an OVEN appliance\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// Announce when oven likely is up to temperature and also\n// Every X minutes as a reminder that it is still on.\n// -----------------------------------------------------------------------\n// 2022-05-28 V1 - zorruno\n// - Taken from previous appliance script and modified for Oven use.\n// -----------------------------------------------------------------------\n\n// Debugging nodes on (true/false)\nflow.set(\"debugFlow\", true) ;\n\n// Appliance Names (for notifications)\nflow.set(\"applianceName\", \"Oven\") ; \nflow.set(\"applianceAction\", \"oven cooking\") ; \n\n//flow.set(\"JSONPowerTopic\", msg.payload.ENERGY.Power);\n\n// Announcement text (for voice notifications)\n// It is an array, and the announcement will be randomised.\nflow.set(\"voiceAnnouncements\", [\n    \"Hey, the oven is up to temperature\",\n    \"Notice, the oven has reached temperature\",\n    \"The oven is up to temperature\",\n    \"The oven is now pre heated\",\n    \"Hey, the oven has reached preheat temperature\",\n    ]) ;\n\n// -----------------------------------------------------------------------\n// Electrical tariff info   \n// -----------------------------------------------------------------------\n// Updated with Auckland Contact Energy Costs, Feb 2022\n// cost is in cents per kWh and start and end are the times\n// to change tariffs.\n// Yes, free power between 9pm and 12am\nvar tariff = {\"costDay\": 0.2023, \n              \"costNight\": 0.0,\n              \"start\": 0,\n              \"end\": 21} ;\n\n// -----------------------------------------------------------------------\n// applianceAction OPERATION Settings\n// -----------------------------------------------------------------------\n// offPower : Below this value, the applianceAction is \"Off\"\n// standbyPower : between this and offPower, the applianceAction is \"Standby\"\n// operatingPowerMinimum : between this and standbyPower, it is \"Waiting\"\n//                       : above this, the appliance is \"Operating\"\n// -----------------------------------------------------------------------\n\n// applianceAction shows \"Off\" if average power is below offPower\nflow.set(\"offPower\", 0.2) ;\n// applianceAction shows \"Standby\" if average power is below standbyPower\n// It will show \"Waiting\" if average power is between standbyPower\n// and operatingPowerMinimum\nflow.set(\"standbyPower\", 5) ;\n// Minimum power when fully \"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\", 35) ;\n// How many times to do a power reading for rolling average.\nflow.set(\"resolution\", 6) ;\n// How often does the appliance report back (seconds)\nflow.set(\"metricFrequency\", 10) ;\n\n// -----------------------------------------------------------------------\n// For Oven Specifically\n// -----------------------------------------------------------------------\nflow.set(\"applianceUpToTemperature\", \"No\");  // Is the Oven up to temperature?\nflow.set(\"reminderFrequency\", 15);           // How often, in Mins to voice remind that it is on\n\n// -----------------------------------------------------------------------\n// No need to change these.  \n// Set up the Arrays, with their default values.\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":200,"y":140,"wires":[[]]},{"id":"23808a29ed20c018","type":"link out","z":"aa69d6219a52c4d6","name":"Appliance Completed","mode":"link","links":["ced8fa197a585a62","ef8f3ea21d61bd24","3c48b99c62c6a2ab"],"x":985,"y":500,"wires":[]},{"id":"c073ace0ccf735a5","type":"comment","z":"aa69d6219a52c4d6","name":"Operation","info":"","x":820,"y":280,"wires":[]},{"id":"10a50fc88593ea79","type":"comment","z":"aa69d6219a52c4d6","name":"Notifications","info":"","x":1126,"y":75,"wires":[]},{"id":"8603ae1b4dfc119f","type":"debug","z":"aa69d6219a52c4d6","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":920,"y":140,"wires":[]},{"id":"26fa6eed05eeb181","type":"comment","z":"aa69d6219a52c4d6","name":"Power Input","info":"","x":370,"y":100,"wires":[]},{"id":"49b0a15dac84f9a2","type":"pushover","z":"aa69d6219a52c4d6","g":"bda712586b1c1964","name":"Pushover Phone ","device":"Device","title":"","priority":0,"sound":"pianobar","url":"","url_title":"","html":false,"x":1600,"y":200,"wires":[]},{"id":"472646659f5b3541","type":"function","z":"aa69d6219a52c4d6","g":"bda712586b1c1964","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":"","libs":[],"x":1400,"y":200,"wires":[["49b0a15dac84f9a2"]]},{"id":"4db1356efdbf8e83","type":"inject","z":"aa69d6219a52c4d6","g":"bda712586b1c1964","name":"Test Notification","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1400,"y":140,"wires":[["472646659f5b3541"]]},{"id":"ced8fa197a585a62","type":"link in","z":"aa69d6219a52c4d6","g":"bda712586b1c1964","name":"Appliance Pushover Notification","links":["23808a29ed20c018"],"x":1095,"y":200,"wires":[["472646659f5b3541"]]},{"id":"888283abdf05dfd2","type":"comment","z":"aa69d6219a52c4d6","g":"bda712586b1c1964","name":"Pushover App","info":"","x":1150,"y":140,"wires":[]},{"id":"b0e9c1325134b458","type":"inject","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1800","payloadType":"num","x":330,"y":320,"wires":[["c77102c66558cc55"]]},{"id":"823fbf8560c0df9c","type":"debug","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":560,"y":380,"wires":[]},{"id":"14d4112db16ca0c9","type":"inject","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"40","payloadType":"num","x":330,"y":360,"wires":[["c77102c66558cc55"]]},{"id":"fb7753450c4b3132","type":"inject","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"14.2","payloadType":"num","x":330,"y":400,"wires":[["c77102c66558cc55"]]},{"id":"415bf3df68ea6ca3","type":"comment","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"Inject Values For Testing Only","info":"Can be used to generate test power values into the input","x":380,"y":220,"wires":[]},{"id":"5247ccb50a2ffacd","type":"inject","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3265","payloadType":"num","x":330,"y":280,"wires":[["c77102c66558cc55"]]},{"id":"c77102c66558cc55","type":"change","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"Passthrough","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload","tot":"msg","dc":true}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":320,"wires":[["823fbf8560c0df9c","55b2b942d7f708d2"]]},{"id":"17456ff6bfb1095f","type":"inject","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2","payloadType":"num","x":330,"y":440,"wires":[["c77102c66558cc55"]]},{"id":"7c190130fb6a1741","type":"inject","z":"aa69d6219a52c4d6","g":"36f5580ec5a5551d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":550,"y":440,"wires":[["c77102c66558cc55"]]},{"id":"a9a87bc5b7df1bca","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug : Cycle Cost Array","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugCycleCostArray')\t","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":520,"y":1060,"wires":[]},{"id":"ee13a099d6298a2c","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Recent Power Readings","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugRecentPowerArray')\t","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":520,"y":1120,"wires":[]},{"id":"efb9fc99cd666fac","type":"comment","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"If Debugging On","info":"","x":340,"y":540,"wires":[]},{"id":"b8e4a4108b58c5fb","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug: Cost So Far","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugCostSoFar')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":500,"y":880,"wires":[]},{"id":"f2acdfe5244354f5","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug: Power So Far","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugPowerSoFar')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":510,"y":940,"wires":[]},{"id":"6ce923a42f474217","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug: Average Now","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugAverage')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":510,"y":640,"wires":[]},{"id":"5741033e44de8bed","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Operation","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":470,"y":580,"wires":[]},{"id":"d7d1471bb7935da6","type":"file","z":"aa69d6219a52c4d6","g":"8c7c84bbd6c31889","name":"Push to CSV File","filename":"","appendNewline":true,"createDir":true,"overwriteFile":"false","encoding":"none","x":1750,"y":360,"wires":[["f1ad20250c777baf"]]},{"id":"5708e51658172aec","type":"function","z":"aa69d6219a52c4d6","g":"8c7c84bbd6c31889","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":1290,"y":360,"wires":[["e12c42c010bd4c7e"]]},{"id":"e12c42c010bd4c7e","type":"json","z":"aa69d6219a52c4d6","g":"8c7c84bbd6c31889","name":"Convert to JSON","property":"payload","action":"","pretty":false,"x":1550,"y":360,"wires":[["d7d1471bb7935da6"]]},{"id":"f1ad20250c777baf","type":"debug","z":"aa69d6219a52c4d6","g":"8c7c84bbd6c31889","name":"debug","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":1740,"y":300,"wires":[]},{"id":"3c48b99c62c6a2ab","type":"link in","z":"aa69d6219a52c4d6","g":"8c7c84bbd6c31889","name":"Appliance History CSV File Creation","links":["23808a29ed20c018"],"x":1095,"y":360,"wires":[["5708e51658172aec"]]},{"id":"c25aa22035ee424b","type":"inject","z":"aa69d6219a52c4d6","g":"8c7c84bbd6c31889","name":"Test Notification","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1400,"y":294,"wires":[["5708e51658172aec"]]},{"id":"fba7ddbe2f38c520","type":"comment","z":"aa69d6219a52c4d6","g":"8c7c84bbd6c31889","name":"Data to CSV File","info":"","x":1160,"y":294,"wires":[]},{"id":"a76bfc4296dde59e","type":"cast-to-client","z":"aa69d6219a52c4d6","g":"7bd67f3d1bbf642f","name":"Notify A Google Home","url":"","contentType":"","message":"","language":"","ip":"","port":"","volume":"50","x":1720,"y":500,"wires":[[]]},{"id":"73bcc387377a7ab7","type":"change","z":"aa69d6219a52c4d6","g":"7bd67f3d1bbf642f","name":"Google Home Notification Details","rules":[{"t":"set","p":"ip","pt":"msg","to":"192.168.1.256","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":1460,"y":500,"wires":[["a76bfc4296dde59e"]]},{"id":"581956480486be71","type":"link in","z":"aa69d6219a52c4d6","g":"7bd67f3d1bbf642f","name":"Appliance Google Home Announce","links":["82804458c4c01c96"],"x":1095,"y":500,"wires":[["503c235049463e64"]]},{"id":"a61be1f1f40c0d08","type":"inject","z":"aa69d6219a52c4d6","g":"7bd67f3d1bbf642f","name":"Test Notification","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Test","payloadType":"str","x":1420,"y":460,"wires":[["73bcc387377a7ab7"]]},{"id":"a2d060253e0ca448","type":"comment","z":"aa69d6219a52c4d6","g":"7bd67f3d1bbf642f","name":"Google Home Announce","info":"","x":1190,"y":460,"wires":[]},{"id":"503c235049463e64","type":"subflow:19f54c3b.bb52b4","z":"aa69d6219a52c4d6","g":"7bd67f3d1bbf642f","name":"","x":1210,"y":500,"wires":[["73bcc387377a7ab7"]]},{"id":"99e7a8ba0ddb2fce","type":"poll-state","z":"aa69d6219a52c4d6","name":"","server":"194abd6174f64b4a","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"10","updateIntervalType":"num","updateIntervalUnits":"seconds","outputinitially":false,"outputonchanged":false,"entity_id":"sensor.main_oven_power_3","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":450,"y":140,"wires":[["8603ae1b4dfc119f","55b2b942d7f708d2"]]},{"id":"ec9bb17646afd295","type":"link out","z":"aa69d6219a52c4d6","name":"Oven Up to Temp","mode":"link","links":["4f97afa041176700","d8d2d40ba5535537"],"x":985,"y":620,"wires":[]},{"id":"07facb7be37eb16b","type":"switch","z":"aa69d6219a52c4d6","name":"Operation Mode","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"Finished","vt":"str"},{"t":"eq","v":"UpToTemperature","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":840,"y":500,"wires":[["23808a29ed20c018"],["43524c3af6134ffe"]]},{"id":"ba0ee44279504f29","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug: Hours","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugHours')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":480,"y":820,"wires":[]},{"id":"ddaed4f024da5b26","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug: Reminder Mins","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugReminderMins')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":510,"y":700,"wires":[]},{"id":"71688b9d2a860ae0","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug: Reminder Due?","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugRemindNow')","targetType":"jsonata","statusVal":"payload","statusType":"auto","x":510,"y":760,"wires":[]},{"id":"8d97da4a79207b83","type":"inject","z":"aa69d6219a52c4d6","d":true,"g":"36f5580ec5a5551d","name":"","props":[{"p":"payload"}],"repeat":"10","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"1800","payloadType":"num","x":530,"y":260,"wires":[["c77102c66558cc55"]]},{"id":"55b2b942d7f708d2","type":"function","z":"aa69d6219a52c4d6","name":"MAIN OPERATION","func":"// ---------------------------------------------------\n// Modified code from the other appliance monitoring\n// Specifically to measure and notify re an electric oven\n// MAIN LOOP\n// Full loop to calculate power average of appliance and \n// work out when it is operating or up to temperature.  \n// When operational it\n// fill arrays with power used, and cost of power calculated.\n// ---------------------------------------------------\n// 2022-02-27 V1.0 - zorruno\n// - First version from the previous appriance monitoring script\n// ---------------------------------------------------\n\n// The input message must be the current power in Watts\nvar power = parseInt(msg.payload) ;\n\n// Get context variables into local variables\nvar operation = context.get(\"operation\") || \"Off\" ; // current Operation\nvar applianceUpToTemperature = context.get(\"applianceUpToTemperature\") || \"No\" ;\n\nvar reminderDue = context.get(\"reminderDue\") ; // can't use the || trick for booleans\nif (reminderDue === undefined) { reminderDue = true }\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 applianceName = flow.get(\"applianceName\") ;\nvar opPowerMin = flow.get(\"operatingPowerMinium\") ;\nvar currentCycle = flow.get(\"currentApplianceCycle\") ;\nvar reminderFrequency = flow.get(\"reminderFrequency\") ;  // How often, in Mins to voice remind that it is on\n\n// Get flow arrays into local variables\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// AVERAGE POWER\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    {\n    return accumulator + a;\n    }\n\n// Push power into TotalPower array for average\nrecentPowerArray.unshift(power);\n\n// Remove X element to get total resolution for average calc\nif(recentPowerArray[res] === undefined) \n    {\n    flow.set(\"recentPowerArray\", recentPowerArray);\n    }\nelse \n    {\n    recentPowerArray.splice(res, 1);\n    flow.set(\"recentPowerArray\", recentPowerArray);\n    }\n\n// Calculate average power from array\nvar sum = recentPowerArray;\nvar average = (sum.reduce(add)/recentPowerArray.length);   // Average the array\n\n\n// ---------------------------------------------------\n// OFF\n// Appliance is \"Off\"\n// 0.2 is just an arbitrary low value, in case not exactly 0\n// ---------------------------------------------------\nif (average < 0.2)\n    {\n    context.set(\"operation\", \"Off\") ;\n    }\n// ---------------------------------------------------\n\n\n// ---------------------------------------------------\n// STANDBY\n// Appliance is \"Standby\" (i.e. powered up but no element\n// ---------------------------------------------------\nif (average >= 0.2 && average <= standby)\n    {\n    context.set(\"operation\", \"Standby\") ;\n    }\n// ---------------------------------------------------\n\n// ---------------------------------------------------\n// OPERATING\n// Appliance has just started its Operating cycle \n// from Standby or Off\n// ---------------------------------------------------\n//if((average > standby && operation === \"Standby\") \n//  || (average > standby && operation === \"Off\")  ) {\n \nif (average > standby && ( operation === \"Standby\" || operation === \"Off\"))\n    {\n    context.set(\"operation\", \"Operating\"); \n  \n    // Put the start time into the currentCycle array\n    currentCycle.cycleTimeStart = dateS;  \n    flow.set(\"currentApplianceCycle\",currentCycle);\n \n    // Clear the power calc array to start calculating for new cycle\n    cyclePowerArray = [0]; // Clear array to start cycle\n    flow.set(\"cyclePowerArray\",cyclePowerArray);\n  \n    // Clear the cost array to start calculating for new cycle\n    cycleCostArray = [0]; \n    flow.set(\"cycleCostArray\",cycleCostArray);\n    }\n// ---------------------------------------------------\n\n// ---------------------------------------------------\n// OPERATING\n// Appliance is now Operating \n// ---------------------------------------------------\nif (average >= opPowerMin && operation !== \"Operating\")\n    {\n    context.set(\"operation\", \"Operating\") ;\n    }\n// ---------------------------------------------------\n\n// ---------------------------------------------------\n// WAITING\n// Appliance is \"Waiting\" (i.e. is on or just up to temp)\n// If has just switched to Waiting and was prevously \"Operating\"\n// the it is \"UpToTemperature\"\n// ---------------------------------------------------\nif (average >= standby && average <= opPowerMin)\n    {\n    if (operation === \"Operating\" && applianceUpToTemperature != \"Yes\" ) \n        {\n        context.set(\"operation\", \"UpToTemperature\") ;\n        context.set(\"applianceUpToTemperature\", \"Yes\") ;\n        }\n    else \n        {\n        context.set(\"operation\", \"Waiting\") ;   \n        }\n    }\n// ---------------------------------------------------\n\n// ---------------------------------------------------\n// POWER CALCULATION\n// Calculate power and cost and put into array.\n// Only do this when cycle is occurring (Operating, OR Waiting).\n// Note this method doesn't capture EVERY data point - \n// eg 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\") || (operation === \"Waiting\")  ) \n    {\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        {\n        price = tariff.costDay ;         // Apply day tariff\n        }\n    if ( hour < tariff.start || hour >= tariff.end )\n        {\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// ---------------------------------------------------\n// FINISHED\n// Appliance was in Operating cycle OR Waiting, \n// but now has now finished (i.e. switched off or into Standby)\n// ---------------------------------------------------\n//if ((average < standby && operation === \"Operating\")\n//    || (average < standby && operation === \"Waiting\")  ) \n\nif (average <= standby && ( operation === \"Operating\" || operation === \"Waiting\"))\n    {\n    context.set(\"operation\", \"Finished\") ;          // we are Finished\n    context.set(\"applianceUpToTemperature\", \"No\") ; // reset this for next operation\n    currentCycle.cycleTimeStop = dateS ;            // log the end time\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    currentCycle.totalCyclePowerFormatted = sumOfPower ;\n    \n    flow.set(\"currentApplianceCycle\",currentCycle);  // Store in flow variable\n}\n\n// ---------------------------------------------------\n// REMINDER\n// Appliance is in Operating cycle OR Waiting, \n// and is still operating (e.e above standby power)\n// Send reminder (to second output asynchrously)\n// ---------------------------------------------------\n\nif (average >= standby && ( operation === \"Operating\" || operation === \"Waiting\"))\n    {\n    //  Calculate how long has passed in minutes from cycle start\n    var msg2 ;\n    var diffMilliseconds = (date - currentCycle.cycleTimeStart * 1000) ; // milliseconds between now & currentCycleStart\n    var diffMinsFromStart = Math.round(((diffMilliseconds % 86400000) % 3600000) / 60000) ; // minutes between now & currentCycleStart\n    if ( diffMinsFromStart > 0 && diffMinsFromStart % reminderFrequency == 0 )  // use modulus to see if a reminder is due\n        {\n        if ( reminderDue )\n            {\n            msg2 = {payload: \"A reminder, the\" + applianceName \n                + \" has been on for \" + diffMinsFromStart \n                + \" Minutes.\"} ;\n            node.send([null, msg2]) ;\n            reminderDue = false ;\n            }  \n        }\n        else  \n        {\n        reminderDue = true ;\n        }\n    context.set(\"reminderDue\", reminderDue) ;  // set to node context for next round\n    }\n    \n// ---------------------------------------------------\n// Output debug stuff on each loop\n// ---------------------------------------------------\nif (flow.get(\"debugFlow\") === true)\n    {\n    flow.set(\"debugAverage\",average) ;\n    flow.set(\"debugHours\",hour) ;\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    flow.set(\"debugReminderMins\",diffMinsFromStart) ;\n    \n    flow.set(\"debugRemindNow\",reminderDue) ;\n    }\n\nmsg.payload = context.get(\"operation\") ;\n//return [msg,msg2];\nreturn [msg,null];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":850,"y":320,"wires":[["07facb7be37eb16b","c02a52a199e619e8"],["348344baec71b7b3"]]},{"id":"cb6f03b66278c507","type":"debug","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug: Cycle Power Array","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"$flowContext('debugCyclePowerArray')\t","targetType":"jsonata","statusVal":"$flowContext('debugCyclePowerArray')\t","statusType":"auto","x":520,"y":1000,"wires":[]},{"id":"348344baec71b7b3","type":"link out","z":"aa69d6219a52c4d6","name":"Oven Reminder Out","mode":"link","links":["4f97afa041176700","d8d2d40ba5535537"],"x":985,"y":400,"wires":[]},{"id":"6da7350026aa133a","type":"link in","z":"aa69d6219a52c4d6","g":"0b34903c8e63b79d","name":"Debug In","links":["c02a52a199e619e8"],"x":295,"y":800,"wires":[["5741033e44de8bed","6ce923a42f474217","ddaed4f024da5b26","71688b9d2a860ae0","ba0ee44279504f29","b8e4a4108b58c5fb","f2acdfe5244354f5","cb6f03b66278c507","a9a87bc5b7df1bca"]]},{"id":"c02a52a199e619e8","type":"link out","z":"aa69d6219a52c4d6","name":"Main Operation Debug Out","mode":"link","links":["6da7350026aa133a"],"x":995,"y":260,"wires":[]},{"id":"43524c3af6134ffe","type":"function","z":"aa69d6219a52c4d6","name":"Random Msg","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":"","libs":[],"x":850,"y":620,"wires":[["ec9bb17646afd295"]]},{"id":"15a57bd93e503ac1","type":"play audio","z":"aa69d6219a52c4d6","g":"d41e169c3fafbcb0","name":"","voice":"36","x":1450,"y":860,"wires":[]},{"id":"a3798d3c47f70b68","type":"inject","z":"aa69d6219a52c4d6","g":"d41e169c3fafbcb0","name":"Test Notification","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Test","payloadType":"str","x":1420,"y":800,"wires":[["15a57bd93e503ac1"]]},{"id":"f91c02ec71070e1c","type":"comment","z":"aa69d6219a52c4d6","g":"d41e169c3fafbcb0","name":"Local Browser Announce","info":"","x":1190,"y":800,"wires":[]},{"id":"94ddb30f1a9e2ef1","type":"subflow:19f54c3b.bb52b4","z":"aa69d6219a52c4d6","g":"d41e169c3fafbcb0","name":"","x":1250,"y":860,"wires":[["15a57bd93e503ac1"]]},{"id":"d8d2d40ba5535537","type":"link in","z":"aa69d6219a52c4d6","g":"d41e169c3fafbcb0","name":"Appliance Local Browser Announce","links":["348344baec71b7b3","ec9bb17646afd295"],"x":1095,"y":860,"wires":[["94ddb30f1a9e2ef1","2f3f13ccc12f6973"]]},{"id":"2f3f13ccc12f6973","type":"debug","z":"aa69d6219a52c4d6","g":"d41e169c3fafbcb0","name":"","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1260,"y":900,"wires":[]},{"id":"c3735a8b230d334c","type":"comment","z":"aa69d6219a52c4d6","g":"272dc020b5c61328","name":"Touchscreen Speaker Announce","info":"","x":1210,"y":600,"wires":[]},{"id":"8c5d9b28bcc48e49","type":"subflow:19f54c3b.bb52b4","z":"aa69d6219a52c4d6","g":"272dc020b5c61328","name":"","x":1250,"y":660,"wires":[["d3c79c4e60e0bc29"]]},{"id":"d3c79c4e60e0bc29","type":"api-call-service","z":"aa69d6219a52c4d6","g":"272dc020b5c61328","name":"Google say","server":"194abd6174f64b4a","version":3,"debugenabled":false,"service_domain":"tts","service":"google_say","entityId":"media_player.mpd","data":"{\"entity_id\":\"media_player.mpd\",\"message\":\"{{payload}}\",\"language\":\"en\"}","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1470,"y":660,"wires":[[]]},{"id":"d14020bf21a7aced","type":"inject","z":"aa69d6219a52c4d6","g":"272dc020b5c61328","name":"Test Speak \"Hello World\"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello World","payloadType":"str","x":1490,"y":600,"wires":[["d3c79c4e60e0bc29"]]},{"id":"4f97afa041176700","type":"link in","z":"aa69d6219a52c4d6","g":"272dc020b5c61328","name":"Appliance Touchscreen Speaker Announce","links":["348344baec71b7b3","82804458c4c01c96","ec9bb17646afd295"],"x":1095,"y":660,"wires":[["8c5d9b28bcc48e49","0d5408d4dcfb62c1"]]},{"id":"0d5408d4dcfb62c1","type":"debug","z":"aa69d6219a52c4d6","g":"272dc020b5c61328","name":"","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1260,"y":700,"wires":[]},{"id":"c124edfdb44faea1","type":"comment","z":"aa69d6219a52c4d6","g":"3f27ad3e4ea74f1d","name":"MQTT Feed Notification","info":"","x":1180,"y":1000,"wires":[]},{"id":"403ab9925b217b6d","type":"inject","z":"aa69d6219a52c4d6","g":"3f27ad3e4ea74f1d","name":"Test Notification","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1420,"y":1000,"wires":[["96b08f3f098196e9"]]},{"id":"bf4334572404760b","type":"mqtt out","z":"aa69d6219a52c4d6","g":"3f27ad3e4ea74f1d","name":"","topic":"viewroad-status/activityfeed/ovencomplete","qos":"","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"e28b763a.77bd98","x":1700,"y":1060,"wires":[]},{"id":"ef8f3ea21d61bd24","type":"link in","z":"aa69d6219a52c4d6","g":"3f27ad3e4ea74f1d","name":"Appliance MQTT Report","links":["23808a29ed20c018"],"x":1095,"y":1060,"wires":[["96b08f3f098196e9"]]},{"id":"d0465695d7ab7bf8","type":"moment","z":"aa69d6219a52c4d6","g":"3f27ad3e4ea74f1d","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":1430,"y":1060,"wires":[["bf4334572404760b"]]},{"id":"96b08f3f098196e9","type":"change","z":"aa69d6219a52c4d6","g":"3f27ad3e4ea74f1d","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":1240,"y":1060,"wires":[["d0465695d7ab7bf8"]]},{"id":"e28b763a.77bd98","type":"mqtt-broker","name":"mqtt","broker":"192.168.1.257","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":""}]



Last edited by ZorrUno
Mon, 27 Jun 2022 01:43 UTC [diff]


--
CategoryHomeAutomation
CategoryNodered
CategoryMQTT
CategoryHomeAssistant
CategoryESPHome

Iconic One Theme | Powered by Wordpress