Node-Red Flow Samples/Sharing

Ok, I will try that too as it currently isnt reporting its current state properly. Unfortunately I will need the property window for almost everything as I dont know if I am doing it properly at this point.

Im now working on the messages for garage and locks. Any way you can share the property windows for some of the simple nodes?

I don't use pushover so @aaiyar or someone with will have to chime in..

Change node is fairly easy.. also note the "help" pane to the right of the properties page, this usually has a lot of good info - HE Nodes is well documented!

Note: I'm NOT suggesting you RTFM but it is a handy reference for things :wink:

Note2: The other thing you can do is use the Email node for alerts too.

1 Like

I did some reading. But it is becoming clear that I did not read enough. :\

1 Like

I think you are on the right track! It takes a little getting used to - the whole message object passing thing. Once you get the hang of it it really is straightforward and very powerful.

1 Like

Well this is literally my first weekend tackling this. I have a lot of flows to create to be able to leave WebCoRE. By the time I am done, Ill be a little more comfortable I am thinking.

In the other giant Node Red thread, I had discussed a challenge with separating the case of a level reading from a generic RGBW bulb between a case where the color (hue and/or saturation and optionally level) is being changed and where only the brightness (level) is being changed.

I have now built a solution to this challenge that I want to share here. I have tested it, but it is possible that there is some corner case that I did not reach in my design and testing. If you choose to use it, feel free to reach out if you run into a problem and I will try to help or update the flow to address your problem.

[{"id":"d4f615bad7f8c4d9","type":"group","z":"5c9e82eadde3cbc4","name":"Flow that takes inputs from Hue, Saturation and Level and figures out if it is a color change or only a brightness (Level) change","style":{"stroke":"#000000","fill":"#e3f3d3","label":true,"color":"#000000"},"nodes":["5e85427461b9099f","386250a07242f362","bd9646b4dcad07d6","a195a6014dce2014","097a5670fbf70301","d26243490fe07b6c"],"x":158,"y":2733,"w":1048,"h":254},{"id":"5e85427461b9099f","type":"rbe","z":"5c9e82eadde3cbc4","g":"d4f615bad7f8c4d9","name":"Filter out duplicate\\n attribute readings","func":"rbe","gap":"","start":"","inout":"out","septopics":false,"property":"payload.attributes","topi":"topic","x":730,"y":2860,"wires":[["bd9646b4dcad07d6"]]},{"id":"386250a07242f362","type":"http request","z":"5c9e82eadde3cbc4","g":"d4f615bad7f8c4d9","name":"Get Full Details\\n from Maker interface\\n with http request node","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://192.168.86.116:80/apps/api/103/devices/139/setLevel/52?access_token=111111111111111111111115b74f038850f","tls":"","persist":false,"proxy":"","authType":"","x":510,"y":2860,"wires":[["5e85427461b9099f"]]},{"id":"bd9646b4dcad07d6","type":"function","z":"5c9e82eadde3cbc4","g":"d4f615bad7f8c4d9","name":"0026 Decide if it is\\n HSL or Brightness","func":"//0026 Decide if it is HSL or Brightness\n/* Very important note.\n    For the original creators need, the HSL output sends the hue and saturation that comes from the Hubitat, but ignores the Level.\n    This is because when you use the Hubitat color picker, then you will almost inevitably change the level.\n    The level however is also the brightness of the bulb.\n    So, I use the previously set brightness (level) so the color of the light changes, but the brightness doesn't change.\n    If you want to use the level sent from Hubitat when coming from the color picker, then you want to alter the line:\n        updateHistory(\"VirtualDevices\", devID, [\"Hue\", \"Saturation\", \"Level\"], [newHue, newSaturation, oldLevel])\n    to\n        updateHistory(\"VirtualDevices\", devID, [\"Hue\", \"Saturation\", \"Level\"], [newHue, newSaturation, newLevel])\n*/\n//standard debugging capability start\nlet showDebug = false;\nlet showDebug2 = false;\nlet showDebug3 = false;\nshowDebug ? node.warn(\"DesiredDebugOutput\") : null;\n//standard debugging capability end\n\nshowDebug2 ? node.warn(\"-------------\") : null;\nlet devID = msg.payload.id;                                                 //get hubitat device number which is the key within the global variable\nlet pastValues = RED.util.cloneMessage(global.get(\"VirtualDevices\") || {}); //get a new object (not reference) that is either empty or global variable\nif (!(pastValues.hasOwnProperty(devID))) {                                  //check if the device is already in the global variable\n    let newMeasurement =                                                    //placeholder structure if the device needs to be added to the global variable\n    {\n        \"Latest\": {\n            \"Value\": null,\n            \"Time\": null\n        },\n        \"Last\": {\n            \"Value\": null,\n            \"Time\": null\n        },\n        \"Last Different\": {\n            \"Value\": null,\n            \"Time\": null\n        }\n    };\n    let nullObject = {                                                      //creates the object with the device id as the key and the empty entries object as the value\n        [devID]: {\n            \"Hue\": RED.util.cloneMessage(global.get(newMeasurement)),       //IMPORTANT NOTE: \n            //When I originally did this, I did not clone the object. As a result when it was\n            //saved to the global varible, the Hue, Saturation and Level keys all had the same value because\n            //it was a shared reference to the same object.\n            //This made debugging the flow very hard because I kept thinking the code below was updating all\n            //3 for some bug in the update code when it was 1 object being displayed under 3 different keys\n            //Lesson learned for the creator who isn't as smart about Javascript as he thought he was\n            \"Saturation\": RED.util.cloneMessage(global.get(newMeasurement)),\n            \"Level\": RED.util.cloneMessage(global.get(newMeasurement))\n        }\n    };\n    Object.assign(pastValues, nullObject);                                  //adds the formated but empty entry to the global variable\n    global.set(\"VirtualDevices\", pastValues);                               //writes the global variable\n    showDebug ? node.warn(\"New Blank put in place because global either doesn't exist or doesn't have the device yet\") : null;\n}\nshowDebug ? node.warn(msg.payload) : null;\nlet oldHue = pastValues[devID].Hue.Latest.Value                             //get the previous hue value saved in the global\nlet oldSaturation = pastValues[devID].Saturation.Latest.Value               //get the previous saturation value saved in the global\nlet oldLevel = pastValues[devID].Level.Latest.Value                         //get the previous level value saved in the global\nlet newHue = msg.payload.attributes[6].currentValue                         //get the new hue value saved in the global\nlet newSaturation = msg.payload.attributes[2].currentValue                  //get the new saturation value saved in the global\nlet newLevel = msg.payload.attributes[9].currentValue                       //get the new level value saved in the global\nlet newMsg = {}\nshowDebug3 ? node.warn(oldHue) : null;\nshowDebug3 ? node.warn(newHue) : null;\nshowDebug3 ? node.warn(oldSaturation) : null;\nshowDebug3 ? node.warn(newSaturation) : null;\nif ((oldHue == newHue) && (oldSaturation == newSaturation)) {               //go into the then section if neither hue nor saturation changed (i.e. it is really a brightness change)\n    if (!(pastValues[devID].Level.Latest == newLevel)) {   //check if this is a new brightness value\n        newMsg.Brightness = newLevel;                                       //put the level into the msg object that will be returned\n        updateHistory(\"VirtualDevices\", devID, [\"Level\"], [newLevel]);      //call the common function to upadate the global variable with the new level\n        return [newMsg, null];                                              //return the msg on output 1 for use as a brightness change\n    };\n} else {\n    newMsg.HSL = [newHue, newSaturation, oldLevel];                         //build the HSL output from the new hue, new saturation and the pre-existing level\n    //See important note on why the pre-existing level\n    updateHistory(\"VirtualDevices\",                                         //call the common function to upadate the global variable with the new hue,saturation, and level triplet\n        devID,\n        [\"Hue\", \"Saturation\", \"Level\"],\n        [newHue, newSaturation, oldLevel]);\n    return [null, newMsg];                                                  //return the msg on output 2 for use as a HSL color value\n};\nreturn;\n\n/**\n* @param {string} deviceType\n* @param {string} deviceKey\n* @param {array} valueKeys\n* @param {array} updates\n*/\nfunction updateHistory(deviceType, deviceKey, valueKeys, updates) {\n    showDebug ? node.warn(valueKeys) : null;\n    showDebug ? node.warn(updates) : null;\n    if (valueKeys.length != updates.length) { return };                     //do not process if the number of keys and values don't match\n    let pastValues = RED.util.cloneMessage(global.get(deviceType) || {});   //get a new object (not reference) that is either empty or global variable\n    let newMeasurement =                                                    //placeholder structure if the device needs to be added to the global variable\n    {\n        \"Latest\": {\n            \"Value\": null,\n            \"Time\": null\n        },\n        \"Last\": {\n            \"Value\": null,\n            \"Time\": null\n        },\n        \"Last Different\": {\n            \"Value\": null,\n            \"Time\": null\n        }\n    };\n    var d = new Date();                                                     //get a date to be used to label when the updated values where recorded\n    let measurement;                                                        //create variables to be used inside of the for loop\n    let value;\n    for (let index = 0; index < updates.length; index++) {                  //loop through the keys and values performing the same logic on each one\n        measurement = valueKeys[index]                                      //store the key for this time through the look into a variable for easier reference\n        value = updates[index]\n        showDebug ? node.warn(measurement + \" \" + value) : null;\n        if (pastValues[deviceKey].hasOwnProperty(measurement)) {            //check if the measurement is already a key in the object attached to the device Id\n            showDebug ? node.warn(\"if (pastValues[deviceKey].hasOwnProperty(measurement)) {  The measurement exists\") : null;\n            pastValues[deviceKey][measurement].Last = RED.util.cloneMessage(pastValues[deviceKey][measurement].Latest); //create a copy of the previous entry and put into the Last part of the object\n            pastValues[deviceKey][measurement].Latest.Time = d;                                                         //Store the date for the latest time entry in the global variable\n            if (!(pastValues[deviceKey][measurement].Latest.Value == value)) {                                          //check if the value is different from the last update\n                showDebug2 ? node.warn(\"new value for the \" + measurement) : null;\n                showDebug2 ? node.warn(deviceKey) : null;\n                showDebug3 ? node.warn(measurement) : null;\n                showDebug3 ? node.warn(\"~124     before the update of Latest value\") : null;\n                showDebug3 ? node.warn(pastValues[deviceKey][measurement]) : null;\n                pastValues[deviceKey][measurement][\"Last Different\"] =                                                  //since they did no match, create a copy of the previous entry and put into the Last Different part of the object\n                    RED.util.cloneMessage(pastValues[deviceKey][measurement].Latest);\n                showDebug2 ? node.warn(\"~128 \" + measurement + \" \" + value) : null;\n                showDebug2 ? node.warn(pastValues[deviceKey][measurement]) : null;\n                pastValues[deviceKey][measurement].Latest.Value = value;                                                //add the passed value to the Latest part of the object\n                showDebug2 ? node.warn(pastValues[deviceKey][measurement]) : null;\n            }\n        } else {                                                                                                        //process for when the passed measurement doesn't yet exist\n            showDebug2 ? node.warn(\"if (pastValues[deviceKey].hasOwnProperty(measurement)) {} Else\") : null;\n            pastValues[deviceKey][measurement] = RED.util.cloneMessage(newMeasurement);                                 //add the measurement as a key with the empty structure\n            pastValues[deviceKey][measurement].Latest.Time = d;                                                         //add the date to the Latest part of the object\n            pastValues[deviceKey][measurement].Latest.Value = value;                                                    //add the passed value to the Latest part of the object\n        };\n        showDebug2 ? node.warn(\"About to Loop\") : null;\n        showDebug2 ? node.warn(pastValues) : null;\n    };                                                                                                                  //end of the for loop\n    showDebug2 ? node.warn(\"~141 about to show the object to be added to\") : null;\n    showDebug2 ? node.warn(pastValues) : null;\n    global.set(\"VirtualDevices\", pastValues);                                                                           //write the updates to the global variable\n    return\n};","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":930,"y":2860,"wires":[["097a5670fbf70301"],["a195a6014dce2014"]],"info":"# Very important note.\r\n    For the original creators need, the HSL output sends the hue and saturation that comes from the Hubitat, but ignores the Level.\r\n    This is because when you use the Hubitat color picker, then you will almost inevitably change the level.\r\n    The level however is also the brightness of the bulb.\r\n    So, I use the previously set brightness (level) so the color of the light changes, but the brightness doesn't change.\r\n    If you want to use the level sent from Hubitat when coming from the color picker, then you want to alter the line:\r\n        updateHistory(\"VirtualDevices\", devID, [\"Hue\", \"Saturation\", \"Level\"], [newHue, newSaturation, oldLevel])    \r\n    to\r\n        updateHistory(\"VirtualDevices\", devID, [\"Hue\", \"Saturation\", \"Level\"], [newHue, newSaturation, newLevel])\r\n\r\n"},{"id":"a195a6014dce2014","type":"debug","z":"5c9e82eadde3cbc4","g":"d4f615bad7f8c4d9","name":"HSL","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1110,"y":2900,"wires":[]},{"id":"097a5670fbf70301","type":"debug","z":"5c9e82eadde3cbc4","g":"d4f615bad7f8c4d9","name":"B","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1110,"y":2820,"wires":[]},{"id":"d26243490fe07b6c","type":"group","z":"5c9e82eadde3cbc4","g":"d4f615bad7f8c4d9","name":"Incoming inputs from Hubitat","style":{"fill":"#ffffff","label":true,"color":"#000000"},"nodes":["435e6f24f59ba2f0","7df790dc571302b3","9b375b45b3e33ace"],"x":184,"y":2759,"w":192,"h":202},{"id":"435e6f24f59ba2f0","type":"hubitat device","z":"5c9e82eadde3cbc4","g":"d26243490fe07b6c","deviceLabel":"Mike's High Desk Lamp","name":"Hue","server":"4452f29.dcf160c","deviceId":"139","attribute":"hue","sendEvent":true,"x":300,"y":2800,"wires":[["386250a07242f362"]]},{"id":"7df790dc571302b3","type":"hubitat device","z":"5c9e82eadde3cbc4","g":"d26243490fe07b6c","deviceLabel":"Mike's High Desk Lamp","name":"Saturation","server":"4452f29.dcf160c","deviceId":"139","attribute":"saturation","sendEvent":true,"x":280,"y":2860,"wires":[["386250a07242f362"]]},{"id":"9b375b45b3e33ace","type":"hubitat device","z":"5c9e82eadde3cbc4","g":"d26243490fe07b6c","deviceLabel":"Mike's High Desk Lamp","name":"Level","server":"4452f29.dcf160c","deviceId":"139","attribute":"level","sendEvent":true,"x":300,"y":2920,"wires":[["386250a07242f362"]]},{"id":"4452f29.dcf160c","type":"hubitat config","name":"116 to NTPI1F","usetls":false,"host":"192.168.86.116","port":"80","appId":"103","nodeRedServer":"http://ntpi1f:1880","webhookPath":"/hubitat/116toNTPI1F","autoRefresh":true,"useWebsocket":false,"colorEnabled":true,"color":"#a942e0"}]

To use this, change the Hubitat nodes to pull from your hub. Use the Hubitat interface to change the color (color picker, hue or saturation). You will see the format of the output on the HSL debug node. msg.HSL is an array with following format: [hue, saturation, level] Then change just the level and see the format of the output on the B debug node. You will see the format of the output is msg.Brightness = level.

It is worth knowing that it stores info in a global variable name VirtualDevices. That name can be changed by editing the function node. Also, if you want the output under a different key (say msg.payload), that alteration can be made in function node as well.

I hope this helps anyone else that runs into the challenge I had. If you want more detail it is at: https://community.hubitat.com/t/node-red-nodes-for-hubitat/34386/4988

Edit: Do also see that the access token to your maker instance (along with the Hubitat address are in the HTTP node. You should alter them to match your setup if you want to get it to work. Sorry I missed that on first posting.

Very important note.
If you ignore this portion of the post, the output may look like it has an error for the Level portion of the HSL array.

For my need, the HSL output sends the hue and saturation that comes from the Hubitat, but ignores the Level. This is because when you use the Hubitat color picker, then you will almost inevitably change the level. The level, however, is also the brightness of the bulb.

So, I use the previously set brightness (level) so the color of the light changes, but the brightness doesn't change. If you want to use the level sent from Hubitat when coming from the color picker, then you want to alter the line in the function node:

updateHistory("VirtualDevices", devID, ["Hue", "Saturation", "Level"], [newHue, newSaturation, oldLevel])
to
updateHistory("VirtualDevices", devID, ["Hue", "Saturation", "Level"], [newHue, newSaturation, newLevel])

2 Likes

Anyone using one of the various alexa node red forks have their "on device activity" nodes stop working yesterday?

1 Like

Mine was working as of yesterday and I know this because my daughter was complaining about it so all good there :wink: . I am using the AppleStrudel version.

I am also on AppleStrudel at the moment. Boy... I sure wish Amazon would open up a public API for this stuff. These various projects are GREAT when they work correctly but it sure feels like they are held together with duct tape and hope (100% Amazon's fault on that, not the maintainers of the various implementations)

I'm sure it's a bit of both - of course when you are at the mercy of cloud providers you kind of get what you get unfortunately - on their terms.. strong argument for local processing.

1 Like

For sure. I'd happily pay Amazon for a "prosumer" box that had a local API - even if that API still needed to reach out to the amazon cloud for things that NEED cloud connectivity. Or... just pay them for maintained/official API access.

2 Likes

Spun up a fresh VM, installed NR and applestrudel. Same issue, everything works except for "on device activity". Hopefully it is just some kind of amazon cloud hiccup.

Hmmm okay will check it out and let you know if I get the same...

I'm the same here, not getting anything out of debug either, absolutely nothing :cry:

Yep not working for me either... sigh.

Mines just started working again this morning, I didn't do anything :slight_smile:

1 Like

Some team in IT over at Amazon is having "one of those days" no doubt... mine is not working yet but I probably screwed up the config.

1 Like

I'd think with all the ST outages too, it was a massive "one of those days" hahaha. So glad thats been binned now!!

1 Like

Maybe "one of those jobs"... :exploding_head:

1 Like

And quite possibly they dont have a job any more :open_mouth: " Whats this button do??" :joy: