Node-RED nodes for hubitat

Good question. I've not reached that point of debugging yet in Node Red.

I dont know the answer, but you could use the stoptimer function, that will reset the countdown timer if another message is received.

node-red-contrib-stoptimer

2 Likes

@stephen_nutt

I also do what @mike has indicated, i.e. use the stoptimer node for motion lighting. And it was @mike who pointed out to @waynespringer79 and myself that if the stoptimer node receives a message of "stop" or "STOP", it stops the timer from running until another message is received.

For motion lighting, "stop" can be sent when the motion sensor is active and some other message can be sent when the sensor is inactive. I was really stymied on motion lighting until @mike's help.

2 Likes

You can do something similar with the delay node by sending it a msg.reset=true on "active" but both @mike and @aaiyar are correct in saying that stoptimer is better suited to what you are trying to do.

In my example node, I would use a mytimeout node normally. I was just using this as a way to illustrate my question. Do multiple instances of a sequence run simultaneously or does the 2nd triggering of the sequence stop the initial running of the sequence?

Every time the "trigger" node activates it would start the flow. Multiple activations could have multiple simultaneous sequences/actions occurring. However, depending on the nodes in the flow and how they are configured, the active flows may be halted. This is why rate limit nodes exist (rbe for example). They can prevent unwanted results from multiple triggers occurring in succession.

1 Like

This.

rbe nodes are super useful as @stephack has indicated. Where I can use them, I use flow variables to achieve a similar end point ....

I'm wondering if anyone knows how to display a table in the Node Red dashboard. I used @fblackburn's temperature flow yesterday, but I'd also like to get it onto the dashboard for current values, much like what HE's SuperTile or TileMaster does:

image

I'm thinking I might try and move my dashboard to Node Red.

I have been following this thread for some time now, and I have learned a lot from all of you. Thank you for sharing your experiences and expertise. I have set up Node Red and the Hubitat pallette. So far I have managed to create a few fairly simple flows. But I have now run in to an issue I cannot seem to figure out. I am running Node Red on a Raspberry Pi 4. Yesterday, I had a power outage that shut down my network for about an hour. when everything came back online, all of my devices in active Node Red flows showed a message next to a red box that said "uninitialized." After playing around for a bit, I got the messages to disappear and everything seemed to work fine. I cannot say, though, how the issue managed to resolve itself. Today, I needed to restart my Raspberry Pi. After the reboot, I am seeing the same "uninitialized" message. if I delete the device node and replace it with a new node for the same device, that device seems to come back online in Node Red. That said, there must be some explanation for this problem and some solution better than deleting and reinstalling every device node. I would appreciate any help or input anyone has. Thanks.

Which version are you using? Of Node-RED and node-red-contrib-hubitat?

I want to send text messages to the Hubitat moble app. It is working except that I am seeing html spaces in the text string. "This is a test" is converted into "This%20is%20a%20test". This came up earlier and the issues is that Maker API is sending HTML encoded strings to the devices.

I can get around this with text messages by using periods instead of spaces. That will only get me a minor eye roll from my wife :smiley: but the HTML encoding is breaking SONOs speak test.

I'm hoping that someone has come up with workaround.

Thanks so much! Enjoying the heck out of the node-red support!

EDIT: Found a work around for sending SONOs a text string to speak. It will except strings that have a dash (minus sign) instead of spaces just-like-this.

I am running version 1.0.4 of Node Red and version 0.0.24 of node-red-contrib-hubitat.

A month or so back, someone posted this fantastic cUrl statement to pull a Hubitat backup and store it. Struggling to turn this into a proper node using "Exec" or HttpRequest, anyone got an idea on getting this to run? I thought it would be a fantastic thing to run in the mornings when my mode manager switches to "Daytime"

curl -s http://192.168.0.100/hub/backupDB?fileName=latest -o "/Volumes/Bacukups/Hubitat//_$(date +%Y-%m-%d-%H%M).lzf"

Thanks in advance.

Side note:
Making fantastic progress on getting my rules moved over from RM to NR, ran across this great step by step on getting Alexa to talk

1 Like

Try adding a sequence to every flow where you query the status of a device upon Node-RED startup. My recollection is that I had the same issue you've encountered. I assumed it went away with an update to node-red-contrib-hubitat. However, I also added sequences that query device status along the way - so perhaps that made the change.

I've attached a sample sequence below that you can import and just change the device.

Query at startup
[
    {
        "id": "3ec77471.1e95d4",
        "type": "inject",
        "z": "1eb1e8e3.1c3ca7",
        "name": "15 secs",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "15",
        "x": 120,
        "y": 460,
        "wires": [
            [
                "e0135fdd.83a79",
                "a5922f41.903af8",
                "59ccae9e.c425b8",
                "99c9b443.79a3a"
            ]
        ]
    },
    {
        "id": "99c9b443.79a3a",
        "type": "function",
        "z": "1eb1e8e3.1c3ca7",
        "name": "Switch Status",
        "func": "msg.attribute = \"switch\"\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 336,
        "y": 374,
        "wires": [
            [
                "bccb6285.6a89"
            ]
        ]
    },
    {
        "id": "bccb6285.6a89",
        "type": "hubitat device",
        "z": "1eb1e8e3.1c3ca7",
        "name": "Kitchen Tube",
        "server": "662851c4.3ccad",
        "deviceId": "7",
        "attribute": "switch",
        "sendEvent": true,
        "x": 550,
        "y": 373,
        "wires": [
            [
                "bc1a9e3d.9154"
            ]
        ]
    },
    {
        "id": "bc1a9e3d.9154",
        "type": "change",
        "z": "1eb1e8e3.1c3ca7",
        "name": "save Kitchen Tube status",
        "rules": [
            {
                "t": "set",
                "p": "kt_status",
                "pt": "flow",
                "to": "payload.currentValue",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 796,
        "y": 374,
        "wires": [
            []
        ]
    },
    {
        "id": "662851c4.3ccad",
        "type": "hubitat config",
        "z": "",
        "name": "HubitatS",
        "usetls": false,
        "host": "192.168.1.36",
        "port": "80",
        "token": "",
        "appId": "4489",
        "nodeRedServer": "http://192.168.1.4:1880",
        "webhookPath": "/hubitat/webhook2"
    }
]

@morningz, below is from a post I shared previously. It was the flow I used to keep a daily backup of both my HE hubs. Hopefully it can give you some ideas on how to get yours configured. Of course the ip's and file paths etc would have to be changed. You will also need to have the following nodes installed (if they aren't already)
node-red-contrib-fs-ops
node-red-contrib-string

It maintains a limit of 15 backups before deleting the oldest log. Please also keep in mind that this flow was written for a Windows based version of NR, so the slashes are reversed for the paths if you are running on a Linux box.

My hub firmware backup flow:

[
    {
        "id": "1a25485a.160c68",
        "type": "tab",
        "label": "Daily Hub F/W Backups (1AM & 1:30AM)",
        "disabled": false,
        "info": ""
    },
    {
        "id": "31f80583.137ffa",
        "type": "file",
        "z": "1a25485a.160c68",
        "name": "Save File",
        "filename": "",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 660,
        "y": 40,
        "wires": [
            [
                "fcae276c.cfdc28"
            ]
        ]
    },
    {
        "id": "c916945e.2a3d68",
        "type": "inject",
        "z": "1a25485a.160c68",
        "name": "Dev Hub Daily 4:00AM",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "00 04 * * *",
        "once": false,
        "onceDelay": 0.1,
        "x": 150,
        "y": 40,
        "wires": [
            [
                "67731afb.2979e4"
            ]
        ]
    },
    {
        "id": "67731afb.2979e4",
        "type": "http request",
        "z": "1a25485a.160c68",
        "name": "Get backup",
        "method": "GET",
        "ret": "bin",
        "paytoqs": false,
        "url": "http://192.168.7.221/hub/backupDB?fileName=latest",
        "tls": "",
        "proxy": "",
        "authType": "basic",
        "x": 350,
        "y": 40,
        "wires": [
            [
                "8eadb6f5.01e168"
            ]
        ]
    },
    {
        "id": "835c7307.73939",
        "type": "file",
        "z": "1a25485a.160c68",
        "name": "Save File",
        "filename": "",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 660,
        "y": 240,
        "wires": [
            [
                "37201a0d.a27bd6"
            ]
        ]
    },
    {
        "id": "b83036e9.ace978",
        "type": "inject",
        "z": "1a25485a.160c68",
        "name": "Main Hub Daily 4:30AM",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "30 04 * * *",
        "once": false,
        "onceDelay": 0.1,
        "x": 150,
        "y": 240,
        "wires": [
            [
                "ce651073.497f3"
            ]
        ]
    },
    {
        "id": "ce651073.497f3",
        "type": "http request",
        "z": "1a25485a.160c68",
        "name": "Get backup",
        "method": "GET",
        "ret": "bin",
        "paytoqs": false,
        "url": "http://192.168.7.209/hub/backupDB?fileName=latest",
        "tls": "",
        "proxy": "",
        "authType": "basic",
        "x": 350,
        "y": 240,
        "wires": [
            [
                "c4876440.f33c48"
            ]
        ]
    },
    {
        "id": "8eadb6f5.01e168",
        "type": "string",
        "z": "1a25485a.160c68",
        "name": "Get filename",
        "methods": [
            {
                "name": "strip",
                "params": [
                    {
                        "type": "str",
                        "value": "attachment; filename="
                    }
                ]
            },
            {
                "name": "prepend",
                "params": [
                    {
                        "type": "str",
                        "value": "\\\\nodered\\\\backups\\\\dev\\\\"
                    }
                ]
            }
        ],
        "prop": "headers.content-disposition",
        "propout": "filename",
        "object": "msg",
        "objectout": "msg",
        "x": 510,
        "y": 40,
        "wires": [
            [
                "31f80583.137ffa"
            ]
        ]
    },
    {
        "id": "c4876440.f33c48",
        "type": "string",
        "z": "1a25485a.160c68",
        "name": "Get filename",
        "methods": [
            {
                "name": "strip",
                "params": [
                    {
                        "type": "str",
                        "value": "attachment; filename="
                    }
                ]
            },
            {
                "name": "prepend",
                "params": [
                    {
                        "type": "str",
                        "value": "\\\\nodered\\\\backups\\\\main\\\\"
                    }
                ]
            }
        ],
        "prop": "headers.content-disposition",
        "propout": "filename",
        "object": "msg",
        "objectout": "msg",
        "x": 510,
        "y": 240,
        "wires": [
            [
                "835c7307.73939"
            ]
        ]
    },
    {
        "id": "37201a0d.a27bd6",
        "type": "fs-ops-dir",
        "z": "1a25485a.160c68",
        "name": "# of Backups",
        "path": "\\nodered\\backups\\main\\",
        "pathType": "str",
        "filter": "*",
        "filterType": "str",
        "dir": "files",
        "dirType": "msg",
        "x": 130,
        "y": 320,
        "wires": [
            [
                "f2d91ce9.bb0d5"
            ]
        ]
    },
    {
        "id": "b12fb6f7.a44868",
        "type": "fs-ops-delete",
        "z": "1a25485a.160c68",
        "name": "Del Oldest BckUp",
        "path": "\\nodered\\backups\\main\\",
        "pathType": "str",
        "filename": "files[0]",
        "filenameType": "msg",
        "x": 470,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "f2d91ce9.bb0d5",
        "type": "switch",
        "z": "1a25485a.160c68",
        "name": "15 File Limit",
        "property": "files.length",
        "propertyType": "msg",
        "rules": [
            {
                "t": "gte",
                "v": "15",
                "vt": "num"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 290,
        "y": 320,
        "wires": [
            [
                "b12fb6f7.a44868"
            ]
        ]
    },
    {
        "id": "fcae276c.cfdc28",
        "type": "fs-ops-dir",
        "z": "1a25485a.160c68",
        "name": "# of Backups",
        "path": "\\nodered\\backups\\dev\\",
        "pathType": "str",
        "filter": "*",
        "filterType": "str",
        "dir": "files",
        "dirType": "msg",
        "x": 130,
        "y": 120,
        "wires": [
            [
                "6a93c3f4.90cd5c"
            ]
        ]
    },
    {
        "id": "6fc01da3.081d04",
        "type": "fs-ops-delete",
        "z": "1a25485a.160c68",
        "name": "Del Oldest BckUp",
        "path": "\\nodered\\backups\\dev\\",
        "pathType": "str",
        "filename": "files[0]",
        "filenameType": "msg",
        "x": 470,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "6a93c3f4.90cd5c",
        "type": "switch",
        "z": "1a25485a.160c68",
        "name": "15 File Limit",
        "property": "files.length",
        "propertyType": "msg",
        "rules": [
            {
                "t": "gte",
                "v": "15",
                "vt": "num"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 290,
        "y": 120,
        "wires": [
            [
                "6fc01da3.081d04"
            ]
        ]
    }
]
1 Like

I was never able to figure this out trying to send text to my Google Home Mini. The Mini was speaking the "%20". I hadn't looked at it lately but want to try again because I figured out a way to send my Caller ID info from my Android phone to a Global Variable connector to use on a Dashboard to tell me who called if I am not near my phone but it also inserts the "%20". Obviously, like you, I can decipher the info but it is definitely a sloppy way to to it and definitely not good enough for WAF.

I created a new feature request towards Hubitat for this, let's see if we can get some traction for supporting this....

4 Likes

Here is a different way of doing it with the "node-red-contrib-cheerio-function" plugin. The difference in this version of the flow is that it doesn't create a new backup, it downloads the last backup that was created by Hubitat.

You can set the destination path in the "Settings" node

Downlaod Backup Flow
[
    {
        "id": "45424774.052448",
        "type": "tab",
        "label": "Download Last Hubitat Backup",
        "disabled": false,
        "info": ""
    },
    {
        "id": "dc14a65c.3701d8",
        "type": "inject",
        "z": "45424774.052448",
        "name": "get data",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 111,
        "y": 135.9999976158142,
        "wires": [
            [
                "681a2dc.8dc30d4"
            ]
        ]
    },
    {
        "id": "681a2dc.8dc30d4",
        "type": "http request",
        "z": "45424774.052448",
        "name": "",
        "method": "GET",
        "ret": "txt",
        "paytoqs": false,
        "url": "http://192.168.10.153/hub/backup",
        "tls": "",
        "proxy": "",
        "x": 275.0000114440918,
        "y": 136.00000190734863,
        "wires": [
            [
                "5ffb2d8.1b69dd4"
            ]
        ]
    },
    {
        "id": "5ffb2d8.1b69dd4",
        "type": "cheerio-function",
        "z": "45424774.052448",
        "name": "parse html cheerio",
        "func": "const tableSelector = '#backup-table'\n\n//const cheerio = global.get('cheerio')\n//const $ = cheerio.load(msg.payload)\nconst options = {\n    rowForHeadings: 0,  // extract th cells from this row for column headings (zero-based)\n    ignoreHeadingRow: true, // Don't tread the heading row as data\n    ignoreRows: [],\n}\nconst rowEntries = []\nconst columnHeadings = []\n\n\n$(tableSelector).each(function(i, table) {\n    node.log(\"Something happened\");\n\n    var trs = $(table).find('tr')\n    \n    // Set up the column heading names\n    getColHeadings( $(trs[options.rowForHeadings]) )\n\n    // Process rows for data\n    $(table).find('tr').each(processRow)\n})\n\nif (rowEntries.length === 0)\n    return null;\n\nmsg.payload = {\n    fileName: rowEntries[rowEntries.length-1].Name\n}\n\nreturn msg\n\nfunction getColHeadings(headingRow) {\n    const alreadySeen = {}\n    \n    $(headingRow).find('th').each(function(j, cell) {\n        let tr = $(cell).text().trim()\n        \n        if ( alreadySeen[tr] ) {\n            let suffix = ++alreadySeen[tr]\n            tr = `${tr}_${suffix}`\n        } else {\n            alreadySeen[tr] = 1\n        }\n        \n        columnHeadings.push(tr)\n    })\n}\n\nfunction processRow(i, row) {\n    const rowJson = {}\n    \n    if ( options.ignoreHeadingRow && i === options.rowForHeadings ) return\n    // TODO: Process options.ignoreRows\n    \n    $(row).find('td').each(function(j, cell) {\n        rowJson[ columnHeadings[j] ] = $(cell).text().trim()\n    })\n    \n    // Skip blank rows\n    if (JSON.stringify(rowJson) !== '{}') rowEntries.push(rowJson)\n}\n",
        "outputs": 1,
        "noerr": 0,
        "x": 462.5,
        "y": 137,
        "wires": [
            [
                "9a78be62.d051d"
            ]
        ]
    },
    {
        "id": "9a78be62.d051d",
        "type": "http request",
        "z": "45424774.052448",
        "name": "",
        "method": "GET",
        "ret": "bin",
        "paytoqs": true,
        "url": "http://192.168.10.153/hub/backupDB?",
        "tls": "",
        "proxy": "",
        "authType": "basic",
        "x": 268.5,
        "y": 259,
        "wires": [
            [
                "8e274a2.c00ccb8"
            ]
        ]
    },
    {
        "id": "a81ffa29.327a08",
        "type": "file",
        "z": "45424774.052448",
        "name": "",
        "filename": "",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "binary",
        "x": 571.5,
        "y": 258,
        "wires": [
            []
        ]
    },
    {
        "id": "8e274a2.c00ccb8",
        "type": "string",
        "z": "45424774.052448",
        "name": "Settings",
        "methods": [
            {
                "name": "strip",
                "params": [
                    {
                        "type": "str",
                        "value": "attachment; filename="
                    }
                ]
            },
            {
                "name": "prepend",
                "params": [
                    {
                        "type": "str",
                        "value": "/var/log/"
                    }
                ]
            }
        ],
        "prop": "headers.content-disposition",
        "propout": "filename",
        "object": "msg",
        "objectout": "msg",
        "x": 434.5,
        "y": 257,
        "wires": [
            [
                "a81ffa29.327a08"
            ]
        ]
    }
]
2 Likes

Hi @fblackburn, I've imported the flow into my node-red and I'm starting to get error/warning

not sure what I'm missing. any idea? Cheers

image

You need to install node-red-dashboard.

1 Like