Skip to content

EV charge monitoring

nygma2004 edited this page Mar 12, 2023 · 3 revisions

This is a longer work in progress project where I am monitoring the charging of my EV with Node-Red:

  • My EV has a single phase AC charger and I monitor the electricity usage using an Eastron SDM120 energy monitor
  • I built a ESP8266 project to convert the Modbus data to MQTT which is processed in Node-Red: https://github.com/nygma2004/sdm1202mqtt
  • Flow detects when the charging starts and stops (by monitoring the charge current)
  • Calculates the charge time and the used kWh energy
  • Accumulates the data for daily statistics
  • Stores data in Influx DB bucket

I share the various videos on this step below:

Step 1: SDM120 energy monitor to MQTT gateway

A video on the PCB and ESP project to build a MQTT gateway for a single phase energy meter.

SDM120 MQTT Gateway

Step 2: Physical build

This is not related to Node-Red, but in case you are interested in the physical build.

Charging station build

Step 3: Starting the Node-Red flow

This is a longer format video where I recorded the process when the entire logic started.

Starting the Node-Red flow

Step 4: Flow refinements

Fixing earlier mistakes and refining the flow.

Flow refinements

Step 5: Daily statistics

Adding daily statistics, so I can see how long the car was charging daily, and the electricity consumption for that day.

Daily statistics

The Node-Red flow

And there is the published flow as explained in the videos above:

[
    {
        "id": "a05fd98639604605",
        "type": "tab",
        "label": "EV",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "48b964b93c7a3bf3",
        "type": "comment",
        "z": "a05fd98639604605",
        "name": "Monitor energy consumption",
        "info": "",
        "x": 140,
        "y": 40,
        "wires": []
    },
    {
        "id": "95ab49ab7f68eb35",
        "type": "mqtt in",
        "z": "a05fd98639604605",
        "name": "",
        "topic": "carcharger/data",
        "qos": "0",
        "datatype": "auto-detect",
        "broker": "cea5258a.b34038",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 120,
        "y": 100,
        "wires": [
            [
                "ff074d005878a777",
                "07a85e1da2ae5051",
                "da684f4a493c7449",
                "d5695bc1e633148f"
            ]
        ]
    },
    {
        "id": "ff074d005878a777",
        "type": "rbe",
        "z": "a05fd98639604605",
        "name": "Changes in Power",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": false,
        "property": "payload",
        "topi": "topic",
        "x": 450,
        "y": 100,
        "wires": [
            [
                "94ae1a728c4b7abb"
            ]
        ]
    },
    {
        "id": "94ae1a728c4b7abb",
        "type": "delay",
        "z": "a05fd98639604605",
        "name": "",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "minute",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "allowrate": false,
        "outputs": 1,
        "x": 670,
        "y": 100,
        "wires": [
            [
                "b0a691f37a923d80"
            ]
        ]
    },
    {
        "id": "b0a691f37a923d80",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Convert to Influx",
        "func": "\nlet record = {\n    \"measurement\": \"sensors\",\n    \"tags\": {\n        \"device\": \"carcharger\"\n    },\n    \"fields\": {\n        \"voltage\": msg.payload.voltage,\n        \"current\": msg.payload.current,\n        \"activepower\": msg.payload.activepower,\n        \"powerfactor\": msg.payload.powerfactor,\n        \"frequency\": msg.payload.frequency,\n        \"totalactiveenergy\": msg.payload.totalactiveenergy\n    }\n};\n\n\nmsg.payload = [record];\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 880,
        "y": 100,
        "wires": [
            [
                "b452211c52f94466",
                "7ef13bf3d29cf4c0"
            ]
        ]
    },
    {
        "id": "b452211c52f94466",
        "type": "link out",
        "z": "a05fd98639604605",
        "name": "",
        "mode": "link",
        "links": [
            "0d3a8155db4e5123",
            "783d71afcd0cb582"
        ],
        "x": 1035,
        "y": 100,
        "wires": []
    },
    {
        "id": "5ec1a53397d12493",
        "type": "comment",
        "z": "a05fd98639604605",
        "name": "Log data to Influx",
        "info": "",
        "x": 440,
        "y": 40,
        "wires": []
    },
    {
        "id": "07a85e1da2ae5051",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Monitor charging state",
        "func": "let powerthreshold = 10;\n\nfunction padTo2Digits(num) {\n    return num.toString().padStart(2, '0');\n}\n\nfunction convertMsToTime(milliseconds) {\n    let seconds = Math.floor(milliseconds / 1000);\n    let minutes = Math.floor(seconds / 60);\n    let hours = Math.floor(minutes / 60);\n\n    seconds = seconds % 60;\n    minutes = minutes % 60;\n\n    // πŸ‘‡οΈ If you don't want to roll hours over, e.g. 24 to 00\n    // πŸ‘‡οΈ comment (or remove) the line below\n    // commenting next line gets you `24:00:00` instead of `00:00:00`\n    // or `36:15:31` instead of `12:15:31`, etc.\n    // hours = hours % 24;\n\n    return padTo2Digits(hours)+\":\"+padTo2Digits(minutes)+\":\"+padTo2Digits(seconds);\n}\n\nlet lastenergy = context.get(\"lastenergy\") || 0;\nif (msg.payload.totalactiveenergy===-1) {\n    msg.payload.totalactiveenergy = lastenergy;\n} else {\n    context.set(\"lastenergy\", msg.payload.totalactiveenergy);\n}\n\nlet chargestate = flow.get(\"chargestate\");\nif (chargestate===undefined) {\n    chargestate = { \"charging\": false};\n    flow.set(\"chargestate\", chargestate);\n}\n\nif ((!chargestate.charging) && (msg.payload.activepower >= powerthreshold)) {\n    // charging starts\n    chargestate = {};\n    chargestate.charging = true;\n    chargestate.starttime = new Date().getTime();\n    chargestate.startenergy = msg.payload.totalactiveenergy;\n    node.status({fill:\"green\",shape:\"ring\",text:\"Charging\"});\n    flow.set(\"chargestate\", chargestate);\n    return [{\"topic\": \"start\", \"payload\": chargestate}];\n}\n\nif ((chargestate.charging) && (msg.payload.activepower < powerthreshold)) {\n    // charging stopped\n    chargestate.charging = false;\n    chargestate.endtime = new Date().getTime();\n    chargestate.endenergy = msg.payload.totalactiveenergy;\n    chargestate.energyused = chargestate.endenergy - chargestate.startenergy;\n    chargestate.chargetime_msec = chargestate.endtime - chargestate.starttime;\n    chargestate.chargetime_text = convertMsToTime(chargestate.chargetime_msec);\n    flow.set(\"chargestate\", chargestate);\n    node.status({ fill: \"grey\", shape: \"ring\", text: \"Not charging, last charge: \" + chargestate.chargetime_text + \", \" + chargestate.energyused.toFixed(2)+ \" kWh\" });\n    return [{ \"topic\": \"stop\", \"payload\": chargestate }];\n} \n\nif ((chargestate.charging) && (msg.payload.activepower > powerthreshold)) {\n    // charging in progress\n    chargestate.chargetime_msec = new Date().getTime() - chargestate.starttime;\n    chargestate.chargetime_text = convertMsToTime(chargestate.chargetime_msec);\n    chargestate.energyused = msg.payload.totalactiveenergy - chargestate.startenergy;\n    flow.set(\"chargestate\", chargestate);\n    node.status({ fill: \"green\", shape: \"ring\", text: \"Charging: \" + chargestate.chargetime_text + \", \" + msg.payload.current + \" A, \" + chargestate.energyused.toFixed(2) + \" kWh\" });\n    return [{ \"topic\": \"charging\", \"payload\": chargestate }];\n} \n\n\n\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 460,
        "y": 240,
        "wires": [
            [
                "c7541ebec4b8a4b7",
                "4733fa211b46536b"
            ]
        ]
    },
    {
        "id": "b406a7931f92d1e0",
        "type": "comment",
        "z": "a05fd98639604605",
        "name": "Charge state",
        "info": "",
        "x": 430,
        "y": 160,
        "wires": []
    },
    {
        "id": "6ea0e5ed8ccbc622",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Send charge report",
        "func": "msg.payload = { \"service\": 23, \"type\": \"message\", \"content\": \"πŸš— Charging completed: \" + msg.payload.chargetime_text + \", energy used: \"+msg.payload.energyused.toFixed(2)+\" kWh\"};\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 890,
        "y": 280,
        "wires": [
            [
                "e2a7460baed461fb"
            ]
        ]
    },
    {
        "id": "e2a7460baed461fb",
        "type": "link out",
        "z": "a05fd98639604605",
        "name": "",
        "mode": "link",
        "links": [
            "86deb2f58b76aa52"
        ],
        "x": 1065,
        "y": 280,
        "wires": []
    },
    {
        "id": "c7541ebec4b8a4b7",
        "type": "switch",
        "z": "a05fd98639604605",
        "name": "Charging?",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "start",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "stop",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "charging",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 670,
        "y": 220,
        "wires": [
            [
                "6cb45b7890af32ce"
            ],
            [
                "6ea0e5ed8ccbc622",
                "7d7e2d83c4cbeb05"
            ],
            [
                "accc6b4d95f0bc1a"
            ]
        ]
    },
    {
        "id": "6cb45b7890af32ce",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Charging started",
        "func": "msg.payload = { \"service\": 23, \"type\": \"message\", \"content\": \"πŸš— Charging started\"};\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 890,
        "y": 220,
        "wires": [
            [
                "e2a7460baed461fb"
            ]
        ]
    },
    {
        "id": "da684f4a493c7449",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Online State (New)",
        "func": "let devicename = \"EVCharger\"; // Device name used for context variable\nlet system_id = 49; // System id number for diagnostic update\nlet online_threshold = 10; // Seconds between updates under which the device is considered online\nlet offline_threshold = 60; // Seconds between updates above which the device is considered offline\nlet low_battery = 15;      // Low battery warning below this value\nlet battery = -1;\n\nlet devicestatus = global.get(\"devicestatus\");\nif (devicestatus === undefined) {\n    devicestatus = {};\n}\nif (devicestatus[devicename] === undefined) {\n    devicestatus[devicename] = { \"systemid\": system_id };\n}\n\nif (msg.topic.indexOf(\"battery\") > -1) {\n    battery = parseInt(msg.payload);\n}\n\n\nlet temp = devicestatus[devicename].lastupdate;\nvar current = new Date().getTime();\nmsg.payload = \"No data\";\nif (msg.topic !== \"timecheck\") {\n    // Do not update the context if it is triggered by the check inject node\n    devicestatus[devicename].lastupdate = current;\n    temp = current;\n}\n\nif (temp !== undefined) {\n    current = current - temp;\n    current = Math.floor(current / 1000);\n    var minute = Math.floor(current / 60);\n    var hour = Math.floor(minute / 60);\n    var day = Math.floor(hour / 24);\n    if (current > 24 * 60 * 60) {\n        msg.payload = \"Last update \" + day + \" days, \" + hour % 24 + \" hours, \" + minute % 60 + \" minutes, \" + current % 60 + \" seconds ago\";\n    } else if (current > 60 * 60) {\n        msg.payload = \"Last update \" + hour % 24 + \" hours, \" + minute % 60 + \" minutes, \" + current % 60 + \" seconds ago\";\n    } else if (current > 60) {\n        msg.payload = \"Last update \" + minute % 60 + \" minutes, \" + current % 60 + \" seconds ago\";\n    } else {\n        msg.payload = \"Last update \" + current % 60 + \" seconds ago\";\n    }\n    devicestatus[devicename].lastupdatetext = msg.payload;\n\n    if (devicestatus[devicename].state !== 1) {\n        if (current < online_threshold) {\n            msg.payload = devicename+  \" is now online\";\n            msg.system = system_id; // System id, use 1 for Dummy\n            msg.state = 1; // specify if the message is to change system status\n            msg.severity = 0; // 0: information, 1: warning, 2: error\n            //msg.email = true; // if separate email should be sent\n            //msg.emailtext = \"\"; this a long text which goes into the email  \n            msg.warning = true;\n            devicestatus[devicename].state = msg.state;\n        }\n    } else {\n        if (current > offline_threshold) {\n            msg.payload = devicename +  \" is not transmitting\";\n            msg.system = system_id; // System id, use 1 for Dummy\n            msg.state = 99; // specify if the message is to change system status\n            msg.severity = 0; // 0: information, 1: warning, 2: error\n            //msg.email = true; // if separate email should be sent\n            //msg.emailtext = \"\"; this a long text which goes into the email            \n            msg.warning = true;\n            devicestatus[devicename].state = msg.state;\n        }\n        if ((devicestatus[devicename].state === 1) && (battery < low_battery) && (battery !== -1)) {\n            msg.payload = devicename + \"'s battery is low\";\n            msg.system = system_id; // System id, use 1 for Dummy\n            msg.state = 60; // specify if the message is to change system status\n            msg.severity = 2; // 0: information, 1: warning, 2: error\n            //msg.email = true; // if separate email should be sent\n            //msg.emailtext = \"\"; this a long text which goes into the email            \n            msg.warning = true;\n            devicestatus[devicename].state = msg.state;\n        }\n        if ((devicestatus[devicename].state === 60) && (battery > low_battery) && (battery !== -1)) {\n            msg.payload = devicename + \" has a new battery\";\n            msg.system = system_id; // System id, use 1 for Dummy\n            msg.state = 1; // specify if the message is to change system status\n            msg.severity = 0; // 0: information, 1: warning, 2: error\n            //msg.email = true; // if separate email should be sent\n            //msg.emailtext = \"\"; this a long text which goes into the email            \n            msg.warning = true;\n            devicestatus[devicename].state = msg.state;\n        }\n    }\n\n\n}\n\nglobal.set(\"devicestatus\", devicestatus);\nnode.status({ fill: \"blue\", shape: \"ring\", text: msg.payload });\n\nreturn msg;\n\n\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 410,
        "y": 440,
        "wires": [
            [
                "538097c7af40e7ab"
            ]
        ]
    },
    {
        "id": "785fc28ca8c4b348",
        "type": "link out",
        "z": "a05fd98639604605",
        "name": "",
        "links": [
            "13e089a7.73cb46"
        ],
        "x": 795,
        "y": 440,
        "wires": []
    },
    {
        "id": "538097c7af40e7ab",
        "type": "switch",
        "z": "a05fd98639604605",
        "name": "Update diag?",
        "property": "warning",
        "propertyType": "msg",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "outputs": 1,
        "x": 610,
        "y": 440,
        "wires": [
            [
                "785fc28ca8c4b348",
                "321ab4b7dd312967"
            ]
        ]
    },
    {
        "id": "321ab4b7dd312967",
        "type": "switch",
        "z": "a05fd98639604605",
        "name": "Offline?",
        "property": "state",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "99",
                "vt": "num"
            },
            {
                "t": "eq",
                "v": "1",
                "vt": "num"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 440,
        "y": 360,
        "wires": [
            [
                "b13792f5fd99aca2",
                "b32250bdb77d3194"
            ],
            [
                "bd7de38ab75df6e1"
            ]
        ]
    },
    {
        "id": "b13792f5fd99aca2",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Dummy message",
        "func": "msg.payload = { \"voltage\": 0, \"current\": 0, \"activepower\": 0, \"frequency\": 0, \"powerfactor\": 0, \"totalactiveenergy\": -1};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 610,
        "y": 320,
        "wires": [
            [
                "07a85e1da2ae5051"
            ]
        ]
    },
    {
        "id": "b32250bdb77d3194",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Power off",
        "func": "msg.payload = { \"service\": 23, \"type\": \"message\", \"content\": \"πŸš— Off peak tariff ended\"};\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 840,
        "y": 340,
        "wires": [
            [
                "e2a7460baed461fb"
            ]
        ]
    },
    {
        "id": "bd7de38ab75df6e1",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Power on",
        "func": "msg.payload = { \"service\": 23, \"type\": \"message\", \"content\": \"πŸš— Off peak tariff started\"};\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 840,
        "y": 380,
        "wires": [
            [
                "e2a7460baed461fb"
            ]
        ]
    },
    {
        "id": "a776c0f64b849620",
        "type": "inject",
        "z": "a05fd98639604605",
        "name": "Update",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "2",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "timecheck",
        "payload": "",
        "payloadType": "date",
        "x": 120,
        "y": 500,
        "wires": [
            [
                "da684f4a493c7449"
            ]
        ]
    },
    {
        "id": "7ef13bf3d29cf4c0",
        "type": "debug",
        "z": "a05fd98639604605",
        "name": "Influx playload",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1110,
        "y": 160,
        "wires": []
    },
    {
        "id": "accc6b4d95f0bc1a",
        "type": "debug",
        "z": "a05fd98639604605",
        "name": "Charge update",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1020,
        "y": 520,
        "wires": []
    },
    {
        "id": "4733fa211b46536b",
        "type": "debug",
        "z": "a05fd98639604605",
        "name": "debug 12",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 650,
        "y": 160,
        "wires": []
    },
    {
        "id": "d5695bc1e633148f",
        "type": "debug",
        "z": "a05fd98639604605",
        "name": "debug 13",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 210,
        "y": 260,
        "wires": []
    },
    {
        "id": "7d7e2d83c4cbeb05",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Daily stats",
        "func": "function padTo2Digits(num) {\n    return num.toString().padStart(2, '0');\n}\n\nfunction convertMsToTime(milliseconds) {\n    let seconds = Math.floor(milliseconds / 1000);\n    let minutes = Math.floor(seconds / 60);\n    let hours = Math.floor(minutes / 60);\n\n    seconds = seconds % 60;\n    minutes = minutes % 60;\n\n    // πŸ‘‡οΈ If you don't want to roll hours over, e.g. 24 to 00\n    // πŸ‘‡οΈ comment (or remove) the line below\n    // commenting next line gets you `24:00:00` instead of `00:00:00`\n    // or `36:15:31` instead of `12:15:31`, etc.\n    // hours = hours % 24;\n\n    return padTo2Digits(hours) + \":\" + padTo2Digits(minutes) + \":\" + padTo2Digits(seconds);\n}\n\nlet ev = flow.get(\"ev\");\nif (ev === undefined) {\n    ev = {};\n    flow.set(\"ev\", ev);\n}\n\nif (msg.topic === \"nextday\") {\n    // reset the counter and copy the values to yesterday\n    delete ev.yesterday;\n    if (ev.today !== undefined) {\n        ev.yesterday = ev.today;\n        delete ev.today;\n        ev.charges = [];\n        flow.set(\"ev\", ev);\n    }\n} else {\n    // accumulate the charge data for the current day\n    if (ev.today === undefined) {\n        // initiate the structure for first use\n        ev.today = {};\n        ev.today.chargetime_msec = 0;\n        ev.today.energyused = 0.0;\n    }\n    ev.today.energyused += msg.payload.energyused;\n    ev.today.chargetime_msec += msg.payload.chargetime_msec\n    ev.today.chargetime_text = convertMsToTime(ev.today.chargetime_msec);\n    ev.charges.push(msg.payload);\n    flow.set(\"ev\", ev);\n}\n\nmsg.payload = ev;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1210,
        "y": 400,
        "wires": [
            [
                "c32301e856f92608"
            ]
        ]
    },
    {
        "id": "03f80aaf635667d0",
        "type": "inject",
        "z": "a05fd98639604605",
        "name": "Reset stats",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "00 08 * * *",
        "once": false,
        "onceDelay": 0.1,
        "topic": "nextday",
        "payload": "",
        "payloadType": "date",
        "x": 1020,
        "y": 440,
        "wires": [
            [
                "7d7e2d83c4cbeb05"
            ]
        ]
    },
    {
        "id": "0ed6db05a301a58a",
        "type": "link out",
        "z": "a05fd98639604605",
        "name": "",
        "mode": "link",
        "links": [
            "0d3a8155db4e5123",
            "783d71afcd0cb582"
        ],
        "x": 1765,
        "y": 400,
        "wires": []
    },
    {
        "id": "18122d80545ff87b",
        "type": "function",
        "z": "a05fd98639604605",
        "name": "Convert to Influx",
        "func": "if (msg.payload.yesterday !== undefined) {\n    let record = {\n        \"measurement\": \"sensors\",\n        \"tags\": {\n            \"device\": \"ev\"\n        },\n        \"fields\": msg.payload.yesterday\n    };\n\n    msg.payload = [record];\n    return msg;\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1620,
        "y": 400,
        "wires": [
            [
                "0ed6db05a301a58a"
            ]
        ]
    },
    {
        "id": "c32301e856f92608",
        "type": "switch",
        "z": "a05fd98639604605",
        "name": "Daily update?",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "nextday",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 1420,
        "y": 400,
        "wires": [
            [
                "18122d80545ff87b"
            ]
        ]
    },
    {
        "id": "cea5258a.b34038",
        "type": "mqtt-broker",
        "name": "",
        "broker": "192.168.1.80",
        "port": "1883",
        "clientid": "node-red",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    }
]