Cisco Meraki and Spark Demo Flows

A collection of flows to demonstrate several Cisco APIs for Meraki and Spark.

Flows

  • Meraki Scanning API
  • Meraki Captive Portal API
  • Meraki Dashboard API
  • Spark API

Usage

Each flow demonstrates the core mechanics of the API, which is ideal for beginning any project. The Info window for each flow will include a more detail overview along with instructions for configurations.

It is expected that you have a Cisco Meraki network and a Cisco Spark developer account to make use of these flows.

[{"id":"677ea62c.d4fe48","type":"tab","label":"Location API","disabled":false,"info":"# Meraki Location Scanning API \n\nThis flow demonstrates the basic mechanics of\nthe Scanning API web service. It will provide\ntwo API endpoints to interact with the Meraki API.\n\n`[get] /scanning`\nResponds to Meraki with the `validator` to \nvalidate the receiver.\n\n`[post] /scanning`\nReceives observation data from a Meraki AP. \nThe data is compared against the `secret` to\nvalidate the sender.\n\nA **link** node is then used to demonstrate how the\ndata can be analyzed and routed. \n\nIt is now up to you to do something awesome with\nthis information. Add a map, database or IoT\ncontrol...\n\nhttp://developers.meraki.com\nhttp://www.InternetOfLEGO.com\n\n"},{"id":"ad289f82.1f3ed","type":"tab","label":"Captive Portal","disabled":false,"info":"# Meraki External Captive Portal (ExCap)\n\nThis flow demonstrates the mechanics of working\nwith the Meraki Captive Portal API. \n\nThere are two \"Splash Page\" types with Meraki, \n**Click-through** and **Sign on**.\n\nThere are two flows to highlight the differences\nbut they may share common assets, such as a ToS.\n\n# Overview\n\n## Click-through\nThe click-through splash page simply expects\nto receive a request from Meraki which includes\nthe parameters of the authenticating client. It\nwill then parse the parameters, present a splash\npage and eventually redirect the client to \nthe `base_grant_url` once the form is submitted\n\n`[get] /excapClick`\nThe path Meraki will use as the **Custom Splash \nURL**\nThe client will see the splash page, which is \nprovided by the **HTML (template)** node.\n\n`[post] /excapClickLogin`\nThe URL that will be the web form's \"Action\".\nIt will then send the user form data to the flow\nand redirect the client to the `base_grant_url`.\n\n\n## Sign On\n\n`[get] /excapSignOn`\nThe path Meraki will use as the **Custom Splash\nURL**\nThe client will see the splash page, which is \nprovided by the **HTML (template)** node.\nThe form will include a Meraki provided `login_url`, which will\nbe set as the \"Action\" on the web form.\n\n`[post] /excapSuccess`\nThis path Meraki will use as the continue_url\nfor the client once they have succesfully logged\nin. Use this to have a final interaction with the\nclient.\n\n\n# Usage\n\nLogin to Meraki Dashboard and configure the \ncustom Splash Page URL to one of these methods.\n* http://yourserver:1880/excapClick\n* http://yourserver:1880/excapSignOn\n\nIf using Sign On, you must also configure WiFi \nUser accounts that are authorized for the SSID.\n\n# More Info\n\nhttps://meraki.cisco.com/lib/pdf/meraki_whitepaper_captive_portal.pdf\nhttp://developers.meraki.com\nhttp://www.InternetOfLEGO.com\n\n\n"},{"id":"df4cef7b.9e8a6","type":"tab","label":"Dashboard API - Admin Form","disabled":false,"info":"# Dashboard API - Admin Form\nA flow to create and administrator for a\nMeraki network. \n\n## Configure\n* Update the Environment Variables node\n**Env Variables: UPDATE THIS**\n\nYour API KEY (Keep this secret. Do not publish)\n```\nconst API_KEY = 'YourAPIKey'; \n```\n\nYour Shard number. https://n149.meraki.com , \nwhere n149 represent the shard number. \nYou can see this URL when you login to your Meraki Dashboard.\nThis is defined to avoid redirect issues.\n```\nvar shard = 'n149'; \n```\nYour Default Organization ID (optional)\n```\nvar orgId = ''; \n```\n\n# Usage\n\n`[get /meraki/admins]`\nPresents the web form.\n\nNavigate to\n* http://localhost:1880/meraki/admins\n\n\n# Verify\nIf the user was created properly, the web page\nwill display the committed details. You can\nverify the account in the Meraki Dashboard\n\n# More Info\n\nhttp://developers.meraki.com\nhttp://www.InternetOfLEGO.com\n\n\n\n\n\n\n\n\n\nThis flow can be adjusted to\nleaverage the many other API endpoints available.\n"},{"id":"2a0bfedb.19a932","type":"tab","label":"Cisco Spark","disabled":false,"info":"# Cisco Spark API Demo\nCollects data from **input** nodes and sends it\nto a Spark room.\n\n# Configure\nUpdate the **function** node \"Spark Send Message (API) - UPDATE\"\n* Set Your Variables\n**Your Access Token**\n```\nvar accessToken = \"EnterYourAccessTokenHere\";\n```\n**Your Room ID**\n```\nvar roomId = \"EnterYourRoomIDHere\";\n```\n\n# Usage\nThe link nodes from other flows can be sent to \nthe \"Cisco Spark\" **link in* node. As messages\narrive, they will be sent to the Spark room. The\nmessages will typically be formatted before sending\nto adjust for the Spark message room layout.\n\n# More Info\n\nLearn about getting an accessToken and using their \nonline API console to obtain the roomId.\nhttps://developer.ciscospark.com/\n\nhttps://developer.ciscospark.com/"},{"id":"b8bba6c6.bce9a8","type":"debug","z":"677ea62c.d4fe48","name":"scanning Data","active":true,"console":"false","complete":"true","x":620,"y":260,"wires":[]},{"id":"57a8733a.3c233c","type":"http in","z":"677ea62c.d4fe48","name":"","url":"/scanning","method":"get","upload":false,"swaggerDoc":"","x":120,"y":120,"wires":[["1c20f2f8.bd123d","66409674.4d78a8"]]},{"id":"1c20f2f8.bd123d","type":"function","z":"677ea62c.d4fe48","name":"validate-validator - UPDATE","func":"\n//Set the validator variable to match the Meraki network\n//The URL will not need to include the validator string, for enhanced security\n//~~~~~~~~~\nvalidator = \"yourValidatorKey\";\n\n// For testing purposes:\n// This validator accepts a message with the validator defined in the URL\n// Example: http://yourserver:1880/cmx?validator=8e0846499d9a3f6d23f7bogusd55b9d6325035f5\n\n//~~~~~~~~~\n//validator = msg.payload.validator;\n\n\nmsg.payload = validator;\nreturn msg;","outputs":1,"noerr":0,"x":360,"y":120,"wires":[["43c770fa.58d81","77f86e16.e7509"]]},{"id":"43c770fa.58d81","type":"http response","z":"677ea62c.d4fe48","name":"scanning response","statusCode":"","headers":{},"x":610,"y":120,"wires":[]},{"id":"66409674.4d78a8","type":"debug","z":"677ea62c.d4fe48","name":"scanning validator raw","active":true,"console":"false","complete":"payload","x":340,"y":160,"wires":[]},{"id":"d4e4132e.b05ed","type":"comment","z":"677ea62c.d4fe48","name":"Location API core mechanics","info":"CMX/Location API core functions","x":140,"y":80,"wires":[]},{"id":"77f86e16.e7509","type":"debug","z":"677ea62c.d4fe48","name":"scanning Validator","active":true,"console":"false","complete":"payload","x":610,"y":160,"wires":[]},{"id":"281beb10.b703a4","type":"function","z":"677ea62c.d4fe48","name":"validate-secret - UPDATE","func":"// This node verifies that the secrect configured\n// on the Meraki Dashboard Network, matches.\n//var secret = \"merakilous\"; \nvar secret = \"supersecret\";\n\nif(msg.payload.secret == secret){\n    return msg;\n}else{\n    // Secret is invalid\n    \n}\n\n","outputs":1,"noerr":0,"x":350,"y":220,"wires":[["b8bba6c6.bce9a8","be58f84f.5d0bc8","dff10141.0795e"]]},{"id":"78fb178.88f0ae8","type":"debug","z":"677ea62c.d4fe48","name":"scanning post","active":false,"console":"false","complete":"payload","x":320,"y":260,"wires":[]},{"id":"be58f84f.5d0bc8","type":"http response","z":"677ea62c.d4fe48","name":"cmx response","x":600,"y":220,"wires":[]},{"id":"c188b3e0.8cf98","type":"comment","z":"677ea62c.d4fe48","name":"Meraki Location API - Using Native Nodes -- README","info":"This flow demonstrates how to interact with the\nMeraki CMX (Scanning) Location API.\n\n# Be sure to update the validator and secret\n\nThe \"CMX core functions\" consist of two REST endpoints.\nThe 'get' enpoint waits to hear from Meraki,\nwhich will send a request to this server. The flow will \nthen respond with the \"validator\", which was defined in \nthe Meraki Dashboard. If the correct validator is returned, \nMeraki has confirmed that it is sending location data to \nthe correct server.\n\nThe second enpoint accepts 'post' requests, which will\nbe receiving the JSON structured location data from Meraki.\n\nOnce the data has arrived in the `msg.payload`, the results\nwill be stored in a local MongoDB database if the supplied secret\nmatches what's defined in the global variables. This is to ensure\nthat it is in fact Meraki sending this data. \n\nThe data can then be retrieved by opening the other 'get' endpoints, such \nas /showcmxNative.\n\n","x":461,"y":27,"wires":[]},{"id":"785d08d9.f64b88","type":"debug","z":"ad289f82.1f3ed","name":"excapSignOn raw data","active":true,"console":"false","complete":"payload","x":200,"y":480,"wires":[]},{"id":"34da95b.df9996a","type":"http in","z":"ad289f82.1f3ed","name":"","url":"/excapSignOn","method":"get","swaggerDoc":"","x":150,"y":440,"wires":[["785d08d9.f64b88","6473d59c.1930cc"]]},{"id":"65701c35.2be8d4","type":"http response","z":"ad289f82.1f3ed","name":"","x":770,"y":160,"wires":[]},{"id":"a9d9b12.51e395","type":"http in","z":"ad289f82.1f3ed","name":"","url":"/excapClick","method":"get","swaggerDoc":"","x":140,"y":120,"wires":[["50a65a91.2cbdc4","f6542a67.63cfa8"]]},{"id":"de9b1754.8c32e8","type":"http response","z":"ad289f82.1f3ed","name":"","x":770,"y":480,"wires":[]},{"id":"8ca6d108.2de8c","type":"template","z":"ad289f82.1f3ed","name":"HTML Login Page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html>\n<head>\n{{{payload.css}}}\n\n\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <title>Registration Form</title>\n\n</head>\n<body>\n\n    \n\n\n    <div class=\"main-content\">\n    <form class=\"form-register\" method=POST action={{payload.login_url}} />\n        <input type=\"hidden\" name=\"continue_url\" value={{payload.continue_url}} />\n        <input type=\"hidden\" name=\"host\" value ={{host}}>\n        <input type=\"hidden\" name=\"base_grant_url\" value = {{payload.base_grant_url}}/>\n        <input type=\"hidden\" name=\"user_continue_url\" value = {{payload.user_continue_url}}/>\n        <input type=\"hidden\" name=\"node_mac\" value = {{payload.node_mac}}/>\n        <input type=\"hidden\" name=\"client_ip\" value = {{payload.client_ip}}/>\n        <input type=\"hidden\" name=\"client_mac\" value = {{payload.client_mac}}/>\n        <input type=\"hidden\" name=\"success_url\" value={{payload.continue_url}} />\n\n            <div class=\"form-register-with-email\">\n\n                <div class=\"form-white-background\">\n\n                    <div class=\"form-title-row\">\n                        <h1>Login to WiFi</h1>\n                    </div>\n\n                    <div class=\"form-row\">\n                        <label>\n                            <span>Email</span>\n                            <input type=\"email\" name=\"email\" required>\n                        </label>\n                    </div>\n\n                    <div class=\"form-row\">\n                        <label>\n                            <span>Password</span>\n                            <input type=\"password\" name=\"password\" required>\n                        </label>\n                    </div>\n\n                    <div class=\"form-row\">\n                        <label class=\"form-checkbox\">\n                            <input type=\"checkbox\" name=\"checkbox\" checked value=\"accepted\" required>\n                            <span>I agree to the <a href=\"/excapTerms\" target\"_blank\">terms and conditions</a></span>\n                        </label>\n                    </div>\n\n                    <div class=\"form-row\">\n                        <button type=\"submit\">Register</button>\n                    </div>\n\n                </div>\n\n            </div>\n\n            <!-- Future Social Features\n            <div class=\"form-sign-in-with-social\">\n\n                <div class=\"form-row form-title-row\">\n                    <span class=\"form-title\">Sign in with</span>\n                </div>\n\n                <a href=\"#\" class=\"form-google-button\">Google</a>\n                <a href=\"#\" class=\"form-facebook-button\">Facebook</a>\n                <a href=\"#\" class=\"form-twitter-button\">Twitter</a>\n                \n\n            </div>\n            -->\n\n        </form>\n                Your IP: {{payload.client_ip}}\n                <br>\n                Your MAC: {{payload.client_mac}}\n\n    </div>\n\n</body>\n\n</html>","x":570,"y":480,"wires":[["de9b1754.8c32e8"]]},{"id":"50a65a91.2cbdc4","type":"function","z":"ad289f82.1f3ed","name":"Set login_url","func":"msg.payload.login_url = msg.payload.base_grant_url + '?continue_url=' + msg.payload.user_continue_url;\n\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":120,"wires":[["e8df823a.91a29"]]},{"id":"f6542a67.63cfa8","type":"debug","z":"ad289f82.1f3ed","name":"excapClick raw data","active":true,"console":"false","complete":"payload","x":200,"y":160,"wires":[]},{"id":"77600c5c.923134","type":"comment","z":"ad289f82.1f3ed","name":"Meraki Click-through, read more...","info":"Configure by placing this host server address\nfollowed by /excapClick in the Custom Splash Page URL\nsection of the Meraki Dashboard.\n\nie. https://yourserver:1880/excapClick\n\nBe sure to update the \"walled garden\" in the \nMeraki Dashboard with the IP address of your \nserver and any other resources required before\nlogin, such as your website, images, CSS, etc.\n\nThe HTML page can be modified as needed. \n","x":173,"y":79,"wires":[]},{"id":"a7287d8c.0b3f7","type":"comment","z":"ad289f82.1f3ed","name":"Meraki Sign-on, read more...","info":"Configure by placing this host server address\nfollowed by /excapClick in the Custom Splash Page URL\nsection of the Meraki Dashboard.\n\nie. https://yourserver:1880/excapSignOn\n\nBe sure to update the \"walled garden\" in the \nMeraki Dashboard with the IP address of your \nserver and any other resources required before\nlogin, such as your website, images, CSS, etc.\n\n","x":159,"y":396,"wires":[]},{"id":"56cef44d.b819fc","type":"template","z":"ad289f82.1f3ed","name":"HTML Splash Page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html>\n<head>\n{{{payload.css}}}\n\n\n\t<meta charset=\"utf-8\">\n\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n\t<title>Registration Form</title>\n\n</head>\n<body>\n\n\t\n\n\n    <div class=\"main-content\">\n\n        <form class=\"form-register\" method=POST action=\"/excapClickLogin\">\n                <input type=\"hidden\" name=\"continue_url\" value={{payload.continue_url}} />\n                <input type=\"hidden\" name=\"host\" value ={{host}}>\n                <input type=\"hidden\" name=\"base_grant_url\" value = {{payload.base_grant_url}}/>\n                <input type=\"hidden\" name=\"user_continue_url\" value = {{payload.user_continue_url}}/>\n                <input type=\"hidden\" name=\"node_mac\" value = {{payload.node_mac}}/>\n                <input type=\"hidden\" name=\"client_ip\" value = {{payload.client_ip}}/>\n                <input type=\"hidden\" name=\"client_mac\" value = {{payload.client_mac}}/>\n            \n            <div class=\"form-register-with-email\">\n\n                <div class=\"form-white-background\">\n\n                    <div class=\"form-title-row\">\n                        <h1>Login to WiFi</h1>\n                    </div>\n\n                    <div class=\"form-row\">\n                        <label>\n                            <span>Name</span>\n                            <input type=\"text\" name=\"name\" required>\n                        </label>\n                    </div>\n\n                    <div class=\"form-row\">\n                        <label>\n                            <span>Email</span>\n                            <input type=\"email\" name=\"email\" required>\n                        </label>\n                    </div>\n\n                    <div class=\"form-row\">\n                        <label class=\"form-checkbox\">\n                            <input type=\"checkbox\" name=\"checkbox\" checked value=\"accepted\" required>\n                            <span>I agree to the <a href=\"/excapTerms\" target\"_blank\">terms and conditions</a></span>\n                        </label>\n                    </div>\n\n                    <div class=\"form-row\">\n                        <button type=\"submit\">Register</button>\n                    </div>\n\n                </div>\n\n            </div>\n\n            <!-- Future Social Features\n            <div class=\"form-sign-in-with-social\">\n\n                <div class=\"form-row form-title-row\">\n                    <span class=\"form-title\">Sign in with</span>\n                </div>\n\n                <a href=\"#\" class=\"form-google-button\">Google</a>\n                <a href=\"#\" class=\"form-facebook-button\">Facebook</a>\n                <a href=\"#\" class=\"form-twitter-button\">Twitter</a>\n                \n\n            </div>\n            -->\n\n        </form>\n                Your IP: {{payload.client_ip}}\n                <br>\n                Your MAC: {{payload.client_mac}}\n\n    </div>\n\n</body>\n\n</html>\n\n","x":570,"y":160,"wires":[["65701c35.2be8d4"]]},{"id":"458d3034.3cab1","type":"http in","z":"ad289f82.1f3ed","name":"","url":"/excapClickLogin","method":"post","swaggerDoc":"","x":160,"y":220,"wires":[["bf6360ac.edf2f","1bb67572.8b7f0b","33ed0e9b.30d802"]]},{"id":"bf6360ac.edf2f","type":"function","z":"ad289f82.1f3ed","name":"Process login with 302 redirect","func":"// check if client has connected properly and received response from AP\nif (msg.payload.base_grant_url){\n    msg.statusCode = 302;\n    msg.headers = {\n      'Location': msg.payload.base_grant_url + '?continue_url=' + msg.payload.user_continue_url\n      //add other headers here...\n    };\n}else{\n    // error: send client back to login\n    msg.statusCode = 302;\n    msg.headers = {\n      'Location': '/excapNotConnected'\n      //add other headers here...\n    };\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":530,"y":220,"wires":[["b398ae98.80f2","dedaeb10.4019b8"]]},{"id":"b398ae98.80f2","type":"http response","z":"ad289f82.1f3ed","name":"","x":770,"y":220,"wires":[]},{"id":"1bb67572.8b7f0b","type":"debug","z":"ad289f82.1f3ed","name":"excapClick Login data","active":true,"console":"false","complete":"payload","x":200,"y":260,"wires":[]},{"id":"d07099d5.ea4b78","type":"comment","z":"ad289f82.1f3ed","name":"Store form data - read more...","info":"The login data now can be sent somehwere to be \nlogged. A link node is attached to easily use\nthis data in other parts of the flow.","x":500,"y":260,"wires":[]},{"id":"c912801f.475bc","type":"comment","z":"ad289f82.1f3ed","name":"Terms and Conditions","info":"","x":144,"y":750,"wires":[]},{"id":"d2e63925.ee6138","type":"comment","z":"ad289f82.1f3ed","name":"Success","info":"","x":106,"y":538,"wires":[]},{"id":"90642ce8.4089c","type":"http in","z":"ad289f82.1f3ed","name":"","url":"/excapTerms","method":"get","swaggerDoc":"","x":150,"y":800,"wires":[["ee81bdad.d7fb6"]]},{"id":"99ce6c82.9919b","type":"http response","z":"ad289f82.1f3ed","name":"","x":790,"y":840,"wires":[]},{"id":"70a86349.0fce0c","type":"template","z":"ad289f82.1f3ed","name":"HTML Terms and Conditions","field":"payload","format":"handlebars","template":"<head>\n    <title>Terms and Conditions</title>\n{{{payload}}}\n</head>\n<body>\n    <div class=\"container\">\n    <h1>Terms and Conditions</h1>\n    <ul>\n        <li>Please use our network responsibly. </li>\n        <li>Your computer's MAC and IP address will be logged.</li>\n    </ul>\n    </div>\n</body>\n\n\n","x":600,"y":840,"wires":[["99ce6c82.9919b"]]},{"id":"ab45d396.4b0c3","type":"http in","z":"ad289f82.1f3ed","name":"","url":"/excapSuccess","method":"get","swaggerDoc":"","x":150,"y":580,"wires":[["5edcbfb5.54958"]]},{"id":"53f0d212.921d5c","type":"http response","z":"ad289f82.1f3ed","name":"","x":790,"y":620,"wires":[]},{"id":"92561f5a.4c3a","type":"template","z":"ad289f82.1f3ed","name":"HTML Success","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<head>\n    <title>Success!</title>\n{{{payload.css}}}\n</head>\n<body>\n    <div class=\"container\">\n    <center><h1>Success!</h1>\n    <h2>You can now use the network.</h2>\n    </center>\n    </div>\n</body>\n\n\n","x":560,"y":620,"wires":[["53f0d212.921d5c"]]},{"id":"cfad1722.d9ae98","type":"comment","z":"ad289f82.1f3ed","name":"Error: Not connected to AP","info":"","x":160,"y":873,"wires":[]},{"id":"76363ad4.c8b6f4","type":"http in","z":"ad289f82.1f3ed","name":"","url":"/excapNotConnected","method":"get","swaggerDoc":"","x":170,"y":920,"wires":[["d6875b72.b0d4e8"]]},{"id":"a795eff5.4ed3","type":"http response","z":"ad289f82.1f3ed","name":"","x":790,"y":960,"wires":[]},{"id":"a00bfbd.6120608","type":"template","z":"ad289f82.1f3ed","name":"HTML Terms and Conditions","field":"payload","format":"handlebars","template":"<head>\n    <title>Error: Not Connected</title>\n{{{payload}}}\n</head>\n<body>\n    <div class=\"container\">\n    <h1>Error: Not Connected</h1>\n    <p>\n        Hmm, somethings not right. It appears you\n        are not connected to the correct wireless network.\n    </p>\n    </div>\n</body>\n\n\n","x":600,"y":960,"wires":[["a795eff5.4ed3"]]},{"id":"54c6abe3.5ef6b4","type":"comment","z":"ad289f82.1f3ed","name":"Cisco Meraki ExCap Service","info":"More information about the Meraki ExCap API:\nhttps://meraki.cisco.com/lib/pdf/meraki_whitepaper_captive_portal.pdf\n\nEnjoy!\n\nWritten by Cory Guynn, 2015.\nConsulting Engineer @ https://meraki.cisco.com/\nTechnical Blogger @ http://www.InternetOfLego.com","x":438.5,"y":31,"wires":[]},{"id":"e8df823a.91a29","type":"template","z":"ad289f82.1f3ed","name":"CSS Style","field":"payload.css","fieldType":"msg","format":"html","syntax":"mustache","template":"<style>\nhtml{\n    background-color: #f3f3f3;\n}\n\n.form-register{\n    max-width: 1000px;\n    width: 100%;\n    margin: 0 auto;\n\n    font: bold 14px sans-serif;\n    text-align: center;\n}\n\n.form-register-with-email{\n    position: relative;\n    display: inline-block;\n    vertical-align: top;\n    margin-right: 130px;\n    text-align: center;\n}\n\n.form-register-with-email .form-white-background{\n    width: 570px;\n    box-sizing: border-box;\n    background-color: #ffffff;\n    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);\n    padding: 60px 80px;\n    margin-bottom: 35px;\n}\n\n.form-register-with-email .form-row{\n    text-align: left;\n    margin-bottom: 23px;\n}\n\n.form-register-with-email .form-title-row{\n    text-align: center;\n    margin-bottom: 50px;\n}\n\n.form-register-with-email h1{\n    display: inline-block;\n    box-sizing: border-box;\n    color:  #4c565e;\n    font-size: 24px;\n    padding: 0 20px 15px;\n    border-bottom: 2px solid #6caee0;\n    margin: 0;\n}\n\n.form-register-with-email .form-row > label span{\n    display: inline-block;\n    box-sizing: border-box;\n    color:  #5f5f5f;\n    width: 125px;\n    text-align: right;\n    padding-right: 25px;\n}\n\n.form-register-with-email input{\n    color:  #5f5f5f;\n    box-sizing: border-box;\n    width: 230px;\n    box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.08);\n    padding: 12px 18px;\n    border: 1px solid #dbdbdb;\n}\n\n.form-register-with-email .form-checkbox input{\n    margin-left: 128px;\n    margin-right: 10px;\n    width: auto;\n    vertical-align: top;\n}\n\n.form-register-with-email .form-row .form-checkbox span{\n    font-size: 12px;\n    font-weight: normal;\n    display: inline-block;\n    text-align: left;\n    width: 220px;\n    margin: 0;\n}\n\n.form-register-with-email .form-checkbox span a{\n    text-decoration: none;\n    color:  #6caee0;\n}\n\n.form-register-with-email button{\n    display: block;\n    border-radius: 2px;\n    background-color:  #6caee0;\n    color: #ffffff;\n    font-weight: bold;\n    box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.08);\n    padding: 15px 35px;\n    border: 0;\n    margin: 55px auto 0;\n    cursor: pointer;\n}\n\n.form-register-with-email .form-log-in-with-existing{\n    text-decoration: none;\n    padding: 4px 8px;\n    font-weight: normal;\n    color: #7b9d62;\n    background-color: #d6f0c3;\n}\n\n\n.form-sign-in-with-social{\n    display: inline-block;\n    max-width: 180px;\n    box-sizing: border-box;\n    vertical-align: top;\n    text-align: center;\n    margin-top: 100px;\n}\n\n.form-sign-in-with-social .form-title-row{\n    margin-bottom: 50px;\n}\n\n.form-sign-in-with-social .form-title{\n    box-sizing: border-box;\n    color:  #4c565e;\n    font-size: 24px;\n    padding: 15px 20px;\n    border-bottom: 2px solid #6caee0;\n}\n\n.form-sign-in-with-social .form-google-button{\n    color:  #ffffff;\n    display: block;\n    width: 145px;\n    height: 40px;\n    font-size: 12px;\n    line-height: 40px;\n    background-color:  rgba(222, 110, 60, 0.9);\n    box-shadow: 1px 2px 2px 0 rgba(0, 0, 0, 0.08);\n    border-radius: 2px;\n    margin: 8px auto;\n    text-decoration: none;\n}\n\n.form-sign-in-with-social .form-facebook-button{\n    color:  #ffffff;\n    display: block;\n    width: 145px;\n    height: 40px;\n    font-size: 12px;\n    line-height: 40px;\n    background-color:  rgba(75, 136, 194, 0.9);\n    box-shadow: 1px 2px 2px 0 rgba(0, 0, 0, 0.08);\n    border-radius: 2px;\n    margin: 8px auto;\n    text-decoration: none;\n}\n\n.form-sign-in-with-social .form-twitter-button{\n    color:  #ffffff;\n    display: block;\n    width: 145px;\n    height: 40px;\n    font-size: 12px;\n    line-height: 40px;\n    background-color:  rgba(123, 195, 226, 0.9);\n    box-shadow: 1px 2px 2px 0 rgba(0, 0, 0, 0.08);\n    border-radius: 2px;\n    margin: 8px auto;\n    text-decoration: none;\n}\n\n/*\tMaking the form responsive. Remove these media queries\n    if you don't need the form to work on mobile devices. */\n\[email protected] (max-width: 900px) {\n\n    .form-register{\n        margin: 20px auto;\n    }\n\n    .form-register-with-email{\n        position: relative;\n        display: block;\n        margin: 0;\n    }\n\n    .form-register-with-email .form-white-background{\n        margin: 0 auto 32px;\n    }\n\n\n\n    .form-sign-in-with-social {\n        margin-top: 105px;\n    }\n\n}\n\[email protected] (max-width: 600px) {\n\n    .form-register-with-email .form-white-background{\n        width: 300px;\n        padding-left: 35px;\n        padding-right: 35px;\n    }\n\n    .form-register-with-email .form-row > label span{\n        display: block;\n        text-align: left;\n        padding: 0 0 10px;\n    }\n\n    .form-register-with-email input{\n        display: block;\n        margin: 0 auto;\n    }\n\n    .form-register-with-email .form-checkbox input{\n        display: inline-block;\n        margin-left: 0;\n    }\n\n    .form-register-with-email .form-checkbox span{\n        width: 200px !important;\n    }\n\n    .form-register-with-email:after{\n        bottom: -80px;\n        left: 50%;\n        margin-left: -25px;\n    }\n\n}\n</style>","x":540,"y":120,"wires":[["56cef44d.b819fc"]]},{"id":"6473d59c.1930cc","type":"template","z":"ad289f82.1f3ed","name":"CSS Style","field":"payload.css","fieldType":"msg","format":"html","syntax":"mustache","template":"<style>\nhtml{\n    background-color: #f3f3f3;\n}\n\n.form-register{\n    max-width: 1000px;\n    width: 100%;\n    margin: 0 auto;\n\n    font: bold 14px sans-serif;\n    text-align: center;\n}\n\n.form-register-with-email{\n    position: relative;\n    display: inline-block;\n    vertical-align: top;\n    margin-right: 130px;\n    text-align: center;\n}\n\n.form-register-with-email .form-white-background{\n    width: 570px;\n    box-sizing: border-box;\n    background-color: #ffffff;\n    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);\n    padding: 60px 80px;\n    margin-bottom: 35px;\n}\n\n.form-register-with-email .form-row{\n    text-align: left;\n    margin-bottom: 23px;\n}\n\n.form-register-with-email .form-title-row{\n    text-align: center;\n    margin-bottom: 50px;\n}\n\n.form-register-with-email h1{\n    display: inline-block;\n    box-sizing: border-box;\n    color:  #4c565e;\n    font-size: 24px;\n    padding: 0 20px 15px;\n    border-bottom: 2px solid #6caee0;\n    margin: 0;\n}\n\n.form-register-with-email .form-row > label span{\n    display: inline-block;\n    box-sizing: border-box;\n    color:  #5f5f5f;\n    width: 125px;\n    text-align: right;\n    padding-right: 25px;\n}\n\n.form-register-with-email input{\n    color:  #5f5f5f;\n    box-sizing: border-box;\n    width: 230px;\n    box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.08);\n    padding: 12px 18px;\n    border: 1px solid #dbdbdb;\n}\n\n.form-register-with-email .form-checkbox input{\n    margin-left: 128px;\n    margin-right: 10px;\n    width: auto;\n    vertical-align: top;\n}\n\n.form-register-with-email .form-row .form-checkbox span{\n    font-size: 12px;\n    font-weight: normal;\n    display: inline-block;\n    text-align: left;\n    width: 220px;\n    margin: 0;\n}\n\n.form-register-with-email .form-checkbox span a{\n    text-decoration: none;\n    color:  #6caee0;\n}\n\n.form-register-with-email button{\n    display: block;\n    border-radius: 2px;\n    background-color:  #6caee0;\n    color: #ffffff;\n    font-weight: bold;\n    box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.08);\n    padding: 15px 35px;\n    border: 0;\n    margin: 55px auto 0;\n    cursor: pointer;\n}\n\n.form-register-with-email .form-log-in-with-existing{\n    text-decoration: none;\n    padding: 4px 8px;\n    font-weight: normal;\n    color: #7b9d62;\n    background-color: #d6f0c3;\n}\n\n\n.form-sign-in-with-social{\n    display: inline-block;\n    max-width: 180px;\n    box-sizing: border-box;\n    vertical-align: top;\n    text-align: center;\n    margin-top: 100px;\n}\n\n.form-sign-in-with-social .form-title-row{\n    margin-bottom: 50px;\n}\n\n.form-sign-in-with-social .form-title{\n    box-sizing: border-box;\n    color:  #4c565e;\n    font-size: 24px;\n    padding: 15px 20px;\n    border-bottom: 2px solid #6caee0;\n}\n\n.form-sign-in-with-social .form-google-button{\n    color:  #ffffff;\n    display: block;\n    width: 145px;\n    height: 40px;\n    font-size: 12px;\n    line-height: 40px;\n    background-color:  rgba(222, 110, 60, 0.9);\n    box-shadow: 1px 2px 2px 0 rgba(0, 0, 0, 0.08);\n    border-radius: 2px;\n    margin: 8px auto;\n    text-decoration: none;\n}\n\n.form-sign-in-with-social .form-facebook-button{\n    color:  #ffffff;\n    display: block;\n    width: 145px;\n    height: 40px;\n    font-size: 12px;\n    line-height: 40px;\n    background-color:  rgba(75, 136, 194, 0.9);\n    box-shadow: 1px 2px 2px 0 rgba(0, 0, 0, 0.08);\n    border-radius: 2px;\n    margin: 8px auto;\n    text-decoration: none;\n}\n\n.form-sign-in-with-social .form-twitter-button{\n    color:  #ffffff;\n    display: block;\n    width: 145px;\n    height: 40px;\n    font-size: 12px;\n    line-height: 40px;\n    background-color:  rgba(123, 195, 226, 0.9);\n    box-shadow: 1px 2px 2px 0 rgba(0, 0, 0, 0.08);\n    border-radius: 2px;\n    margin: 8px auto;\n    text-decoration: none;\n}\n\n/*\tMaking the form responsive. Remove these media queries\n    if you don't need the form to work on mobile devices. */\n\[email protected] (max-width: 900px) {\n\n    .form-register{\n        margin: 20px auto;\n    }\n\n    .form-register-with-email{\n        position: relative;\n        display: block;\n        margin: 0;\n    }\n\n    .form-register-with-email .form-white-background{\n        margin: 0 auto 32px;\n    }\n\n\n\n    .form-sign-in-with-social {\n        margin-top: 105px;\n    }\n\n}\n\[email protected] (max-width: 600px) {\n\n    .form-register-with-email .form-white-background{\n        width: 300px;\n        padding-left: 35px;\n        padding-right: 35px;\n    }\n\n    .form-register-with-email .form-row > label span{\n        display: block;\n        text-align: left;\n        padding: 0 0 10px;\n    }\n\n    .form-register-with-email input{\n        display: block;\n        margin: 0 auto;\n    }\n\n    .form-register-with-email .form-checkbox input{\n        display: inline-block;\n        margin-left: 0;\n    }\n\n    .form-register-with-email .form-checkbox span{\n        width: 200px !important;\n    }\n\n    .form-register-with-email:after{\n        bottom: -80px;\n        left: 50%;\n        margin-left: -25px;\n    }\n\n}\n</style>","x":540,"y":440,"wires":[["8ca6d108.2de8c"]]},{"id":"5edcbfb5.54958","type":"template","z":"ad289f82.1f3ed","name":"CSS Style","field":"payload.css","fieldType":"msg","format":"html","syntax":"mustache","template":"<style type=\"text/css\">\n.container{\n    max-width: 500px;\n    padding: 10px 20px;\n    background: #f4f7f8;\n    margin: 10px auto;\n    padding: 20px;\n    background: #f4f7f8;\n    border-radius: 8px;\n    font-family: Georgia, \"Times New Roman\", Times, serif;\n}\n</style>","x":540,"y":580,"wires":[["92561f5a.4c3a"]]},{"id":"ee81bdad.d7fb6","type":"template","z":"ad289f82.1f3ed","name":"CSS Style","field":"payload","format":"html","template":"<style type=\"text/css\">\n.container{\n    max-width: 500px;\n    padding: 10px 20px;\n    background: #f4f7f8;\n    margin: 10px auto;\n    padding: 20px;\n    background: #f4f7f8;\n    border-radius: 8px;\n    font-family: Georgia, \"Times New Roman\", Times, serif;\n}\n</style>","x":540,"y":800,"wires":[["70a86349.0fce0c"]]},{"id":"d6875b72.b0d4e8","type":"template","z":"ad289f82.1f3ed","name":"CSS Style","field":"payload","format":"html","template":"<style type=\"text/css\">\n.container{\n    max-width: 500px;\n    padding: 10px 20px;\n    background: #f4f7f8;\n    margin: 10px auto;\n    padding: 20px;\n    background: #f4f7f8;\n    border-radius: 8px;\n    font-family: Georgia, \"Times New Roman\", Times, serif;\n}\n</style>","x":540,"y":920,"wires":[["a00bfbd.6120608"]]},{"id":"3d594725.7bf6d8","type":"comment","z":"ad289f82.1f3ed","name":"Supporting Pages","info":"","x":95,"y":689,"wires":[]},{"id":"dedaeb10.4019b8","type":"debug","z":"ad289f82.1f3ed","name":"Process Login","active":true,"console":"false","complete":"payload","x":740,"y":260,"wires":[]},{"id":"79addb9.0d8b624","type":"inject","z":"df4cef7b.9e8a6","name":"Create Admin","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":110,"y":460,"wires":[["90a5f553.073318"]]},{"id":"6148046.a21effc","type":"http request","z":"df4cef7b.9e8a6","name":"","method":"use","ret":"obj","url":"","tls":"","x":810,"y":500,"wires":[["c1d9ec8e.13398","a63968ae.9d5ec8","bf683a1b.a05c48"]]},{"id":"c1d9ec8e.13398","type":"debug","z":"df4cef7b.9e8a6","name":"Create Meraki Admin","active":true,"console":"false","complete":"payload","x":800,"y":560,"wires":[]},{"id":"13240a70.f9ee86","type":"http in","z":"df4cef7b.9e8a6","name":"","url":"/meraki/admins","method":"get","swaggerDoc":"","x":110,"y":220,"wires":[["14d5829a.233c2d"]]},{"id":"14d5829a.233c2d","type":"function","z":"df4cef7b.9e8a6","name":"msg.url = \"/meraki/newAdmin\";","func":"msg.url = \"/meraki/newAdmin\";\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":220,"wires":[["9a20c4fa.3075d8"]]},{"id":"3361376e.9dac98","type":"template","z":"df4cef7b.9e8a6","name":"CSS","field":"payload.style","fieldType":"msg","format":"css","syntax":"mustache","template":"input[type=text], select {\n    width: 100%;\n    padding: 12px 20px;\n    margin: 8px 0;\n    display: inline-block;\n    border: 1px solid #ccc;\n    border-radius: 4px;\n    box-sizing: border-box;\n}\n\ninput[type=email], select {\n    width: 100%;\n    padding: 12px 20px;\n    margin: 8px 0;\n    display: inline-block;\n    border: 1px solid #ccc;\n    border-radius: 4px;\n    box-sizing: border-box;\n}\n\ninput[type=submit] {\n    width: 50%;\n    background-color: #4CAF50;\n    color: white;\n    padding: 14px 20px;\n    margin: 8px 0;\n    border: none;\n    border-radius: 4px;\n    cursor: pointer;\n}\n\ninput[type=submit]:hover {\n    background-color: #45a049;\n}\n\ndiv form {\n    border-radius: 5px;\n    background-color: #f2f2f2;\n    padding: 20px;\n}\n\n.adminform {\n    width: 90%;\n}\n","x":370,"y":300,"wires":[["26a695e6.2d9f0a"]]},{"id":"9a20c4fa.3075d8","type":"template","z":"df4cef7b.9e8a6","name":"JavaScript","field":"payload.script","fieldType":"msg","format":"javascript","syntax":"mustache","template":"\n$(document).ready(function(e) {\n    \n    \n    $.ajax({\n        url: 'api/organizations',\n        type: 'get',\n        datatype: 'json',\n        success: function(data){\n            var toAppend = '';\n            //if(typeof data === 'object'){\n                for(var i=0;i<data.length;i++){\n                    toAppend += '<option value='+data[i][\"id\"]+'>'+data[i]['name']+'</option>';\n                }\n            //}\n            console.log(\"orgs toAppend\",toAppend);\n            $('#orgs').append(toAppend);\n        }\n    });\n    \n    $(\"form[ajax=true]\").submit(function(e) {\n        \n        e.preventDefault();\n        \n        var form_data = $(this).serialize();\n        var form_url = $(this).attr(\"action\");\n        var form_method = $(this).attr(\"method\").toUpperCase();\n        \n        var orgId = $(\"#orgs\").val();\n        \n        $(\"#loadingimg\").show();\n        \n        $.ajax({\n            url: form_url+\"/\"+orgId, \n            type: form_method,       \n            data: form_data,     \n            cache: false,\n            success: function(data){\n                \n                // Create simple HTML response\n                var response = \n                    \"<div>\"+\n                        \"<h2>Created Admin!</h2>\"+\n                        \"<h4>Name</h4><p>\"+data.name+\"</p>\"+\n                        \"<h4>E-Mail</h4><p>\"+data.email+\"</p>\"+\n                        \"<h4>ID</h4><p>\"+data.id+\"</p>\"+\n                        \"<h4>Tags</h4><p>\"+data.tags+\"</p>\"+\n                        \"<h4>Org Access</h4><p>\"+data.orgAccess+\"</p>\"+\n                    \"</div>\";\n                \n                console.log(\"response: \"+JSON.stringify(data));\n                \n                \n                $(\"#result\").html(response); \n                \n                $(\"#loadingimg\").hide();\n                $( '#createadmin' ).each(function(){\n                    this.reset();\n                });\n            },\n            error: function(error){\n                $(\"#result\").html(error.responseText);\n            }\n        });    \n        \n    });\n    \n});","x":390,"y":260,"wires":[["3361376e.9dac98"]]},{"id":"e45abefe.e7999","type":"comment","z":"df4cef7b.9e8a6","name":"Create Admin Form","info":"","x":110,"y":140,"wires":[]},{"id":"c6dd9efc.f7c6f","type":"comment","z":"df4cef7b.9e8a6","name":"Form Submission URL","info":"","x":420,"y":180,"wires":[]},{"id":"e0d2ebf9.e0f3b8","type":"http response","z":"df4cef7b.9e8a6","name":"","x":710,"y":220,"wires":[]},{"id":"26a695e6.2d9f0a","type":"template","z":"df4cef7b.9e8a6","name":"HTML","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<html>\n  <head>\n    <title>Meraki Dasbhoard API - Admin User</title>\n    <meta name=\"viewport\" content=\"initial-scale=1.0, user-scalable=no\">\n    <meta charset=\"utf-8\">\n    <!--\n    <script src=\"http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js\"></script>\n    -->\n    <script\n      src=\"https://code.jquery.com/jquery-3.2.1.min.js\"\n      integrity=\"sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=\"\n      crossorigin=\"anonymous\"></script>\n      \n    <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\" integrity=\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u\" crossorigin=\"anonymous\">  \n    <script>{{{payload.jquery}}}</script>\n    <style>{{{payload.style}}}</style>\n  </head>\n  \n<div class=\"container\">\n    <h2>Create a Meraki Dashboard Administrator</h2>\n    <h4><a href=\"http://developers.meraki.com\">Meraki Developers Portal</a></h4>\n\n<div class=\"col-md-6\">\n<div class=\"adminform\">\n  <form id=\"createadmin\" method=\"post\" action=\"{{url}}\" ajax=\"true\">\n    <select id=\"orgs\" name=\"orgs\">\n        <option>Select Organization..</option>\n    </select>\n    <label>Contact Details</label>\n    <input type=\"text\" id=\"fname\" name=\"firstname\" placeholder=\"First Name\" required=true/>\n    <input type=\"text\" id=\"lname\" name=\"lastname\" placeholder=\"Last Name\" required=true/>\n    <input type=\"email\" id=\"email\" name=\"email\" placeholder=\"E-mail\" required=true/>\n    <br>\n\n    <label>Organization Permissions</label>\n    <select id=\"orgAccess\" name=\"orgAccess\" placeholder=\"Org Access\">\n      <option value=\"full\">full</option>\n      <option value=\"read-only\">read-only</option>\n    </select>\n    \n  \n    <input type=\"submit\" class=\"button\" value=\"Submit\">\n  </form>\n</div>\n</div>\n\n<div class=\"col-md-6\">\n    <span id=\"result\"></span>\n</div>\n</div>\n</body>\n</html>\n<script>{{{payload.script}}}</script>","x":370,"y":340,"wires":[["e0d2ebf9.e0f3b8"]]},{"id":"90a5f553.073318","type":"function","z":"df4cef7b.9e8a6","name":"Sample Data","func":"msg.payload = {\n    \"name\": \"NodeRED Test\",\n    \"email\": \"[email protected]\",\n    \"orgAccess\": \"full\"\n };\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":460,"wires":[["658633d6.29675c"]]},{"id":"de7d5e9f.e2d37","type":"http in","z":"df4cef7b.9e8a6","name":"","url":"/meraki/newAdmin/:orgId","method":"post","swaggerDoc":"","x":170,"y":500,"wires":[["c7f04a08.e17858"]]},{"id":"a63968ae.9d5ec8","type":"http response","z":"df4cef7b.9e8a6","name":"","x":990,"y":500,"wires":[]},{"id":"c7f04a08.e17858","type":"function","z":"df4cef7b.9e8a6","name":"form data","func":"// Convert form data into Meraki API expected JSON\nmsg.orgId = msg.req.params.orgId;\nmsg.payload = {\n    name: msg.req.body.firstname + \" \" + msg.req.body.lastname,\n    email: msg.req.body.email,\n    orgAccess: msg.req.body.orgAccess\n};\n\nreturn msg;","outputs":1,"noerr":0,"x":380,"y":500,"wires":[["6e58b298.df54dc","658633d6.29675c"]]},{"id":"6e58b298.df54dc","type":"debug","z":"df4cef7b.9e8a6","name":"newAdmin Form Data","active":true,"console":"false","complete":"payload","x":296.5555725097656,"y":558.6666631698608,"wires":[]},{"id":"3aa6f51d.995a5a","type":"comment","z":"df4cef7b.9e8a6","name":"Meraki Dashboard API Sample App","info":"Create a Meraki Dasbhoard Admin with a form.\n\nTo open the web form, navigate to \n\nhttp://yourserver:1880/meraki/admins\n","x":459,"y":24.000003814697266,"wires":[]},{"id":"423814f0.814fec","type":"inject","z":"df4cef7b.9e8a6","name":"List Organizations","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":130,"y":660,"wires":[["b24014af.340c48"]]},{"id":"b24014af.340c48","type":"function","z":"df4cef7b.9e8a6","name":"Meraki API - List Organizations","func":"// Set Variables\nvar apiKey = global.get('apiKey');\n\n\n\n// API Call\nmsg.headers = {\n    \"X-Cisco-Meraki-API-Key\": apiKey\n};\n\nmsg.url = \"https://dashboard.meraki.com/api/v0/organizations\";\nmsg.method = 'get';\n\nreturn msg;","outputs":1,"noerr":0,"x":430,"y":660,"wires":[["8a194808.4a86c8"]]},{"id":"8a194808.4a86c8","type":"http request","z":"df4cef7b.9e8a6","name":"","method":"use","ret":"txt","url":"","tls":"","x":670,"y":660,"wires":[["2c98d2eb.d8633e"]]},{"id":"2d289531.de177a","type":"http in","z":"df4cef7b.9e8a6","name":"","url":"/meraki/api/organizations","method":"get","swaggerDoc":"","x":160,"y":700,"wires":[["b24014af.340c48"]]},{"id":"2c98d2eb.d8633e","type":"function","z":"df4cef7b.9e8a6","name":"Large Int Helper","func":"// Convert the org number to a string so that it does not become corrupted \n// when the JSON conversion happens.   \nmsg.payload = msg.payload.replace(/\"id\":(.*?)(?:,)/g,\"\\\"id\\\"\\:\\\"$1\\\"\\,\");\nconsole.log(\"new json with regex replace \",msg.payload);\nreturn msg","outputs":1,"noerr":0,"x":860,"y":660,"wires":[["72aaaadf.6629e4"]]},{"id":"72aaaadf.6629e4","type":"json","z":"df4cef7b.9e8a6","name":"","x":830,"y":700,"wires":[["1327fcac.689453","df97dd88.adcf"]]},{"id":"1327fcac.689453","type":"http response","z":"df4cef7b.9e8a6","name":"","x":1030,"y":700,"wires":[]},{"id":"df97dd88.adcf","type":"debug","z":"df4cef7b.9e8a6","name":"List Orgs JSON","active":true,"console":"false","complete":"payload","x":1000,"y":740,"wires":[]},{"id":"863a25.ead305d8","type":"inject","z":"df4cef7b.9e8a6","name":"Sets Defaults - Injected at start","topic":"","payload":"Setting Environment Variables","payloadType":"str","repeat":"","crontab":"","once":true,"x":170,"y":60,"wires":[["e71ea271.f1c52"]]},{"id":"e71ea271.f1c52","type":"function","z":"df4cef7b.9e8a6","name":"Env Variables: UPDATE THIS","func":"//  ###### User Defined Variables ######\n\nconst API_KEY = 'YourAPIkey'; //Your API KEY (Keep this secret. Do not publish)\nvar shard = 'n###'; // https://n149.meraki.com , where n149 represent the shard number. This is defined to avoid redirect issues.\nvar orgId = 'YourOrgId'; // Your default Organization ID\n\n\n\n\n\n// ###### Set and Get Global variables ######\n\n\n// Assign to Global Context \nglobal.set('apiKey', API_KEY); // pulling API key via \"secret\" node. This avoides publishing the API key when sharing the flow\nglobal.set('shard', shard);\n\nglobal.set('orgId', orgId);\n\n\nmsg.payload = \"settings set\";\nreturn msg;","outputs":1,"noerr":0,"x":440,"y":60,"wires":[["65800103.4d284"]]},{"id":"65800103.4d284","type":"debug","z":"df4cef7b.9e8a6","name":"Flow Env Variables","active":true,"console":"false","complete":"true","x":750,"y":60,"wires":[]},{"id":"658633d6.29675c","type":"function","z":"df4cef7b.9e8a6","name":"Meraki API - Create Admin","func":"// Set Variables\nvar apiKey = global.get('apiKey');\nvar shard = global.get('shard');\n//var orgId = global.get('orgId'); // set via global variables\nvar orgId = msg.orgId; //set via request parameter\n\n// API Call\nmsg.headers = {\n    \"X-Cisco-Meraki-API-Key\": apiKey\n};\nmsg.url = \"https://\"+shard+\".meraki.com/api/v0/organizations/\"+orgId+\"/admins\";\nmsg.method = 'post';\nreturn msg;","outputs":1,"noerr":0,"x":600,"y":500,"wires":[["6148046.a21effc"]]},{"id":"21add94b.8fd866","type":"comment","z":"df4cef7b.9e8a6","name":"APIs","info":"","x":70,"y":400,"wires":[]},{"id":"33ed0e9b.30d802","type":"link out","z":"ad289f82.1f3ed","name":"excapClick Login","links":["bc43f620.69aed8","d8549fe0.db69b"],"x":395,"y":300,"wires":[]},{"id":"9e35271b.7f9d68","type":"http request","z":"2a0bfedb.19a932","name":"","method":"use","ret":"txt","url":"","tls":"","x":530,"y":140,"wires":[["3590c06a.69b73"]]},{"id":"fdea18dc.a4b788","type":"function","z":"2a0bfedb.19a932","name":"Spark Send Message (API) - UPDATE","func":"// ******************\n// Set Your Variables\n// ******************\nvar accessToken = \"EnterYourAccessTokenHere\"; // Your Access Token\nvar roomId = \"EnterYourRoomIDHere\"; // Your Room ID\n\n// -- -- -- -- -- --\n// Build API Call\nmsg.url = \"https://api.ciscospark.com/v1/messages\";\nmsg.headers = { \n    'content-type': 'application/json',\n    'authorization': 'Bearer '+ accessToken \n};\nmsg.payload ={ \n    roomId: roomId,\n    markdown: msg.payload \n}\nmsg.method = \"post\";\n\nreturn msg;","outputs":1,"noerr":0,"x":270,"y":140,"wires":[["9e35271b.7f9d68"]]},{"id":"a85f5179.8069f","type":"inject","z":"2a0bfedb.19a932","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":100,"y":240,"wires":[["c4badf51.f9cc9"]]},{"id":"3590c06a.69b73","type":"debug","z":"2a0bfedb.19a932","name":"Spark Message","active":true,"console":"false","complete":"true","x":740,"y":140,"wires":[]},{"id":"50568783.1b57e8","type":"link in","z":"2a0bfedb.19a932","name":"","links":["30dab5c7.d036da"],"x":255,"y":480,"wires":[["94154c5c.54878"]]},{"id":"4f992b6.1e597d4","type":"comment","z":"2a0bfedb.19a932","name":"Cisco Spark API Examples - README","info":"These sample flows simply take the results from\nthe linked sample flows, formats them and sends a message\nto a Cisco Spark room.\n\nUpdate the Spark API function with your `roomID`\nand your `accessToken`. These can be found on \nthe developers portal for Cisco Spark.\n\nThe `accessToken` can be found in the portal by\nclicking on your profile icon in the top right.\nCopy this token into the function variable.\n\nExample Token\n`ZGJjNzI0ODAtNmEwNS00YjU3LTkzOTYtMjthisisbogushmOWI3M2IyZGI4NzctZDVh`\n\n*API to List Rooms*\nhttps://developer.ciscospark.com/endpoint-rooms-get.html\n- you will get your roomId by running this call\nusing their \"Test Mode\", which will run the command\nand the resulting JSON will appear with the \nid, name, etc. Copy the `id` value into the function variable.\n`\n{\n\t\t\t\"id\": \"Y2lzY29zcGFyazovL3VzL1JPT00vNWExMjhhNTAtNTExZi0xMWU3LTk2NTctYjMzNWEyNmMzYmVj\",\n\t\t\t\"title\": \"Node-RED Testing Sandbox\",\n\t\t\t\"type\": \"group\",\n\t\t\t\"isLocked\": false,\n\t\t\t\"lastActivity\": \"2017-06-14T20:21:09.726Z\",\n\t\t\t\"creatorId\": \"Y2lzY29zcGFyazovL3VzL1BFT1BMRS80ZjY4OWNhZS1mYjNlLTRjNDEtODM3OC1hYmY1Mjc2Y2RmMzQ\",\n\t\t\t\"created\": \"2017-06-14T16:34:35.509Z\"\n\t\t}\n`\n\n*API to Send a Message*\nhttps://developer.ciscospark.com/endpoint-messages-post.html\n","x":430,"y":40,"wires":[]},{"id":"2dd10b01.9ead74","type":"comment","z":"2a0bfedb.19a932","name":"Captive Portal Login Notification","info":"","x":150,"y":280,"wires":[]},{"id":"bf683a1b.a05c48","type":"link out","z":"df4cef7b.9e8a6","name":"Create Meraki Admin","links":["304bc6c8.160c4a"],"x":955,"y":540,"wires":[]},{"id":"304bc6c8.160c4a","type":"link in","z":"2a0bfedb.19a932","name":"","links":["bf683a1b.a05c48"],"x":255,"y":400,"wires":[["f281d735.ca54e8","6ae03988.b59f38"]]},{"id":"f281d735.ca54e8","type":"function","z":"2a0bfedb.19a932","name":"Format Message","func":"var admin = msg.payload;\n\nmsg.payload = \"## New Admin Created! \\n\" +\n    admin.name + \" \" + admin.email + \"   **Access**: \"+admin.orgAccess;\nreturn msg;\n","outputs":1,"noerr":0,"x":410,"y":400,"wires":[["715d791c.1e74a8"]]},{"id":"d8549fe0.db69b","type":"link in","z":"2a0bfedb.19a932","name":"","links":["33ed0e9b.30d802"],"x":255,"y":320,"wires":[["90b9a89d.e8dd18"]]},{"id":"90b9a89d.e8dd18","type":"function","z":"2a0bfedb.19a932","name":"Format Message","func":"\nvar client = msg.payload;\n\nmsg.payload = \"## New WiFi Client Joined! \\n\" +\n    client.name + \" \" + client.email;\nreturn msg;\n","outputs":1,"noerr":0,"x":410,"y":320,"wires":[["a91c1eb0.aa527"]]},{"id":"93947dc2.a6f29","type":"comment","z":"2a0bfedb.19a932","name":"Test Timestamp","info":"","x":100,"y":200,"wires":[]},{"id":"b4ab0969.e909c8","type":"comment","z":"2a0bfedb.19a932","name":"Dashboard API Admin Creation","info":"","x":150,"y":360,"wires":[]},{"id":"392c0cb4.88e934","type":"comment","z":"2a0bfedb.19a932","name":"Location API Notification","info":"","x":130,"y":440,"wires":[]},{"id":"dff10141.0795e","type":"link out","z":"677ea62c.d4fe48","name":"Location API All Data","links":["21ae9ec6.5f47f2"],"x":555,"y":300,"wires":[]},{"id":"cf2f4838.fdd3b8","type":"comment","z":"677ea62c.d4fe48","name":"Workflow Example","info":"","x":110,"y":360,"wires":[]},{"id":"71f4a50c.67001c","type":"debug","z":"677ea62c.d4fe48","name":"WiFi Device","active":true,"console":"false","complete":"true","x":610,"y":440,"wires":[]},{"id":"fc94acc2.8ebd9","type":"http in","z":"677ea62c.d4fe48","name":"","url":"/scanning","method":"post","upload":false,"swaggerDoc":"","x":120,"y":220,"wires":[["281beb10.b703a4","78fb178.88f0ae8"]]},{"id":"21ae9ec6.5f47f2","type":"link in","z":"677ea62c.d4fe48","name":"","links":["dff10141.0795e"],"x":55,"y":440,"wires":[["e50e7fbb.1fe74"]]},{"id":"30dab5c7.d036da","type":"link out","z":"677ea62c.d4fe48","name":"BLE Location API","links":["50568783.1b57e8"],"x":555,"y":480,"wires":[]},{"id":"1d074427.b1109c","type":"function","z":"677ea62c.d4fe48","name":"WiFi Split observations","func":"// send individual observation items to next node\nvar data = msg.payload.data;\n\nfor (i = 0; i < data.observations.length; i++) { \n    msg.payload = data.observations[i];\n    node.send(msg);\n}\n\n","outputs":1,"noerr":0,"x":360,"y":400,"wires":[["71f4a50c.67001c","c4dca2c3.f171d"]]},{"id":"e50e7fbb.1fe74","type":"switch","z":"677ea62c.d4fe48","name":"WiFi or BLE","property":"payload.type","propertyType":"msg","rules":[{"t":"eq","v":"DevicesSeen","vt":"str"},{"t":"eq","v":"BluetoothDevicesSeen","vt":"str"}],"checkall":"true","outputs":2,"x":170,"y":440,"wires":[["1d074427.b1109c"],["6daf8643.1dc028"]]},{"id":"6daf8643.1dc028","type":"function","z":"677ea62c.d4fe48","name":"BLE Split observations","func":"// send individual observation items to next node\nvar data = msg.payload.data;\n\nfor (i = 0; i < data.observations.length; i++) { \n    msg.payload = data.observations[i];\n    node.send(msg);\n}\n\n","outputs":1,"noerr":0,"x":360,"y":480,"wires":[["30dab5c7.d036da","ceb8fed3.01c18"]]},{"id":"ceb8fed3.01c18","type":"debug","z":"677ea62c.d4fe48","name":"BLE Device","active":true,"console":"false","complete":"true","x":610,"y":520,"wires":[]},{"id":"c4dca2c3.f171d","type":"link out","z":"677ea62c.d4fe48","name":"WiFi Location API","links":["66a20d72.119d04"],"x":555,"y":400,"wires":[]},{"id":"f49e6dfd.5c8a7","type":"switch","z":"2a0bfedb.19a932","name":"Search VIP Client- UPDATE","property":"payload.clientMac","propertyType":"msg","rules":[{"t":"cont","v":"74:da:38:56:0a:80","vt":"str"}],"checkall":"true","outputs":1,"x":420,"y":580,"wires":[["9d763271.04738"]]},{"id":"66a20d72.119d04","type":"link in","z":"2a0bfedb.19a932","name":"","links":["c4dca2c3.f171d"],"x":255,"y":540,"wires":[["3e4dad97.3c6262","f49e6dfd.5c8a7"]]},{"id":"3e4dad97.3c6262","type":"function","z":"2a0bfedb.19a932","name":"Format Message","func":"// Write a message with Markdown and include the Google Maps encoded URL.\n// http://maps.google.com/maps?t=h&q=loc:35.487895,-87.332414&z=17 \nvar device = msg.payload;\nvar manufacturer = device.manufacturer || \"\"; // display manufacturer only if it is known\n\nmsg.payload = \"## WiFi Device Found! \\n\" +\n    \"####\"+ manufacturer + \"#### \\n\"+\n    \" **RSSI**:\" + device.rssi +\n    \" **MAC**: \" + device.clientMac +\n    \" [Map](https://maps.google.com/maps?t=h&q=loc:\"+device.location.lat +\",\"+ device.location.lng+\"&z=17)\";\nreturn msg;","outputs":1,"noerr":0,"x":690,"y":540,"wires":[["9c82be49.186a3"]]},{"id":"3af8940e.92cadc","type":"link in","z":"2a0bfedb.19a932","name":"Cisco Spark","links":["c4badf51.f9cc9","a91c1eb0.aa527","715d791c.1e74a8","c66147fd.2d0148","9c82be49.186a3","45bcb8aa.0a9678"],"x":75,"y":140,"wires":[["fdea18dc.a4b788"]]},{"id":"c4badf51.f9cc9","type":"link out","z":"2a0bfedb.19a932","name":"","links":["3af8940e.92cadc"],"x":575,"y":240,"wires":[]},{"id":"a91c1eb0.aa527","type":"link out","z":"2a0bfedb.19a932","name":"","links":["3af8940e.92cadc"],"x":575,"y":320,"wires":[]},{"id":"715d791c.1e74a8","type":"link out","z":"2a0bfedb.19a932","name":"","links":["3af8940e.92cadc"],"x":575,"y":400,"wires":[]},{"id":"c66147fd.2d0148","type":"link out","z":"2a0bfedb.19a932","name":"","links":["3af8940e.92cadc"],"x":835,"y":480,"wires":[]},{"id":"9c82be49.186a3","type":"link out","z":"2a0bfedb.19a932","name":"","links":["3af8940e.92cadc"],"x":835,"y":540,"wires":[]},{"id":"9887c66d.dd3118","type":"comment","z":"2a0bfedb.19a932","name":"Send Message to Cisco Spark Room","info":"","x":170,"y":620,"wires":[]},{"id":"d1796ece.7a068","type":"comment","z":"2a0bfedb.19a932","name":"BLE","info":"","x":170,"y":480,"wires":[]},{"id":"376c4801.f96508","type":"comment","z":"2a0bfedb.19a932","name":"WiFi","info":"","x":170,"y":540,"wires":[]},{"id":"94154c5c.54878","type":"function","z":"2a0bfedb.19a932","name":"Format Message","func":"// Write a message with Markdown and include the Google Maps encoded URL.\n// http://maps.google.com/maps?t=h&q=loc:35.487895,-87.332414&z=17 \nvar device = msg.payload;\nmsg.payload = \"## BLE Device Found! \\n\" +\n    \" **RSSI**:\" + device.rssi +\n    \" **MAC**: \" + device.clientMac +\n    \" [Map](https://maps.google.com/maps?t=h&q=loc:\"+device.location.lat +\",\"+ device.location.lng+\"&z=17)\";\nreturn msg;","outputs":1,"noerr":0,"x":690,"y":480,"wires":[["c66147fd.2d0148"]]},{"id":"9d763271.04738","type":"function","z":"2a0bfedb.19a932","name":"Format Message","func":"// Write a message with Markdown and include the Google Maps encoded URL.\n// http://maps.google.com/maps?t=h&q=loc:35.487895,-87.332414&z=17 \nvar device = msg.payload;\nvar manufacturer = device.manufacturer || \"\"; // display manufacturer only if it is known\n\nmsg.payload = \"## VIP WiFi Device Found! \\n\" +\n    \"####\"+ manufacturer + \"#### \\n\"+\n    \" **RSSI**:\" + device.rssi +\n    \" **MAC**: \" + device.clientMac +\n    \" [Map](https://maps.google.com/maps?t=h&q=loc:\"+device.location.lat +\",\"+ device.location.lng+\"&z=17)\";\nreturn msg;","outputs":1,"noerr":0,"x":690,"y":580,"wires":[["45bcb8aa.0a9678"]]},{"id":"45bcb8aa.0a9678","type":"link out","z":"2a0bfedb.19a932","name":"","links":["3af8940e.92cadc"],"x":835,"y":580,"wires":[]},{"id":"6ae03988.b59f38","type":"debug","z":"2a0bfedb.19a932","name":"Admin Data","active":true,"console":"false","complete":"payload","x":390,"y":440,"wires":[]},{"id":"3a2dd471.54b90c","type":"comment","z":"2a0bfedb.19a932","name":"Send Message to Cisco Spark Room","info":"","x":170,"y":100,"wires":[]}]
dexterlabora

Flow Info

created 2 weeks, 4 days ago
updated 2 weeks, 3 days ago

Node Types

Core
  • comment (x24)
  • debug (x16)
  • function (x19)
  • http in (x11)
  • http request (x3)
  • http response (x11)
  • inject (x4)
  • json (x1)
  • switch (x2)
  • template (x13)
Other
  • link in (x6)
  • link out (x11)
  • tab (x4)

Tags

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