# Subflow used to retrieve JSON key value

**URL:** <https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780>\
**Category:** General\
**Created:** [19 December 2020 23:00 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780 "2020-12-19T23:00:44Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [19 December 2020 23:00 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/1 "2020-12-19T23:00:45Z")

</div>

I am just starting to learn JavaScript & I would really appreciate some guidance. I am trying to create a subflow that will return the value of (2) keys nested within JSON output. (`name` and `last use date`)

The "tricky part" is that I only want to return the name & timestamp from the **most recent value**. (The value that is closest to the "current time".)

For example, based on the JSON output (see below) I would like to return:  
`"last use date": "2020-12-18T13:53:33", "name": "amazon_dash_button"`

```auto
{
    "whitelist": {
            "55dd6daa-d54e-4a8d-dd5d-9a9c12ad5283": {
            "last use date": "2020-12-18T13:21:42",
            "create date": "2019-07-26T14:55:07",
            "name": "Hue 3#iPhoneA"
        },
        "d547d0a5-64dc-45d1-a809-66355a6d942a": {
            "last use date": "2020-12-18T04:33:20",
            "create date": "2019-07-26T21:59:18",
            "name": "hue-alexa-smarthome-skill-v1"
        },
        "5e558a27-d071-46c3-851d-c610e9404a8c": {
            "last use date": "2020-07-15T23:55:51",
            "create date": "2019-07-27T00:13:19",
            "name": "Hue 3#iPhoneB"
        },
        "7d08da1d-6cde-4cd5-d14d-21dd6a77d993": {
            "last use date": "2020-10-16T21:48:15",
            "create date": "2019-07-31T21:01:51",
            "name": "Hue 3#iPhoneC"
        },
        "6Xw2Jjha7Y7HNpvqI2OnAodSsxG5LVUod-yVxzvJ": {
            "last use date": "2020-12-18T13:53:33",
            "create date": "2020-01-29T23:02:57",
            "name": "amazon_dash_button"
        },
        "37e85d98-1832-49cd-a508-5747a7716d52": {
            "last use date": "2020-12-18T05:30:11",
            "create date": "2020-06-18T18:25:20",
            "name": "iftttv2"
        }
    }
}

```

Thanks.

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [19 December 2020 23:15 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/2 "2020-12-19T23:15:19Z")

</div>

Guidance...  
Forget subflow for now & make this work with an inject (to provide the data) --\> function node ( to do the js) --\> debug node to check the output.

Clues...  
Convert the properties in `whitelist` to an array, then sort, then grab the 1st element.

Read up on `Object.keys()` & iterating object properties.

Then read up on `Array.sort()`

Ps, make use of the mdn site. Not only does it explain the things I've mention, it has interactive examples you can tweak and play with right in the browser.

* * *

If you sus that out, you will learn a lot.

If you get stuck, ask.

---

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [20 December 2020 02:33 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/3 "2020-12-20T02:33:30Z")

</div>

@Steve-Mcl  
MDN is a wonderful resource. I am also a huge fan of W3Schools.

However, I am struggling with the fact that each of the top-level properties within the `whitelist` object are **unique hashes**.

`name` and `last use date` are the two values that I care about. All of [the examples](https://www.w3schools.com/js/js_json_objects.asp) that I've found store nested key:value pairs using a `for` loop. However, I cannot use the same strategy, because the hashes (aka: the parent object) use inconsistent/unique names.

As such, I fail to see how I can store the `last use date` values into a single array.  
Thoughts?

---

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [20 December 2020 03:22 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/4 "2020-12-20T03:22:09Z")

</div>

You could use a **Jsonata** expression in a **Change node** to create the array  
and sort it by `"last use date"`

Then just pick the first element [0] which should be the max date

**Example :**

```auto
[{"id":"654412d9.ca3e0c","type":"change","z":"284375ad.fa5e3a","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$sort(**[`last use date`], function($l, $r) {\t $l.`last use date` < $r.`last use date`\t})[0]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":220,"wires":[["6cbadb17.25fd7c"]]},{"id":"6cbadb17.25fd7c","type":"debug","z":"284375ad.fa5e3a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":840,"y":220,"wires":[]},{"id":"5335b63a.5d1fc8","type":"inject","z":"284375ad.fa5e3a","name":"data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"whitelist\":{\"55dd6daa-d54e-4a8d-dd5d-9a9c12ad5283\":{\"last use date\":\"2020-12-18T13:21:42\",\"create date\":\"2019-07-26T14:55:07\",\"name\":\"Hue 3#iPhoneA\"},\"d547d0a5-64dc-45d1-a809-66355a6d942a\":{\"last use date\":\"2020-12-18T04:33:20\",\"create date\":\"2019-07-26T21:59:18\",\"name\":\"hue-alexa-smarthome-skill-v1\"},\"5e558a27-d071-46c3-851d-c610e9404a8c\":{\"last use date\":\"2020-07-15T23:55:51\",\"create date\":\"2019-07-27T00:13:19\",\"name\":\"Hue 3#iPhoneB\"},\"7d08da1d-6cde-4cd5-d14d-21dd6a77d993\":{\"last use date\":\"2020-10-16T21:48:15\",\"create date\":\"2019-07-31T21:01:51\",\"name\":\"Hue 3#iPhoneC\"},\"6Xw2Jjha7Y7HNpvqI2OnAodSsxG5LVUod-yVxzvJ\":{\"last use date\":\"2020-12-18T13:53:33\",\"create date\":\"2020-01-29T23:02:57\",\"name\":\"amazon_dash_button\"},\"37e85d98-1832-49cd-a508-5747a7716d52\":{\"last use date\":\"2020-12-18T05:30:11\",\"create date\":\"2020-06-18T18:25:20\",\"name\":\"iftttv2\"}}}","payloadType":"json","x":390,"y":220,"wires":[["654412d9.ca3e0c"]]},{"id":"afd8cc8c.d930a","type":"comment","z":"284375ad.fa5e3a","name":"Jsonata expression","info":"","x":590,"y":160,"wires":[]}]

```

---

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [20 December 2020 04:16 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/5 "2020-12-20T04:16:50Z")

</div>

@UnborN  
Nice! That works 💯%!

I'll def. spend some time reading up on [JSONata](https://jsonata.org).

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [20 December 2020 08:52 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/6 "2020-12-20T08:52:38Z")

</div>

> [@mengelsen](#):
>
> As such, I fail to see how I can store the `last use date` values into a single array.  
> Thoughts?

Thoughts,? plenty yes....

I had hoped no one would give you a solution (you don't really learn much when it's put on a plate). I got the impression you wanted to learn JS.

PS, JSONata is handy, but it's not really gonna make you a programmer.

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [20 December 2020 09:35 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/7 "2020-12-20T09:35:42Z")

</div>

> [@mengelsen](#):
>
> However, I am struggling with the fact that each of the top-level properties within the `whitelist` object are **unique hashes**.

That is why I said read up on iterating object properties." It would have lead you to something like [this](https://medium.com/backticks-tildes/iterating-through-javascript-objects-5-techniques-and-performance-tests-42b4a222a92b). I also mentioned `Object.keys` (which would have lead you to Object.entries(), for-in etc etc).

example...  
 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/e/a/ea4832c7757a72ff63b187488ad58cad69436bd2.png)

* * *

I also said

> [@Steve-Mcl](#):
>
> Convert the properties in `whitelist` to an array

That should have given you a clue to use the iterating loop to generate an array...

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/c/0/c0630caac03137a2bd9d34fc2364a30cb298a11a.png)

* * *

Then I said ...

> [@Steve-Mcl](#):
>
> then sort, then grab the 1st element.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/1/a/1a22d565722ec4f4a128c5a6b283ff21c2d12b5e.png)

* * *

That would have given you a rather verbose solution - but you would have learned a lot. I would then have shown you a much more concise version using `Object.entries` and arrow functions...

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/6/a/6af0af684573b7a5dadbdad997075bbc91fb0912.png)

or a one liner...

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/9/9/990551ec036683d16e9a1d8adb232c4f56bd810d.png)

---

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [20 December 2020 17:07 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/8 "2020-12-20T17:07:58Z")

</div>

I really like @UnborN's [K.I.S.S.](https://t2informatik.de/en/smartpedia/kiss-principle/) approach. This solution draws attention to a useful property (JSONata) that exists within the **change** node.

The (included) example saves me a lot of time & effort. It also provides others with the ability to reproduce (and then evaluate) the Node-RED flow.

---

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [20 December 2020 17:08 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/9 "2020-12-20T17:08:52Z")

</div>

> [@Steve-Mcl](#):
>
> I also mentioned `Object.keys` (which would have lead you to Object.entries(), for-in etc etc).

@Steve-Mcl Thank you for this detailed explanation. Your "clues" were helpful, but I was thinking on a totally different wavelength. I was trying (unsuccessfully) to use Object.keys() to "capture" the key:value pairs that I cared about. (`name` and `last date used`) However, I now realize that this JS function represents a small step, used to create an array containing nothing but the unique hashes.

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [20 December 2020 17:15 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/10 "2020-12-20T17:15:40Z")

</div>

> [@mengelsen](#):
>
> This solution draws attention to a useful property (JSONata) that exists within the **change** node.

JSONata is good & all but its not really a transferrable skill. (IMO)

I mistakenly thought you wanted to learn how to program (you asked for "Guidance" and specifically "JavaScript")

Accepting a solution that did not take in to account your requests is not really teaching you much.

As for the "KISS" approach - you probably dont realise the large abount of JavaScript code under the hood of JSONata - the JavaScript I showed you (the longer version) is much more KISS (IMO).

---

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [20 December 2020 17:19 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/11 "2020-12-20T17:19:56Z")

</div>

> [@Steve-Mcl](#):
>
> > [@Steve-Mcl](#):
> >
> > Convert the properties in `whitelist` to an array
> 
> That should have given you a clue to use the iterating loop to generate an array...

This part confuses me the most. Your `for` loop (see below) produces the following output:

```auto
var keys = Object.keys(object1.whitelist);
 var asArray = [];
 for (let i = 0; i < keys.length; i++) {
     let key = keys[i];
     let item = object1.whitelist[key]; //get item
     asArray.push(object1.whitelist);
 }
 console.log(asArray)

```

 ![Screen Shot 2020-12-20 at 12.11.55 PM](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/d/9/d908e3a63f63453437a5527b9d723a537c18da05.png)  
(As written) the loop comes close to producing an array containing the whitelist properties. However, it is currently outputting 6 identical "copies" of the data.

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [20 December 2020 17:23 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/12 "2020-12-20T17:23:11Z")

</div>

There is a typo (code was untested) (and the 2nd image shows it corrected)

But that is all part of learning.

change `asArray.push(object1.whitelist);` to `asArray.push(item);`

---

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [20 December 2020 17:32 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/13 "2020-12-20T17:32:41Z")

</div>

> [@Steve-Mcl](#):
>
> change `asArray.push(object1.whitelist);` to `asArray.push(item);`

Much better. 👍🏼

Another acceptable solution would be:  
`asArray.push(object1.whitelist[key])`  
which is **exactly** what you used, when you were demonstrating how to sort the array.

Thank you for this learning opportunity. Much appreciated!

---

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [20 December 2020 19:27 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/14 "2020-12-20T19:27:03Z")

</div>

> [@Steve-Mcl](#):
>
> ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/1/a/1a22d565722ec4f4a128c5a6b283ff21c2d12b5e.png)

@Steve-Mcl can you help me understand the "if" logic (on lines 44 and 45)?  
(By default) is the `compare()` function designed to sort a list of dates oldest --\> newest?  
If so, is it "ok" to swap the comparison operators (\> and \<) in order to sort the array newest --\> oldest?

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [20 December 2020 19:44 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/15 "2020-12-20T19:44:45Z")

</div>

> [@mengelsen](#):
>
> If so, is it "ok" to swap the comparison operators (\> and \<) in order to sort the array newest --\> oldest?

Yes. the goal is to return a positive number (e.g. 1), or a negative number (e.g. -1) or 0 (if they match).

If the sort order needs reversing, either swap the `-1` and `1` OR swap the comparison operators.

---

<div class="post-metadata">

**Author:** ![mengelsen](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/mengelsen/32/33958_2.png) [@mengelsen](https://discourse.nodered.org/u/mengelsen)\
**Post date:** [20 December 2020 22:53 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/16 "2020-12-20T22:53:48Z")

</div>

Ok! Here is the final result.

```auto
[{"id":"ff0ee1c6.69484","type":"tab","label":"Report Home State","disabled":false,"info":"# Report Home State\n\n## Description\n\nThis flow logs Philips Hue light switch changes.\nPlease follow the instructions to set up this flow to your environment.\n\n## Motivation\n\nI use some rules for my light system and some motion detectors.\nAs I arrive at home, my light switches are automatically triggered by my smartphones WiFi logon.\nSometimes I ask myself, if the lights were really off during the day or always on, caused by a potential misconfiguration.\nSo I set up my Raspberry Pi to create a log file of light changes.\n\nMaybe it's usefull for you as well... ;)\n\n## Setup Instructions\n\n1. Follow the instructions on https://www.developers.meethue.com/documentation/getting-started to get your Philips Hue API URL.\n2. Inject the api url to the global variable `global.settings.hue.url`\n3. Set your desired log file name in the node named `Write to Lights States Log File`\n4. Now you can check when the lights were switched on and off\n \n_\n __Note:__ \nThe format of the timestamp is UTC in ISO 8601 compatible format and may not represent your local timezone.\n_"},{"id":"288f64f2.622c9c","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"596ec16a.2048d","type":"subflow","name":"Array Of Booleans to String","info":"","in":[{"x":40,"y":40,"wires":[{"id":"af39cdd0.144f9"}]}],"out":[{"x":440,"y":40,"wires":[{"id":"af39cdd0.144f9","port":0}]}]},{"id":"fdccb947.3480f8","type":"subflow","name":"Get Lights State","info":"","in":[{"x":40,"y":40,"wires":[{"id":"5a751ab7.8f3514"}]}],"out":[{"x":300,"y":40,"wires":[{"id":"5a751ab7.8f3514","port":0}]}]},{"id":"a1719e16.dc588","type":"subflow","name":"Device Responsible for BulbStateChange","info":"","category":"","in":[{"x":20,"y":100,"wires":[{"id":"454db0b1.48969"}]}],"out":[{"x":980,"y":100,"wires":[{"id":"24a93773.36593","port":0}]}],"env":[],"color":"#DDAA99"},{"id":"af39cdd0.144f9","type":"function","z":"596ec16a.2048d","name":"Array Of Booleans to String","func":"let ret = \"\";\n\nfor (const b of msg.payload) {\n ret += (b === true) ? \"1\" : \"0\";\n}\nmsg.payload = ret;\nreturn msg;","outputs":1,"noerr":0,"x":240,"y":40,"wires":[[]]},{"id":"5a751ab7.8f3514","type":"function","z":"fdccb947.3480f8","name":"Get Lights States","func":"let ret = [];\n\nfor (const light in msg.payload) {\n ret.push(msg.payload[light].state.on);\n}\n\nmsg.payload = ret;\nreturn msg;\n","outputs":1,"noerr":0,"x":170,"y":40,"wires":[[]]},{"id":"43b7079.0cad1f8","type":"inject","z":"ff0ee1c6.69484","name":"","props":[{"p":"payload","v":"","vt":"date"},{"p":"topic","v":"","vt":"string"}],"repeat":"5","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":180,"wires":[["a646fed9.c4707"]]},{"id":"72f0c35c.c0d9ac","type":"http request","z":"ff0ee1c6.69484","name":"Get Lights","method":"GET","ret":"obj","url":"","tls":"","x":330,"y":380,"wires":[["44540150.c3bd9"]]},{"id":"5a78994b.8884f8","type":"change","z":"ff0ee1c6.69484","name":"Set Lights URL","rules":[{"t":"set","p":"url","pt":"msg","to":"$globalContext(\"settings.hue.url\") & \"/lights\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":160,"y":380,"wires":[["72f0c35c.c0d9ac"]]},{"id":"a9958d2a.8a77e","type":"change","z":"ff0ee1c6.69484","name":"Save Last State","rules":[{"t":"set","p":"lastLightsState","pt":"flow","to":"lightsState","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":160,"y":280,"wires":[["aa099e57.239"]]},{"id":"aa73ebd6.d0e128","type":"change","z":"ff0ee1c6.69484","name":"Set Current State","rules":[{"t":"set","p":"lightsState","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":170,"y":480,"wires":[["1a8479df.871036"]]},{"id":"d7c05f9.56629a","type":"switch","z":"ff0ee1c6.69484","name":"Lights State Changed?","property":"lightsState","propertyType":"flow","rules":[{"t":"else"},{"t":"neq","v":"lastLightsState","vt":"flow"}],"checkall":"true","repair":false,"outputs":2,"x":190,"y":580,"wires":[["4f3ce9e2.673078"],["45d7d2c0.8a203c"]],"outputLabels":["no","yes"]},{"id":"feda1399.e1a9b","type":"debug","z":"ff0ee1c6.69484","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":690,"y":580,"wires":[]},{"id":"5cbc9fd3.489b","type":"change","z":"ff0ee1c6.69484","name":"off text","rules":[{"t":"set","p":"payload","pt":"msg","to":"ALL LIGHTS OFF","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":640,"wires":[["a93d0be5.e98928"]]},{"id":"4f3ce9e2.673078","type":"change","z":"ff0ee1c6.69484","name":"unchanged text","rules":[{"t":"set","p":"payload","pt":"msg","to":"lights unchanged","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":580,"wires":[[]]},{"id":"9053e561.d4eeb8","type":"debug","z":"ff0ee1c6.69484","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":870,"y":420,"wires":[]},{"id":"44540150.c3bd9","type":"subflow:fdccb947.3480f8","z":"ff0ee1c6.69484","x":500,"y":380,"wires":[["f4b1114.a61d6f","a2b2c731.b87918"]]},{"id":"f4b1114.a61d6f","type":"subflow:596ec16a.2048d","z":"ff0ee1c6.69484","name":"Convert to String","x":690,"y":380,"wires":[["9053e561.d4eeb8","6b1950db.6aaa1"]]},{"id":"45d7d2c0.8a203c","type":"switch","z":"ff0ee1c6.69484","name":"Are All Lights Off?","property":"$contains($flowContext(\"lightsState\"), \"1\")","propertyType":"jsonata","rules":[{"t":"false"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":450,"y":640,"wires":[["5cbc9fd3.489b"],["c1112385.476cf"]],"outputLabels":["yes","no"]},{"id":"69162fa8.7249d","type":"change","z":"ff0ee1c6.69484","name":"changed text","rules":[{"t":"set","p":"payload","pt":"msg","to":"\"LIGHTS CHANGED BY: \" & msg.lightChangedBy","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":700,"wires":[["a93d0be5.e98928"]]},{"id":"39631a9d.7dbb46","type":"file","z":"ff0ee1c6.69484","name":"Write to Lights States Log File","filename":"/usr/local/bin/PhilipsHue/logs/lightsstates.log","appendNewline":true,"createDir":true,"overwriteFile":"false","encoding":"utf8","x":1150,"y":640,"wires":[[]]},{"id":"a93d0be5.e98928","type":"change","z":"ff0ee1c6.69484","name":"Prepend Timestamp","rules":[{"t":"set","p":"payload","pt":"msg","to":"$now() & \" - \" & $flowContext(\"lightsState\") & \" - \" & msg.payload","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":860,"y":640,"wires":[["feda1399.e1a9b","39631a9d.7dbb46"]]},{"id":"89aa4dbb.f9aa","type":"comment","z":"ff0ee1c6.69484","name":"Set `global.settings.hue.url` to your Philips Hue api url: https://www.developers.meethue.com/documentation/getting-started","info":"> To get detailed step by step instructions, open the flow description.","x":440,"y":40,"wires":[],"icon":"node-red/alert.png"},{"id":"5315b9cc.738bd8","type":"comment","z":"ff0ee1c6.69484","name":"Check for changes every ... seconds","info":"","x":170,"y":140,"wires":[]},{"id":"9dcf6fef.1b09f","type":"comment","z":"ff0ee1c6.69484","name":"Save previous light state","info":"","x":130,"y":240,"wires":[]},{"id":"47f0a7ce.a5fec8","type":"comment","z":"ff0ee1c6.69484","name":"Get the current light state","info":"","x":130,"y":340,"wires":[]},{"id":"18a4fbe7.e23ea4","type":"comment","z":"ff0ee1c6.69484","name":"Save current light state","info":"","x":120,"y":440,"wires":[]},{"id":"f0dc5ba3.683aa8","type":"comment","z":"ff0ee1c6.69484","name":"Log light state changes","info":"","x":120,"y":540,"wires":[]},{"id":"f5d529a9.469eb8","type":"comment","z":"ff0ee1c6.69484","name":"⬇ SET FILENAME HERE","info":"> To get detailed step by step instructions, open the flow description.","x":1130,"y":600,"wires":[],"icon":"node-red/node-changed.png"},{"id":"9f60f3be.8913e","type":"change","z":"ff0ee1c6.69484","name":"OPEN ME!","rules":[{"t":"set","p":"settings.hue.url","pt":"global","to":"http://ip_address_here/api/api_token_here","tot":"str"},{"t":"set","p":"pi.apiName","pt":"global","to":"Use \"Flow1\" to identify the API name to omit.","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":80,"wires":[[]]},{"id":"18d30974.dd7e17","type":"inject","z":"ff0ee1c6.69484","name":"Set Initial Variables","props":[{"p":"payload","v":"","vt":"date"},{"p":"topic","v":"","vt":"string"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":150,"y":80,"wires":[["9f60f3be.8913e"]]},{"id":"4b6ae102.ac9d9","type":"comment","z":"ff0ee1c6.69484","name":"⬅ SET VARIABLES HERE","info":"> To get detailed step by step instructions, open the flow description.","x":530,"y":80,"wires":[],"icon":"node-red/node-changed.png"},{"id":"ca52298c.be1508","type":"link in","z":"ff0ee1c6.69484","name":"Save previous light state","links":["a646fed9.c4707"],"x":55,"y":280,"wires":[["a9958d2a.8a77e"]]},{"id":"ecf7b9a1.4ca168","type":"link in","z":"ff0ee1c6.69484","name":"Get the current light state","links":["aa099e57.239"],"x":55,"y":380,"wires":[["5a78994b.8884f8"]]},{"id":"6e1da50d.f53c4c","type":"link in","z":"ff0ee1c6.69484","name":"Save current light state","links":["6b1950db.6aaa1"],"x":55,"y":480,"wires":[["aa73ebd6.d0e128"]]},{"id":"9a9334da.405dd8","type":"link in","z":"ff0ee1c6.69484","name":"Log light state changes","links":["1a8479df.871036"],"x":55,"y":580,"wires":[["d7c05f9.56629a"]]},{"id":"a646fed9.c4707","type":"link out","z":"ff0ee1c6.69484","name":"","links":["ca52298c.be1508"],"x":235,"y":180,"wires":[]},{"id":"aa099e57.239","type":"link out","z":"ff0ee1c6.69484","name":"","links":["ecf7b9a1.4ca168"],"x":275,"y":280,"wires":[]},{"id":"6b1950db.6aaa1","type":"link out","z":"ff0ee1c6.69484","name":"","links":["6e1da50d.f53c4c"],"x":815,"y":380,"wires":[]},{"id":"1a8479df.871036","type":"link out","z":"ff0ee1c6.69484","name":"","links":["9a9334da.405dd8"],"x":295,"y":480,"wires":[]},{"id":"a2b2c731.b87918","type":"debug","z":"ff0ee1c6.69484","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":670,"y":440,"wires":[]},{"id":"6a688d8c.62eab4","type":"comment","z":"a1719e16.dc588","name":"Capture the Philips Hue config","info":"","x":410,"y":60,"wires":[]},{"id":"f3e9749d.059148","type":"http request","z":"a1719e16.dc588","name":"GetConfig","method":"GET","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":350,"y":100,"wires":[["2a54e795.ae9248"]]},{"id":"454db0b1.48969","type":"change","z":"a1719e16.dc588","name":"Set hueConfig URL","rules":[{"t":"set","p":"url","pt":"msg","to":"$globalContext(\"settings.hue.url\") & \"/config\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":170,"y":100,"wires":[["f3e9749d.059148"]]},{"id":"24a93773.36593","type":"function","z":"a1719e16.dc588","name":"Identify Device Last Used","func":" let hueConfig = msg.payload.whitelist;\n let keys = Object.keys(hueConfig);\n let asArray = [];\n \n //Populates the array using JSON from the hue config\n for (let i = 0; i < keys.length; i++) {\n let key = keys[i];\n let item = hueConfig[key]; //get item\n asArray.push(hueConfig[key]);\n }\n \n //Removes the Raspberry Pi from the array\n let omitFromArray = msg.deviceToOmit;\n for (let i = 0; i < keys.length; i++) {\n if ( asArray[i].name === omitFromArray) { \n asArray.splice(i, 1); \n }\n }\n \n //Sorts the array by \"last use date\"\n //Newest to Oldest\n let sorted = asArray.sort(function compare(a,b) {\n let aDate = new Date(a[\"last use date\"]), bDate = new Date(b[\"last use date\"]);\n if (aDate > bDate) return -1;\n if (aDate < bDate) return 1;\n })\n\nmsg.lightChangedBy = sorted[0].name; //get 1st element\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":810,"y":100,"wires":[[]]},{"id":"c1112385.476cf","type":"subflow:a1719e16.dc588","z":"ff0ee1c6.69484","name":"","x":720,"y":700,"wires":[["69162fa8.7249d"]]},{"id":"2a54e795.ae9248","type":"change","z":"a1719e16.dc588","name":"Identify Device to Omit","rules":[{"t":"set","p":"deviceToOmit","pt":"msg","to":"pi.apiName","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":100,"wires":[["24a93773.36593"]]},{"id":"d72db09f.468d48","type":"http request","z":"288f64f2.622c9c","name":"GetConfig","method":"GET","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":490,"y":140,"wires":[["f5871ca4.7f4048"]]},{"id":"7da858a6.99f3c","type":"change","z":"288f64f2.622c9c","name":"Set hueConfig URL","rules":[{"t":"set","p":"url","pt":"msg","to":"$globalContext(\"settings.hue.url\") & \"/config\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":140,"wires":[["d72db09f.468d48"]]},{"id":"f5871ca4.7f4048","type":"function","z":"288f64f2.622c9c","name":"Identify Device Last Used","func":" let hueConfig = msg.payload.whitelist;\n let keys = Object.keys(hueConfig);\n let asArray = [];\n \n //Populates the array using JSON from the hue config\n for (let i = 0; i < keys.length; i++) {\n let key = keys[i];\n let item = hueConfig[key].name; //get item\n asArray.push(hueConfig[key].name);\n }\n\nmsg.payload = asArray;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":690,"y":140,"wires":[["1a491f1c.8368a1"]]},{"id":"3f64c318.0af854","type":"inject","z":"288f64f2.622c9c","name":"","props":[],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payloadType":"str","x":130,"y":140,"wires":[["7da858a6.99f3c"]]},{"id":"1a491f1c.8368a1","type":"debug","z":"288f64f2.622c9c","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":910,"y":140,"wires":[]},{"id":"630e0608.888b28","type":"comment","z":"288f64f2.622c9c","name":"READ ME!","info":"1) Open the [Debug Sidebar](https://nodered.org/docs/user-guide/editor/sidebar/debug).\n2) Activate the (green) debug node.\n(Click the green box to the right of the node.)\n2) Click the blue \"Inject\" button.\n3) Click on the debug output.\n4) _Copy_ the name used by the raspberry pi.\n5) Go back to the \"Report Home State\" flow.\n6) Double click on the \"OPEN ME\" node.\n7) _Paste_ the name into 2nd global variable. (Called: global.pi.apiName)","x":120,"y":100,"wires":[]},{"id":"baf6f5c2.083cb","type":"comment","z":"288f64f2.622c9c","name":"⬇️ Activate debug node","info":"","x":1050,"y":100,"wires":[]}]

```

The Philips API does **not** provide reporting to indicate when lights are turned on/off & by who. This flow achieves just that!

98% of the credit for this flow goes to **[IOIO72](https://flows.nodered.org/flow/0e1f6dab22f60c13b90bede25276e16e)**.  
Their Node-Red flow pinpointed **when** the state changes were occurring.  
I identified what device issued the state change.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [3 January 2021 22:53 UTC](https://discourse.nodered.org/t/subflow-used-to-retrieve-json-key-value/37780/17 "2021-01-03T22:53:53Z")

</div>

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.
