Visualizing and Animating Motion Sensors

This is a simple example of using SVG in a template node (node-red-dashboard) to display and update graphics overlaid on a floor plan. Useful for lots of things, but I used this for showing sensor states in a home

[{"id":"a05b0da9.4c666","type":"tab","label":"Visualizing Motion Sensors","disabled":false,"info":""},{"id":"2f7facf6.23ab74","type":"ui_template","z":"a05b0da9.4c666","group":"44ad467f.f314c8","name":"Visualization","order":1,"width":0,"height":0,"format":"<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js\"></script>\n</head>\n<body style=\"margin: 0px;\">\n\n<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"500\" height=\"800\"  xml:space=\"preserve\" id=\"i01XXX\">\n    <!-- Generated by PaintCode - http://www.paintcodeapp.com -->\n    <defs>\n        <pattern id=\"i01XXX-floorPlanClean-floorPlan\" x=\"0\" y=\"0\" width=\"472\" height=\"1050\" patternUnits=\"userSpaceOnUse\" viewBox=\"0 0 472 1050\">\n            <image xlink:href=\"https://i.pinimg.com/474x/24/25/68/242568f663ba3e62eea4eaea3a26d58d--small-house-plans--sq-ft-house-plans.jpg\" x=\"0\" y=\"0\" width=\"238\" height=\"533\" />\n        </pattern>\n    </defs>\n    <rect id=\"i01XXX-floorPlan\" stroke=\"none\" fill=\"url(#i01XXX-floorPlanClean-floorPlan)\" x=\"0\" y=\"0\" width=\"236\" height=\"525\"  transform=\"scale(1.5, 1.5)\" />\n    <g id=\"i01XXX-allSensors\">\n        <g id=\"i01XXX-motionSensors\" opacity=\"0.28\">\n            <g id=\"i01XXX-gM13\">\n                <path id=\"i01XXX-oM13\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" d=\"M 40.18,-62.19 C 40.18,-26.36 40.18,20.18 40.18,62.19 34.97,63.56 29.49,64.3 23.85,64.3 -11.51,64.3 -40.18,35.51 -40.18,-0 -40.18,-20.82 -30.32,-39.32 -15.05,-51.07 -4.26,-59.37 9.22,-64.3 23.85,-64.3 29.49,-64.3 34.97,-63.56 40.18,-62.19 Z M 40.18,-62.19\" transform=\"translate(106.37, 362.71) rotate(90.48)\"  />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"80\" y=\"340\"><tspan x=\"80\" y=\"379\">M13</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM11\">\n                <path id=\"i01XXX-oM11\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" d=\"M 40.5,-62.39 C 40.5,-26.45 40.5,20.24 40.5,62.39 35.25,63.77 29.73,64.5 24.05,64.5 -11.6,64.5 -40.5,35.62 -40.5,-0 -40.5,-20.88 -30.57,-39.45 -15.17,-51.24 -4.3,-59.56 9.3,-64.5 24.05,-64.5 29.73,-64.5 35.25,-63.77 40.5,-62.39 Z M 40.5,-62.39\" transform=\"translate(106.5, 544.5) rotate(-90)\"  />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"80\" y=\"504\"><tspan x=\"80\" y=\"543\">M11</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM10\">\n                <rect id=\"i01XXX-oM10\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" x=\"37\" y=\"408\" width=\"122\" height=\"89\" rx=\"13\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"62\" y=\"408\"><tspan x=\"62\" y=\"460\">M10</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM8\">\n                <rect id=\"i01XXX-oM8\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" x=\"19\" y=\"704\" width=\"61\" height=\"67\" rx=\"13\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"32\" y=\"720\"><tspan x=\"32\" y=\"745\">M8</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM9\">\n                <rect id=\"i01XXX-oM9\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" x=\"121\" y=\"696\" width=\"54\" height=\"82\" rx=\"13\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"134\" y=\"721\"><tspan x=\"134\" y=\"745\">M9</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM6\">\n                <circle id=\"i01XXX-oM6\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" cx=\"95.5\" cy=\"237.5\" r=\"63.5\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"77\" y=\"206\"><tspan x=\"77\" y=\"244\">M6</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM5\">\n                <circle id=\"i01XXX-oM5\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" cx=\"234.5\" cy=\"219.5\" r=\"63.5\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"212\" y=\"188\"><tspan x=\"212\" y=\"226\">M5</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM4\">\n                <circle id=\"i01XXX-oM4\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" cx=\"275.5\" cy=\"339.5\" r=\"63.5\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"257\" y=\"308\"><tspan x=\"257\" y=\"346\">M4</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM3\">\n                <circle id=\"i01XXX-oM3\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" cx=\"234.5\" cy=\"466.5\" r=\"63.5\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"212\" y=\"435\"><tspan x=\"212\" y=\"473\">M3</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM2\">\n                <circle id=\"i01XXX-oM2\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" cx=\"238.5\" cy=\"593.5\" r=\"63.5\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"216\" y=\"562\"><tspan x=\"216\" y=\"600\">M2</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM1\">\n                <circle id=\"i01XXX-oM1\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" cx=\"238.5\" cy=\"687.5\" r=\"63.5\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"216\" y=\"656\"><tspan x=\"216\" y=\"694\">M1</tspan></text>\n            </g>\n            <g id=\"i01XXX-gM7\">\n                <rect id=\"i01XXX-oM7\" stroke=\"none\" fill=\"rgb(0, 249, 0)\" x=\"19\" y=\"585\" width=\"149\" height=\"67\" rx=\"13\" />\n                \n                <text  fill=\"rgb(0, 0, 0)\" font-family=\"HelveticaNeue-Bold, 'Helvetica Neue', Helvetica, Arial, sans-serif\" font-weight=\"bold\" font-size=\"20\" x=\"51\" y=\"601\"><tspan x=\"51\" y=\"626\">M7</tspan></text>\n            </g>\n        </g>\n    </g>\n</svg>\n\n\n\n\n<script>\n        (function(scope){ \n            scope.$watch('msg', function(msg) {\n                                if (msg){\n\n              $(msg.payload.jquerySelector).attr('opacity', msg.payload.opacity)\n                                }\n            });\n    })(scope);\n</script>\n</body>\n</html>\n","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":1050,"y":240,"wires":[[]]},{"id":"129b5f3f.fdcd51","type":"inject","z":"a05b0da9.4c666","name":"motions to 1.0","topic":"","payload":"{\"jquerySelector\":\"#i01XXX-motionSensors\",\"opacity\":1}","payloadType":"json","repeat":"","crontab":"","once":false,"x":210,"y":200,"wires":[["2f7facf6.23ab74"]]},{"id":"f39259ef.75cf38","type":"inject","z":"a05b0da9.4c666","name":"motions to 0.5","topic":"","payload":"{\"jquerySelector\":\"#i01XXX-motionSensors\",\"opacity\":0.5}","payloadType":"json","repeat":"","crontab":"","once":false,"x":210,"y":260,"wires":[["2f7facf6.23ab74"]]},{"id":"1bb2cfc0.0a7a8","type":"ui_button","z":"a05b0da9.4c666","name":"","group":"44ad467f.f314c8","order":0,"width":0,"height":0,"passthru":false,"label":"Show All Motion Sensors","color":"","bgcolor":"","icon":"","payload":"{\"jquerySelector\":\"#i01XXX-motionSensors\",\"opacity\":1}","payloadType":"json","topic":"","x":450,"y":320,"wires":[["2f7facf6.23ab74"]]},{"id":"28b2e646.dd348a","type":"ui_button","z":"a05b0da9.4c666","name":"","group":"44ad467f.f314c8","order":0,"width":0,"height":0,"passthru":false,"label":"Hide All Motion Sensors","color":"","bgcolor":"","icon":"","payload":"{\"jquerySelector\":\"#i01XXX-motionSensors\",\"opacity\":0}","payloadType":"json","topic":"","x":450,"y":360,"wires":[["2f7facf6.23ab74"]]},{"id":"4e254e02.8ac8a","type":"inject","z":"a05b0da9.4c666","name":"Random values","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":200,"y":560,"wires":[["d7443efb.72a2a"]]},{"id":"d7443efb.72a2a","type":"function","z":"a05b0da9.4c666","name":"all short names","func":"all = ['M1','M2','M3','M4','M5','M6','M7','M8','M9','M10','M11','M13']\nmsg.payload = all\nreturn msg;","outputs":1,"noerr":0,"x":400,"y":560,"wires":[["75a22e71.19b1e"]]},{"id":"954ddfdd.3cb17","type":"function","z":"a05b0da9.4c666","name":"set opacity on the graphics","func":"shortName = msg.payload\npay = {jquerySelector: \"#i01XXX-g\"+shortName,\nopacity: (Math.random() > 0.5 ? 1.0 : 0.0)}\nmsg.payload = pay\nreturn msg;","outputs":1,"noerr":0,"x":840,"y":560,"wires":[["2f7facf6.23ab74"]]},{"id":"75a22e71.19b1e","type":"split","z":"a05b0da9.4c666","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":590,"y":560,"wires":[["954ddfdd.3cb17","3d0a3d4e.5c8232"]]},{"id":"3d0a3d4e.5c8232","type":"function","z":"a05b0da9.4c666","name":"set opacity on the graphic labels","func":"shortName = msg.payload\npay = {jquerySelector: \"#i01XXX-label\"+shortName,\nopacity: (Math.random() > 0.5 ? 1.0 : 0.0)}\nmsg.payload = pay\nreturn msg;","outputs":1,"noerr":0,"x":850,"y":600,"wires":[["2f7facf6.23ab74"]]},{"id":"de434fe4.0e788","type":"ui_button","z":"a05b0da9.4c666","name":"","group":"44ad467f.f314c8","order":0,"width":0,"height":0,"passthru":false,"label":"Randomly Switch Motion Sensors","color":"","bgcolor":"","icon":"","payload":"{\"jquerySelector\":\"#i01XXX-motionSensors\",\"opacity\":0}","payloadType":"json","topic":"","x":420,"y":400,"wires":[["d7443efb.72a2a"]]},{"id":"fe01480f.fec528","type":"comment","z":"a05b0da9.4c666","name":"Open the dashboard and select Testing Motion Viz","info":"","x":260,"y":100,"wires":[]},{"id":"44ad467f.f314c8","type":"ui_group","z":"","name":"Testing Motions","tab":"88123fcf.a392c","disp":true,"width":"10"},{"id":"88123fcf.a392c","type":"ui_tab","z":"","name":"Testing Motion Viz","icon":"dashboard"}]
ssadams11

Flow Info

created 7 months ago

Node Types

Core
  • comment (x1)
  • function (x3)
  • inject (x3)
Other
  • split (x1)
  • tab (x1)
  • ui_button (x3)
  • ui_group (x1)
  • ui_tab (x1)
  • ui_template (x1)

Tags

  • dashboard
  • svg
  • motion
  • sensors
  • visualization
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option