Thursday, February 2, 2017

Reformatting nested JSON with JavaScript

Part 1: Use configuration events in Octoblu
Part 2: Creating custom devices in Octoblu
Part 3: Setting the state of an Octoblu device from a flow
Part 4: Listening to and acting on device state change in Octoblu
Part 5: Breaking values into new keys with a function node

In my last example for an Octoblu Function node I very simply took the string value from a key and using .split() broke it into new fields.

This had the dependency of a format convention for the string.

Now, what if I had an incoming array which contained that data. 
And I only wanted to select certain values within each array element, and I had additional data which was nested within another array that I want to bring up a level to make it easier to evaluate later on.

To describe this differently, lets look at (an abbreviated version of) my incoming message:

{
  "msg": {
    "data": [
      {
        "uuid": "3b8a7529-b0f1-ddba9dc4cc27",
        "desired_state": {
          "pairing_mode": null
        },
        "last_reading": {
          "connection": true,
          "connection_updated_at": 1480529975.6920671,
        },
        "hub_id": "509234",
        "name": "Redmond",
        "locale": "en_us",
        "units": {},
        "created_at": 1476738922,
        "triggers": []
      },
      {
        "last_event": {
          "vibration_occurred_at": null
        },
        "uuid": "4d60c8ad-b6d2-f17c5e4a1192",
        "desired_state": {},
        "last_reading": {
          "motion_changed_at": 1480490895.7546704,
          "motion_true_changed_at": 1480490698.2846074,
          "temperature_changed_at": 1480530247.413451,
          "connection_changed_at": 1480530247.413451
        },
        "name": "Redmond_lunch_door",
        "triggers": []
      },
  },
  "node": "5c0e3d40-bddd-6f97ce016844"
}


I have an incoming message (msg) it has an array (data) of documents.  The data within each document could be different as each is a different device with different capabilities and settings.

From this point I have a couple wants: I need the name information of the sensors (from my previous post), and I need to in-nest the values of last_reading to make it easier to handle down the line.

And, then I want to save this information to my Octoblu device (a few blog posts ago).

Lets just format on the array at this point, I don't want this to get too confusing.

//A document object to hold the sensors per room
var rooms = {};


for ( var i in (msg.data) ){



 var sensor = {}; //an empty document object to populate with new key:values



 sensor.name = msg.data[i].name; //the incoming name

 var dotName = (msg.data[i].name).replace(/_/g,"."); 

   //the name in dot notation instead of underscores (see the next post)


 // break the device name into its descriptors (from the last post)
 var descriptors = (msg.data[i].name).split('_');

 switch(descriptors.length){
  case 3:
   sensor.mapTitle = descriptors[0];
   sensor.room = descriptors[1];
   sensor.device = descriptors[2];
   break;
  case 2:
   sensor.mapTitle = descriptors[0];
   sensor.device = descriptors[1];
   break;
  case 1:
   sensor.device = descriptors[0];
   break;
 }

 // un-nest last_reading to make it easier to handle later on
 var last_reading = msg.data[i].last_reading;
 for ( var reading in last_reading ){
  sensor[reading] = last_reading[reading];
 }

 // only those devices with a room value
 if ( sensor.room ) {
  var room = {};
  dotName = "rooms." + dotName;
  rooms[dotName] = sensor;

  // in the end, I want the devices of a room under the key pattern for that room
 }
}
return rooms;


This is what I get back out:

{
  "msg": {
   "rooms.Redmond.lunch.motion": {
     "name": "Redmond_lunch_motion",
     "mapTitle": "Redmond",
     "room": "lunch",
     "device": "motion",
     "motion": false,
     "motion_updated_at": 1485539337.480442,
     "connection_changed_at": 1485175984.3230183,
     "motion_changed_at": 1485539337.480442,
     "motion_true_changed_at": 1485539125.483463,
     "temperature_changed_at": 1485529054.5705206
   },
   "rooms.Redmond.lunch.refrigerator": {
     "name": "Redmond_lunch_refrigerator",
     "mapTitle": "Redmond",
     "room": "lunch",
     "device": "refrigerator",
     "opened": false,
     "opened_updated_at": 1485539969.6240845,
     "connection_updated_at": 1485539969.6240845,
     "opened_changed_at": 1485539969.6240845
   },
   "rooms.Redmond.lunch.door": {
     "name": "Redmond_lunch_door",
     "mapTitle": "Redmond",
     "room": "lunch",
     "device": "door",
     "opened": false,
     "opened_updated_at": 1485538007.9089093,
     "connection_updated_at": 1485538007.9089093,
     "opened_changed_at": 1485538007.9089093
    }
  },
  "node": "e271a6c0-9f9b-8d7882b7836a"
}


Next post:  How that dot notation key name pattern is useful to me.

No comments: