Cisco Meraki CMX, Spark, Dashboard Example

Overview

This flow shows a basic example using Cisco CMX, Meraki Dashboard, and Spark API's.

The CMX receiver node listens for incoming CMX POSTs and passes along the data. This example uses a simple mac address match on the CMX data and splits the output into 2 paths.

  • The first path parses the CMX data and posts the matching client's RSSI (AP's perspective) and AP tags into a Spark room.
  • The second path parses the CMX data to first determine the AP mac address. The AP mac is passed along and used in a search through Meraki Dashboard inventory obtained through an API call performed by a generic http request node. When a match is found, the device name from Dashboard inventory is passed along to a second Spark node to post a message into a Spark room with the device name.

Resources

[{"id":"e064555e.872698","type":"Meraki CMX","z":"6788a188.0be7a","name":"CMX listener","url":"/cmx","settings":"413d57e0.3cb088","x":103,"y":37,"wires":[["2e0a61a0.0dd8de","cdbf829a.42c2f"]]},{"id":"c0e55cbf.9f315","type":"debug","z":"6788a188.0be7a","name":"No match","active":false,"console":"false","complete":"true","x":563.5,"y":184,"wires":[]},{"id":"2e0a61a0.0dd8de","type":"debug","z":"6788a188.0be7a","name":"Raw CMX debug","active":false,"console":"false","complete":"true","x":185.5,"y":112,"wires":[]},{"id":"cb80b1b5.f7757","type":"file","z":"6788a188.0be7a","name":"logs/cmx.log","filename":"logs/cmx.log","appendNewline":true,"createDir":false,"overwriteFile":"false","x":1028.5,"y":80,"wires":[]},{"id":"b08e64ac.a47378","type":"debug","z":"6788a188.0be7a","name":"Spark POST debug","active":true,"console":"false","complete":"true","x":1384.5,"y":115,"wires":[]},{"id":"4bec2f72.8b0e9","type":"switch","z":"6788a188.0be7a","name":"MAC address match","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"de:ad:00:00:be:ef","vt":"str"},{"t":"else"}],"checkall":"false","outputs":2,"x":450.5,"y":38,"wires":[["41ac5b22.1405c4"],["5a210230.a335dc"]]},{"id":"cdbf829a.42c2f","type":"json","z":"6788a188.0be7a","name":"","x":263.5,"y":38,"wires":[["4bec2f72.8b0e9"]]},{"id":"5a210230.a335dc","type":"json","z":"6788a188.0be7a","name":"","x":541.5,"y":113,"wires":[["c0e55cbf.9f315"]]},{"id":"41ac5b22.1405c4","type":"json","z":"6788a188.0be7a","name":"","x":636.5,"y":32,"wires":[["f51e49f7.694958","38e9b2f2.fa0c2e"]]},{"id":"f51e49f7.694958","type":"function","z":"6788a188.0be7a","name":"Build Spark params","func":"// This function parses the CMX data to construct\n// HTTP POST parameters for sending a Spark message to a room\nmap = msg.payload;\nclient = {}; //reset payload object for clarity\nclient.roomId = 'my_spark_room_id';\nif (map['version'] != '2.0'){\n msg.log = \"got post with unexpected version: #{map['version']}\";\n return msg;\n}else{\n msg.log = \"working with correct version\";\n}\nif (map['type'] != 'DevicesSeen'){\nmsg.log = \"got post for event that we're not interested in: #{map['type']}\";\nreturn msg;\n}\nvar o = map['data']['observations'];\nconsole.log('map.data.apMac = '+map.data['apMac']);\n for (var c in o){\n if (o.hasOwnProperty(c)) {\n if (!o[c]['location']){continue}\n console.log('map.data.observations.clientMac = '+o[c]['clientMac']);\n if (o[c]['clientMac'] === \"de:ad:00:00:be:ef\"){\n  apMac = map.data['apMac'];\n  rssi = o[c]['rssi'];\n  apTags = map.data['apTags'];\n  client.text = 'Client: Jason Bourne detected (rssi ' + rssi + 'dB) by AP tagged:' + apTags;\n }\n if (client.seenEpoch===null || client.seenEpoch === 0){continue}// # This probe is useless, so ignore it\n }\n}\nmsg.payload.body = client;\nreturn msg;","outputs":"1","noerr":0,"x":856,"y":32,"wires":[["cb80b1b5.f7757","efd4d1d2.20f3c","2646d423.4aafdc"]]},{"id":"2646d423.4aafdc","type":"debug","z":"6788a188.0be7a","name":"Spark POST params","active":true,"console":"false","complete":"true","x":960.5,"y":118,"wires":[]},{"id":"38e9b2f2.fa0c2e","type":"function","z":"6788a188.0be7a","name":"Build Dashboard API call","func":"// This function creates HTTP GET parameters\n// for sending to the Meraki Dashboard\nmap = msg.payload;\napi_key = 'my_api_key';\ndashboard_header = {\n        'x-cisco-meraki-api-key': api_key,\n        'Content-Type': 'application/json'\n};\n\nmsg.headers = dashboard_header;\nmsg.ap_mac = map.data.apMac;\nmsg.payload = '';\nreturn msg;\n","outputs":1,"noerr":0,"x":827.5,"y":183,"wires":[["5f345e08.d8fbc","c08082da.318dc"]]},{"id":"c08082da.318dc","type":"http request","z":"6788a188.0be7a","name":"Dashboard GET","method":"GET","ret":"obj","url":"https://dashboard.meraki.com/api/v0/networks/my_net_id/devices","tls":"","x":1081,"y":184,"wires":[["e1a3fc48.842fe","40ba703b.099c4"]]},{"id":"e1a3fc48.842fe","type":"debug","z":"6788a188.0be7a","name":"Dashboard Response","active":true,"console":"false","complete":"payload","x":1230.5,"y":265,"wires":[]},{"id":"40ba703b.099c4","type":"function","z":"6788a188.0be7a","name":"Find device name by mac","func":"// This function parses the Meraki Dashboard API response\n// and creates a message to POST to a Spark room.\n// Fill in the roomId & bot_token with your own details\nclient = {}; //reset payload object for clarity\nclient.roomId = 'my_spark_room_id';\nresult = msg.payload;\nap_mac = msg.ap_mac;\nconsole.log('AP MAC= ' + ap_mac);\nmsg.payload = {};\nfor (var key in result) {\n  if (result.hasOwnProperty(key)) {\n    var dev_mac = result[key].mac;\n    if (dev_mac === ap_mac) {\n      var dev_name = result[key].name;\n      client.text = 'Device name: ' + dev_name;\n  }\n }\n}\nmsg.payload.body = client;\nreturn msg;","outputs":1,"noerr":0,"x":1330.5,"y":184,"wires":[["e7fa8179.c3ff8","9e85e3b7.a3f2d"]]},{"id":"5f345e08.d8fbc","type":"debug","z":"6788a188.0be7a","name":"Dashboard GET params","active":true,"console":"false","complete":"true","x":944.5,"y":263,"wires":[]},{"id":"e7fa8179.c3ff8","type":"debug","z":"6788a188.0be7a","name":"find name function debug","active":true,"console":"false","complete":"true","x":1499.5,"y":264,"wires":[]},{"id":"efd4d1d2.20f3c","type":"Spark API","z":"6788a188.0be7a","profileConfig":"dfc7a292.69d53","apiUrl":"http://127.0.0.1:1880/api/cisco_spark_v1.json","resource":"Messages","method":"createMessage","name":"post cmx details","x":1284.5,"y":32,"wires":[["b08e64ac.a47378"]]},{"id":"9e85e3b7.a3f2d","type":"Spark API","z":"6788a188.0be7a","profileConfig":"dfc7a292.69d53","apiUrl":"http://127.0.0.1:1880/api/cisco_spark_v1.json","resource":"Messages","method":"createMessage","name":"post device name","x":1426,"y":358,"wires":[["89f534ed.321968"]]},{"id":"89f534ed.321968","type":"debug","z":"6788a188.0be7a","name":"POST dev name","active":true,"console":"false","complete":"true","x":1537.5,"y":434,"wires":[]},{"id":"413d57e0.3cb088","type":"meraki-cmx-settings","z":"6788a188.0be7a","name":"Meraki CMX"},{"id":"dfc7a292.69d53","type":"Spark Authentication","z":"6788a188.0be7a","name":"CMX Bot"}]
knakashima

Flow Info

created 2 months, 3 weeks ago

Node Types

Core
  • debug (x8)
  • file (x1)
  • function (x3)
  • http request (x1)
  • json (x3)
  • switch (x1)
Other

Tags

  • cisco
  • meraki
  • cmx
  • spark
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option