Atlas-Scientific EZO-RTD over i2c Sample Flow

Folks, This is a sample flow for the Atlas-Scientific EZO-RTD circuit/sensor. Obviously, it only works over i2c, so you would need to switch your device from UART mode to i2c mode. It's also a good idea to disable the continuous read as this flow manages that for you (in this case it is set to 5 seconds, which is fine for temperature).

The flow was built with a bit of excess overhead for easily adding other EZO devices. The only caveat is that I use the Device type returned from the Status command to keep track of the sensors. Therefore, if you have more than one type of the same device (example: multiple EZO-PMPs), then you will need to find another method to keep track of the different instances (perhaps using the NAME command, provided you setup each of the multiple devices with a unique name).

I have to give credit to MaddyP (reef2reef.com) for his input on the calibration and the clever use of the i2c scan command to find and organize the device types.

This flow is AS-IS. I hope to continue publishing more flows for other Atlas-Scientific devices and sensors.

[{"id":"7b556f76.1aba9","type":"tab","label":"EZO-RTD","disabled":false,"info":"This Flow makes a few assumptions.\n\n1. You will only have 1 of this type of device installed. If you have more than 1, you must assign it a unique name via the NAME property of the EZO device. Following that, you cannot use the Device property that is returned by the Status command to differentiate the instances of the device.  You must modify the code to use the Name command and Name porperty.\n2. You will not assign any IP addresses below 90 to EZO devices and conversely are not assigning any IP addresses above 90 to non-EZO devices.  This prevents the code from throwing errors should you have other non EZO devices on the I2C bus. This limit can be changed by modifying the HasEZOAddr node.\n3. You will be using degC. If you use degF, you will need to change the scale and unit of the dashboard items."},{"id":"ae91b280.508bd","type":"ui_text","z":"7b556f76.1aba9","group":"2d4a8991.a09e46","order":1,"width":0,"height":0,"name":"Value","label":"Value:","format":"{{msg.payload}}","layout":"row-spread","x":1170,"y":1120,"wires":[]},{"id":"740a2dfc.a1a534","type":"ui_text","z":"7b556f76.1aba9","group":"dbd81c06.40a82","order":1,"width":0,"height":0,"name":"Calib","label":"Value:","format":"{{msg.payload}}","layout":"row-spread","x":1170,"y":1160,"wires":[]},{"id":"b4d9dadc.cf7638","type":"inject","z":"7b556f76.1aba9","name":"Poll","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":true,"onceDelay":"5","topic":"","payload":"","payloadType":"str","x":170,"y":1080,"wires":[["ece9674b.79dc38"]]},{"id":"8de58a9a.de5668","type":"function","z":"7b556f76.1aba9","name":"FirstValues","func":"switch(msg.address){\n    case flow.get('RTD'):\n        msg.payload = parseFloat(msg.payload.slice(1,6).toString());\n        return [msg];        \n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":770,"y":1080,"wires":[["ae91b280.508bd","740a2dfc.a1a534","e3ab7a76.59d3c8"]],"outputLabels":["Status"]},{"id":"636536b5.4f8f58","type":"i2c in","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","count":"16","x":530,"y":1120,"wires":[["8de58a9a.de5668"]]},{"id":"2a3f178a.557ad8","type":"comment","z":"7b556f76.1aba9","name":"Set LED.","info":"","x":160,"y":440,"wires":[]},{"id":"ece9674b.79dc38","type":"switch","z":"7b556f76.1aba9","name":"Boot?","property":"boot","propertyType":"flow","rules":[{"t":"false"}],"checkall":"true","repair":false,"outputs":1,"x":310,"y":1080,"wires":[["f1808c68.c7d7a"]]},{"id":"293525e0.9d376a","type":"delay","z":"7b556f76.1aba9","name":"","pauseType":"delay","timeout":"600","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":530,"y":1080,"wires":[["636536b5.4f8f58"]]},{"id":"f1808c68.c7d7a","type":"change","z":"7b556f76.1aba9","name":"Read","rules":[{"t":"set","p":"address","pt":"msg","to":"RTD","tot":"flow"},{"t":"set","p":"command","pt":"msg","to":"82","tot":"num"},{"t":"set","p":"probe","pt":"msg","to":"temp","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":1120,"wires":[["293525e0.9d376a","3c72092.c8e7ef6"]]},{"id":"e3ab7a76.59d3c8","type":"ui_artlessgauge","z":"7b556f76.1aba9","group":"2d4a8991.a09e46","order":2,"width":"6","height":"6","name":"Graph","icon":"","label":"","unit":"degC","layout":"radial","decimals":"2","differential":false,"minmax":true,"colorTrack":"#555555","style":"","colorFromTheme":true,"property":"payload","secondary":"secondary","inline":false,"animate":true,"sectors":[{"val":0,"col":"#00ffff","t":"min","dot":2},{"val":50,"col":"#00ffff","t":"sec","dot":2},{"val":100,"col":"#00ffff","t":"max","dot":2}],"lineWidth":"3","bgcolorFromTheme":true,"diffCenter":"","x":1170,"y":1080,"wires":[]},{"id":"816e8f27.09719","type":"ui_text","z":"7b556f76.1aba9","group":"dbd81c06.40a82","order":2,"width":0,"height":0,"name":"I2C Address","label":"I2C Address:","format":"{{msg.payload}}","layout":"row-spread","x":1170,"y":280,"wires":[]},{"id":"1015ec8.8834414","type":"ui_text","z":"7b556f76.1aba9","group":"dbd81c06.40a82","order":3,"width":0,"height":0,"name":"Last Status","label":"Last Restart:","format":"{{msg.restart}}","layout":"row-spread","x":1170,"y":200,"wires":[]},{"id":"3671e56d.06778a","type":"ui_text","z":"7b556f76.1aba9","group":"dbd81c06.40a82","order":4,"width":0,"height":0,"name":"mV","label":"Power:","format":"{{msg.mv}} Vcc","layout":"row-spread","x":1150,"y":160,"wires":[]},{"id":"9622b543.0e07f8","type":"i2c scan","z":"7b556f76.1aba9","name":"","busno":"1","x":160,"y":220,"wires":[["870e3e89.3ef82"],[]]},{"id":"440bbfc9.6d741","type":"inject","z":"7b556f76.1aba9","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"true","payloadType":"bool","x":170,"y":160,"wires":[["9622b543.0e07f8"]]},{"id":"46c09d2a.8d3ca4","type":"delay","z":"7b556f76.1aba9","name":"","pauseType":"delay","timeout":"300","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":610,"y":160,"wires":[["14b0142d.e1bf5c"]]},{"id":"14b0142d.e1bf5c","type":"i2c in","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","count":"10","x":610,"y":200,"wires":[["4b2f79b0.6edd58"]]},{"id":"884225b4.dcc8e8","type":"change","z":"7b556f76.1aba9","name":"","rules":[{"t":"set","p":"address","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"command","pt":"msg","to":"105","tot":"num"},{"t":"set","p":"payload","pt":"msg","to":"","tot":"str"},{"t":"set","p":"boot","pt":"flow","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":240,"wires":[["46c09d2a.8d3ca4","5860ade5.070334"]]},{"id":"4b2f79b0.6edd58","type":"function","z":"7b556f76.1aba9","name":"Set Flow Variables","func":"if (msg.payload != \"\"){\n    var status = msg.payload.toString().split(\",\");\n    var probe = status[1];\n    var address = msg.address;\n    var target = 0\n    msg.command = 83;\n    msg.payload = Buffer.from('tatus', 'ascii');\n    msg.probe = probe;\n    flow.set(probe, address);\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":240,"wires":[["82d5cd84.c914d","fb0c7824.dab148","5860ade5.070334"]]},{"id":"870e3e89.3ef82","type":"split","z":"7b556f76.1aba9","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":350,"y":160,"wires":[["44892c6b.3e9b84"]]},{"id":"c7fe2617.f60fd8","type":"comment","z":"7b556f76.1aba9","name":"Boot sequence.","info":"","x":180,"y":40,"wires":[]},{"id":"82d5cd84.c914d","type":"delay","z":"7b556f76.1aba9","name":"","pauseType":"delay","timeout":"300","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":810,"y":160,"wires":[["adeffe35.4995d"]]},{"id":"f31d4b6c.79e748","type":"ui_template","z":"7b556f76.1aba9","group":"396e902b.f95f9","name":"Dashboard CCS Group Overrides","order":5,"width":0,"height":0,"format":"<style id=\"group-styles-override\">\n.nr-dashboard-cardpanel > p {\n    max-height: 24px;\n    min-height: 24px;\n    margin-left: 8px;\n    margin-top: 2px;\n    margin-bottom: 2px;\n    font-weight: 4500;\n    font-size: larger;\n}\n\n@media(max-width: 660px) {\n    md-sidenav,\n    md-sidenav.md-locked-open,\n    md-sidenav.md-closed.md-locked-open-add-active {\n    \tmin-width: 40px !important;\n        width: auto;\n    \tmax-width: 120px !important;\n    }\n}\n@media(min-width: 661px) {\n    md-sidenav,\n    md-sidenav.md-locked-open,\n    md-sidenav.md-closed.md-locked-open-add-active {\n    \tmin-width: 40px !important;\n        width: auto;\n    \tmax-width: 240px !important;\n    }\n}\n\n    .multistate-switch-container {\n        height:18px;\n    }\n    .multistate-switch-wrapper{\n        border-radius:15px;\n        height:18px;\n    }\n    .multistate-switch-slider{\n        border-radius:15px;\n        \n    }\n.switchwrapper {\n  margin: auto 0;\n}\n.toggle_radio{\n    margin: auto;\n    overflow: hidden;\n    border-radius: 50px;\n    position: relative;\n    height: 19px;\n    border: 1px solid var(--nr-dashboard-groupBorderColor);\n    background: var(--nr-dashboard-widgetBgndColor);\n}\n.toggle_radio > * {\n  float: left;\n}\n.toggle_radio input[type=radio]{\n  display: none;\n}\n.toggle_4{\n     width: 25%;\n}\n.toggle_3{\n     width: 33%;\n}\n.toggle_2{\n     width: 50%;\n}\n.toggle_radio label{\n  display: block;\n  height: 15px;\n  margin: 0;\n  line-height :15px;\n  border-radius: 50px;\n  cursor: pointer;\n  z-index: 1;\n  text-align: center;\n}\n.toggle_radio label >p {\n    background:transparent !important;\n}\n.toggle_option_slider{\n  \n  height: 15px;\n  position: absolute;\n  top: 2px;\n  border-radius: 50px;\n  transition: all .3s ease;\n  z-index:0;\n  left:2px;\n  opacity:0.5;\n  background: var(--nr-dashboard-widgetColor);\n}\ndiv.led_847.led_container {\nright:0px;\nheight: calc(100%-6px *2);\ntext-align: center;\npadding: 6px\n}\n\n\n</style>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":420,"y":40,"wires":[[]]},{"id":"44892c6b.3e9b84","type":"switch","z":"7b556f76.1aba9","name":"HasEZOAddr","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"90","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":370,"y":200,"wires":[["884225b4.dcc8e8"]]},{"id":"adeffe35.4995d","type":"i2c in","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","count":"16","x":810,"y":200,"wires":[["9812299e.4af758"]]},{"id":"fb0c7824.dab148","type":"function","z":"7b556f76.1aba9","name":"I2C","func":"switch(msg.probe){\n    case 'rtd':\n        msg.payload = msg.address;\n        return [msg];    \n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":970,"y":240,"wires":[["816e8f27.09719"]],"outputLabels":["Status"]},{"id":"9812299e.4af758","type":"function","z":"7b556f76.1aba9","name":"Status / mV","func":"msg.payload = msg.payload.toString().split(\",\");\n\nswitch(msg.payload[1]){\n    case 'P':\n        msg.restart = 'Powered Off';\n        break;\n    case 'S':\n        msg.restart = 'Software Reset';\n        break;\n    case 'B':\n        msg.restart = 'Brown Out';\n        break;\n    case 'W':\n        msg.restart = 'Watchdog';\n        break;\n    case 'U':\n        msg.restart = 'Unknown';\n        break;\n}\n\nmsg.mv = msg.payload[2];\nflow.set('boot', false);\n\nswitch(msg.address){\n    case flow.get('RTD'):\n        return [msg];\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":990,"y":160,"wires":[["3671e56d.06778a","1015ec8.8834414"]],"outputLabels":["Status"]},{"id":"e2a93b03.8c0ce8","type":"comment","z":"7b556f76.1aba9","name":"Read Sensor.","info":"","x":170,"y":1020,"wires":[]},{"id":"a98869a6.bd0448","type":"comment","z":"7b556f76.1aba9","name":"Scan Bus for Sensor.","info":"","x":200,"y":100,"wires":[]},{"id":"3c72092.c8e7ef6","type":"i2c out","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","payload":"payload","payloadType":"msg","count":"1","x":330,"y":1160,"wires":[[]]},{"id":"5860ade5.070334","type":"i2c out","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","payload":"payload","payloadType":"msg","count":"1","x":610,"y":280,"wires":[[]]},{"id":"ebf3bf60.bc4be","type":"comment","z":"7b556f76.1aba9","name":"Change I2C address.","info":"","x":200,"y":320,"wires":[]},{"id":"19b34f55.2654f1","type":"ui_button","z":"7b556f76.1aba9","name":"ChangeI2C","group":"dbd81c06.40a82","order":7,"width":6,"height":1,"passthru":false,"label":"Change I2C","tooltip":"","color":"","bgcolor":"","icon":"","payload":"temp","payloadType":"str","topic":"","topicType":"str","x":170,"y":380,"wires":[["2fe7ffbc.1f055"]]},{"id":"2fe7ffbc.1f055","type":"function","z":"7b556f76.1aba9","name":"Set Address","func":"switch (msg.payload){\n    case 'temp':\n        msg.address = flow.get('RTD');\n        break;  \n}\n\nmsg.command = 73;\nmsg.payload = 'Enter new I2C address:';\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":380,"wires":[["bbded51b.591ec8"]]},{"id":"bbded51b.591ec8","type":"ui_toast","z":"7b556f76.1aba9","position":"prompt","displayTime":"3","highlight":"","sendall":false,"outputs":1,"ok":"OK","cancel":"Cancel","raw":false,"topic":"","name":"","x":550,"y":380,"wires":[["f12716ca.4bc598"]]},{"id":"f12716ca.4bc598","type":"function","z":"7b556f76.1aba9","name":"Set payload","func":"if (msg.payload != \"\"){\n    msg.payload = Buffer.from('2C,' + parseFloat(msg.payload), 'ascii');\n    flow.set('boot', true);\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":730,"y":380,"wires":[["1a0b68a2.cb10f7","c36ca050.bdeab"]]},{"id":"1a0b68a2.cb10f7","type":"delay","z":"7b556f76.1aba9","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":900,"y":380,"wires":[["9622b543.0e07f8"]]},{"id":"c36ca050.bdeab","type":"i2c out","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","payload":"payload","payloadType":"msg","count":"1","x":730,"y":420,"wires":[[]]},{"id":"e993b09f.c8816","type":"ui_switch","z":"7b556f76.1aba9","name":"LED","label":"LED","tooltip":"","group":"dbd81c06.40a82","order":5,"width":6,"height":1,"passthru":true,"decouple":"false","topic":"","topicType":"str","style":"","onvalue":"temp,on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"temp,off","offvalueType":"str","officon":"","offcolor":"","animate":true,"x":150,"y":500,"wires":[["77728ff8.16ee5"]]},{"id":"77728ff8.16ee5","type":"function","z":"7b556f76.1aba9","name":"Set Address","func":"var probe = msg.payload.split(\",\");\nvar value = msg.payload.split(\",\");\n\nswitch (probe[0]){\n        case 'temp':\n        msg.address = flow.get('RTD');\n        break;         \n}\n\nswitch (value[1]){\n    case 'on':\n        msg.payload = Buffer.from(',1', 'ascii');\n        break\n    case 'off':\n        msg.payload = Buffer.from(',0', 'ascii');\n        break;\n}\n\nmsg.command = 76;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":500,"wires":[["b5ff1a56.86d588"]]},{"id":"b5ff1a56.86d588","type":"i2c out","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","payload":"payload","payloadType":"msg","count":"1","x":370,"y":540,"wires":[[]]},{"id":"55a73f2e.fff08","type":"comment","z":"7b556f76.1aba9","name":"Factory reset.","info":"","x":170,"y":880,"wires":[]},{"id":"4ceec35a.203d6c","type":"ui_button","z":"7b556f76.1aba9","name":"FactoryReset","group":"dbd81c06.40a82","order":8,"width":6,"height":1,"passthru":false,"label":"Factory Reset","tooltip":"","color":"","bgcolor":"","icon":"","payload":"temp","payloadType":"str","topic":"","topicType":"str","x":170,"y":940,"wires":[["5c771898.ba7218"]]},{"id":"5c771898.ba7218","type":"function","z":"7b556f76.1aba9","name":"Set Address","func":"switch (msg.payload){\n     case 'pmp':\n        msg.address = flow.get('PMP');\n        break;  \n}\n\nmsg.command = 70;\nmsg.payload = 'Do you want to reset ' + msg.payload + ' ezo device to factory settings?'\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":940,"wires":[["d992ecd9.093c6"]]},{"id":"d992ecd9.093c6","type":"ui_toast","z":"7b556f76.1aba9","position":"dialog","displayTime":"3","highlight":"","sendall":false,"outputs":1,"ok":"Reset","cancel":"Cancel","raw":false,"topic":"Factory Reset","name":"","x":550,"y":940,"wires":[["197c593a.1a6317"]]},{"id":"197c593a.1a6317","type":"function","z":"7b556f76.1aba9","name":"Send I2C","func":"if (msg.payload != \"\"){\n    msg.payload = Buffer.from('actory', 'ascii');\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":720,"y":940,"wires":[["8c9f7299.56aea"]]},{"id":"8c9f7299.56aea","type":"i2c out","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","payload":"payload","payloadType":"msg","count":"1","x":710,"y":980,"wires":[[]]},{"id":"378fdb45.a1e484","type":"comment","z":"7b556f76.1aba9","name":"Flash LED.","info":"","x":160,"y":560,"wires":[]},{"id":"883c0466.d1a468","type":"ui_button","z":"7b556f76.1aba9","name":"Flash LED","group":"dbd81c06.40a82","order":6,"width":6,"height":1,"passthru":false,"label":"Find","tooltip":"","color":"","bgcolor":"","icon":"","payload":"temp","payloadType":"str","topic":"","topicType":"str","x":170,"y":620,"wires":[["b8bcbc0e.65bce"]]},{"id":"b8bcbc0e.65bce","type":"function","z":"7b556f76.1aba9","name":"Set Address","func":"switch (msg.payload){\n    case 'temp':\n        msg.address = flow.get('RTD');\n        break;             \n}\n\nmsg.command = 70;\nmsg.payload = Buffer.from('ind', 'ascii');\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":620,"wires":[["403e7ae2.3d7744"]]},{"id":"403e7ae2.3d7744","type":"i2c out","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","payload":"payload","payloadType":"msg","count":"1","x":370,"y":660,"wires":[[]]},{"id":"d7a63c5e.71678","type":"comment","z":"7b556f76.1aba9","name":"Calibrate.","info":"","x":160,"y":700,"wires":[]},{"id":"29fd477d.a09728","type":"ui_button","z":"7b556f76.1aba9","name":"Calibrate","group":"dbd81c06.40a82","order":9,"width":0,"height":0,"passthru":false,"label":"Calibrate","tooltip":"","color":"","bgcolor":"","icon":"","payload":"temp","payloadType":"str","topic":"","topicType":"str","x":160,"y":760,"wires":[["49c5216f.f285b"]]},{"id":"49c5216f.f285b","type":"function","z":"7b556f76.1aba9","name":"Set Address","func":"msg.probe = msg.payload;\n\nswitch (msg.probe){\n       case 'temp':\n        msg.address = flow.get('RTD');\n        break;        \n}\n\nmsg.command = 67;\nmsg.payload = \"Do you want to calibrate \" + msg.probe + \" device?  Existing calibration data will be erased.\";\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":760,"wires":[["65a71a0b.d8f104"]]},{"id":"65a71a0b.d8f104","type":"ui_toast","z":"7b556f76.1aba9","position":"dialog","displayTime":"3","highlight":"","sendall":false,"outputs":1,"ok":"Calibrate","cancel":"Cancel","raw":false,"topic":"","name":"","x":550,"y":760,"wires":[["1b31346f.8b010c"]]},{"id":"1b31346f.8b010c","type":"function","z":"7b556f76.1aba9","name":"Calibrate","func":"if (msg.payload != \"Cancel\"){\n    var count = context.get('count') || 0;\n    count +=1;\n    context.set('count', count);\n    msg.count = count;\n    \n    switch (msg.probe){\n        case 'temp':\n            switch (count){\n                case 1:\n                    msg.topic = \"\";\n                    msg.payload = \"Enter actual temperature:\";\n                    return [msg,null,null];\n            }\n            break;            \n    }\n    \n    context.set('count', 0);\n    msg.topic = \"\";\n    msg.payload = \"Calibration complete!\";\n    return [null,null,msg];\n}","outputs":3,"noerr":0,"initialize":"","finalize":"","libs":[],"x":740,"y":820,"wires":[["3df8e887.9ffa68"],["553f52e8.97fb6c"],["df20c56f.ef7628"]]},{"id":"3df8e887.9ffa68","type":"ui_toast","z":"7b556f76.1aba9","position":"dialog","displayTime":"3","highlight":"","sendall":false,"outputs":1,"ok":"Next","cancel":"","raw":false,"topic":"","name":"Next","x":930,"y":760,"wires":[["8a863001.a93f3","1b31346f.8b010c"]]},{"id":"553f52e8.97fb6c","type":"ui_toast","z":"7b556f76.1aba9","position":"prompt","displayTime":"3","highlight":"","sendall":true,"outputs":1,"ok":"Calibrate","cancel":"Cancel","raw":false,"topic":"","name":"Input","x":930,"y":820,"wires":[["8a863001.a93f3","1b31346f.8b010c"]]},{"id":"df20c56f.ef7628","type":"ui_toast","z":"7b556f76.1aba9","position":"dialog","displayTime":"3","highlight":"","sendall":false,"outputs":1,"ok":"OK","cancel":"","raw":false,"topic":"","name":"OK","x":930,"y":880,"wires":[[]]},{"id":"8a863001.a93f3","type":"function","z":"7b556f76.1aba9","name":"Send I2C","func":"switch (msg.probe){\n    case 'temp':\n        switch (msg.count){\n            case 1:\n                msg.topic = \"\";\n                return msg;\n        }\n        break;\n  }","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1160,"y":820,"wires":[["98ed979a.fda268"]]},{"id":"98ed979a.fda268","type":"i2c out","z":"7b556f76.1aba9","name":"","busno":"1","address":"","command":"","payload":"payload","payloadType":"msg","count":"1","x":1150,"y":860,"wires":[[]]},{"id":"7c8294de.55f20c","type":"ui_template","z":"7b556f76.1aba9","d":true,"group":"396e902b.f95f9","name":"Virtual Keyboard","order":1,"width":0,"height":0,"format":"<script> \n    \n// the semi-colon before function invocation is a safety net against concatenated\n// scripts and/or other plugins which may not be closed properly.\n; (function ($, window, document, undefined) {\n\n    // undefined is used here as the undefined global variable in ECMAScript 3 is\n    // mutable (ie. it can be changed by someone else). undefined isn't really being\n    // passed in so we can ensure the value of it is truly undefined. In ES5, undefined\n    // can no longer be modified.\n\n    // window and document are passed through as local variable rather than global\n    // as this (slightly) quickens the resolution process and can be more efficiently\n    // minified (especially when both are regularly referenced in your plugin).\n\n    // Create the defaults once\n    var pluginName = \"jkeyboard\",\n        defaults = {\n            layout: \"english\",\n            input: $('#input'),\n            customLayouts: {\n                selectable: []\n            },\n        };\n\n\n    var function_keys = {\n        backspace: {\n            text: 'DEL',\n        },\n        return: {\n            text: 'Enter'\n        },\n        shift: {\n            text: 'Shift'\n        },\n        space: {\n            text: 'Space'\n        },\n        numeric_switch: {\n            text: '123',\n            command: function () {\n                this.createKeyboard('numeric');\n                this.events();\n            }\n        },\n        layout_switch: {\n            text: '<i class=\"fa fa-keyboard-o\" aria-hidden=\"true\"></i>',\n            command: function () {\n                var l = this.toggleLayout();\n                this.createKeyboard(l);\n                this.events();\n            }\n        },\n        character_switch: {\n            text: 'ABC',\n            command: function () {\n                this.createKeyboard(layout);\n                this.events();\n            }\n        },\n        symbol_switch: {\n            text: '#+=',\n            command: function () {\n                this.createKeyboard('symbolic');\n                this.events();\n            }\n        }\n    };\n\n\n    var layouts = {\n        selectable: ['azeri', 'english', 'russian','french', 'emoji'],\n        azeri: [\n            ['q', 'ü', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'ö', 'ğ'],\n            ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ı', 'ə'],\n            ['shift', 'z', 'x', 'c', 'v', 'b', 'n', 'm', 'ç', 'ş', 'backspace'],\n            ['numeric_switch', 'layout_switch', 'space', 'return']\n        ],\n        english: [\n            ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',],\n            ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',],\n            ['shift', 'z', 'x', 'c', 'v', 'b', 'n', 'm', 'backspace'],\n            ['numeric_switch', 'layout_switch', 'space', 'return']\n        ],\n        russian: [\n            ['й', 'ц', 'у', 'к', 'е', 'н', 'г', 'ш', 'щ', 'з', 'х'],\n            ['ф', 'ы', 'в', 'а', 'п', 'р', 'о', 'л', 'д', 'ж', 'э'],\n            ['shift', 'я', 'ч', 'с', 'м', 'и', 'т', 'ь', 'б', 'ю', 'backspace'],\n            ['numeric_switch', 'layout_switch', 'space', 'return']\n        ],\n        french: [\n            ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',],\n            ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l','à','ç'],\n            ['shift', 'z', 'x', 'c', 'v', 'b', 'n', 'm','é','è', 'backspace'],\n            ['numeric_switch', 'layout_switch', 'space', 'return']\n        ],\n        emoji: [\n            ['😀', '😁', '😂', '🤣', '😃', '😄', '😅', '😆', '😉', '😊',],\n            ['😋', '😎', '😍', '😘', 'g', 'h', 'j', 'k', 'l','à','ç'],\n            ['shift', 'z', 'x', 'c', 'v', 'b', 'n', 'm','é','è', 'backspace'],\n            ['numeric_switch', 'layout_switch', 'space', 'return']\n        ],            \n        numeric: [\n            ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],\n            ['-', '/', ':', ';', '(', ')', '$', '&', '@', '\"'],\n            ['symbol_switch', '.', ',', '?', '!', \"'\", 'backspace'],\n            ['character_switch', 'layout_switch', 'space', 'return'],\n        ],\n        numbers_only: [\n            ['1', '2', '3',],\n            ['4', '5', '6',],\n            ['7', '8', '9',],\n            ['0', 'return', 'backspace'],\n        ],\n        symbolic: [\n            ['[', ']', '{', '}', '#', '%', '^', '*', '+', '='],\n            ['_', '\\\\', '|', '~', '<', '>'],\n            ['numeric_switch', '.', ',', '?', '!', \"'\", 'backspace'],\n            ['character_switch', 'layout_switch', 'space', 'return'],\n\n        ]\n    }\n\n    var shift = false, capslock = false, layout = 'english', layout_id = 0;\n\n    // The actual plugin constructor\n    function Plugin(element, options) {\n        this.element = element;\n        // jQuery has an extend method which merges the contents of two or\n        // more objects, storing the result in the first object. The first object\n        // is generally empty as we don't want to alter the default options for\n        // future instances of the plugin\n        this.settings = $.extend({}, defaults, options);\n        // Extend & Merge the cusom layouts\n        layouts = $.extend(true, {}, this.settings.customLayouts, layouts);\n        if (Array.isArray(this.settings.customLayouts.selectable)) {\n            $.merge(layouts.selectable, this.settings.customLayouts.selectable);\n        }\n        this._defaults = defaults;\n        this._name = pluginName;\n        this.init();\n    }\n\n    Plugin.prototype = {\n        init: function () {\n            layout = this.settings.layout;\n            this.createKeyboard(layout);\n            this.events();\n        },\n\n        setInput: function (newInputField) {\n            this.settings.input = newInputField;\n        },\n\n        createKeyboard: function (layout) {\n            shift = false;\n            capslock = false;\n\n            var keyboard_container = $('<ul/>').addClass('jkeyboard'),\n                me = this;\n\n            layouts[layout].forEach(function (line, index) {\n                var line_container = $('<li/>').addClass('jline');\n                line_container.append(me.createLine(line));\n                keyboard_container.append(line_container);\n            });\n\n            $(this.element).html('').append(keyboard_container);\n        },\n\n        createLine: function (line) {\n            var line_container = $('<ul/>');\n\n            line.forEach(function (key, index) {\n                var key_container = $('<li/>').addClass('jkey').data('command', key);\n\n                if (function_keys[key]) {\n                    key_container.addClass(key).html(function_keys[key].text);\n                }\n                else {\n                    key_container.addClass('letter').html(key);\n                }\n\n                line_container.append(key_container);\n            })\n\n            return line_container;\n        },\n\n        events: function () {\n            var letters = $(this.element).find('.letter'),\n                shift_key = $(this.element).find('.shift'),\n                space_key = $(this.element).find('.space'),\n                backspace_key = $(this.element).find('.backspace'),\n                return_key = $(this.element).find('.return'),\n\n                me = this,\n                fkeys = Object.keys(function_keys).map(function (k) {\n                    return '.' + k;\n                }).join(',');\n\n            letters.on('click', function () {\n                me.type((shift || capslock) ? $(this).text().toUpperCase() : $(this).text());\n            });\n\n            space_key.on('click', function () {\n                me.type(' ');\n            });\n\n            return_key.on('click', function () {\n                me.type(\"\\n\");\n                me.settings.input.parents('form').submit();\n            });\n\n            backspace_key.on('click', function () {\n                me.backspace();\n            });\n\n            shift_key.on('click', function () {\n                if (capslock) {\n                    me.toggleShiftOff();\n                    capslock = false;\n                } else {\n                    me.toggleShiftOn();\n                }\n            }).on('dblclick', function () {\n                capslock = true;\n            });\n\n\n            $(fkeys).on('click', function () {\n                var command = function_keys[$(this).data('command')].command;\n                if (!command) return;\n\n                command.call(me);\n            });\n        },\n\n        type: function (key) {\n            var input = this.settings.input,\n                val = input.val(),\n                input_node = input.get(0),\n                start = input_node.selectionStart,\n                end = input_node.selectionEnd;\n\n            var max_length = $(input).attr(\"maxlength\");\n            if (start == end && end == val.length) {\n                if (!max_length || val.length < max_length) {\n                    input.val(val + key);\n                    input.change()\n                }\n            } else {\n                //console.log(input_node.type)\n                if (input_node.type != \"number\"){\n                    var new_string = this.insertToString(start, end, val, key);\n                    input.val(new_string);\n                    start++;\n                    end = start;\n                    input_node.setSelectionRange(start, end);\n                    input.change()\n                }else{\n                    console.log(\"Not supposed to go there as number types are changed to text type and back\")\n                    input.val(key + val);\n                    input.change()\n                }\n                \n            }\n\n            input.trigger('focus');\n\n            if (shift && !capslock) {\n                this.toggleShiftOff();\n            }\n        },\n\n        backspace: function () {\n            var input = this.settings.input,\n                val = input.val();\n                input_node = input.get(0),\n                start = input_node.selectionStart,\n                end = input_node.selectionEnd;\n            \n            input.val(val.slice(0, start-1) + val.slice(start))\n            input.change()\n            input.focus()\n            input_node.setSelectionRange(start-1, start-1);\n        },\n\n        toggleShiftOn: function () {\n            var letters = $(this.element).find('.letter'),\n                shift_key = $(this.element).find('.shift');\n\n            letters.addClass('uppercase');\n            shift_key.addClass('active')\n            shift = true;\n        },\n\n        toggleShiftOff: function () {\n            var letters = $(this.element).find('.letter'),\n                shift_key = $(this.element).find('.shift');\n\n            letters.removeClass('uppercase');\n            shift_key.removeClass('active');\n            shift = false;\n        },\n\n        toggleLayout: function () {\n            layout_id = layout_id || 0;\n            var plain_layouts = layouts.selectable;\n            layout_id++;\n\n            var current_id = layout_id % plain_layouts.length;\n            var SelectedLayoutName = plain_layouts[current_id];\n            $('#vkeyname').text('V-Keyboard ' + SelectedLayoutName )\n            return plain_layouts[current_id];\n        },\n\n        insertToString: function (start, end, string, insert_string) {\n            return string.substring(0, start) + insert_string + string.substring(end, string.length);\n        }\n    };\n\n        /*\n\t\t// A really lightweight plugin wrapper around the constructor,\n\t\t// preventing against multiple instantiations\n\t\t$.fn[ pluginName ] = function ( options ) {\n\t\t\t\treturn this.each(function() {\n\t\t\t\t\t\tif ( !$.data( this, \"plugin_\" + pluginName ) ) {\n\t\t\t\t\t\t\t\t$.data( this, \"plugin_\" + pluginName, new Plugin( this, options ) );\n\t\t\t\t\t\t}\n\t\t\t\t});\n\t\t};\n        */\n        var methods = {\n            init: function(options) {\n                if (!this.data(\"plugin_\" + pluginName)) {\n                    this.data(\"plugin_\" + pluginName, new Plugin(this, options));\n                }\n            },\n\t\t\tsetInput: function(content) {\n\t\t\t\tthis.data(\"plugin_\" + pluginName).setInput($(content));\n            },\n            setLayout: function(layoutname) {\n                // change layout if it is not match current\n                object = this.data(\"plugin_\" + pluginName);\n                if (typeof(layouts[layoutname]) !== 'undefined' && object.settings.layout != layoutname) {\n                    object.settings.layout = layoutname;\n                    object.createKeyboard(layoutname);\n                    object.events();\n                };\n            },\n        };\n\n\t\t$.fn[pluginName] = function (methodOrOptions) {\n            if (methods[methodOrOptions]) {\n                return methods[methodOrOptions].apply(this.first(), Array.prototype.slice.call( arguments, 1));\n            } else if (typeof methodOrOptions === 'object' || ! methodOrOptions) {\n                // Default to \"init\"\n                return methods.init.apply(this.first(), arguments);\n            } else {\n                $.error('Method ' +  methodOrOptions + ' does not exist on jQuery.tooltip');\n            }\n        };\n\n})(jQuery, window, document);\n</script>\n<style>\n    .jkeyboard {\n  display: inline-block;\n}\n.jkeyboard, .jkeyboard .jline, .jkeyboard .jline ul {\n  display: block;\n  margin: 0;\n  padding: 0;\n}\n.jkeyboard .jline {\n  text-align: center;\n  margin-left: -14px;\n}\n.jkeyboard .jline ul li {\n  font-family: arial, sans-serif;\n  font-size: 20px;\n  display: inline-block;\n  border: 1px solid #468db3;\n  -webkit-box-shadow: 0 0 3px #468db3;\n  -webkit-box-shadow: inset 0 0 3px #468db3;\n  margin: 5px 0 1px 6px;\n  color: #000000;\n  border-radius: 5px;\n  width: 52px;\n  height: 52px;\n  box-sizing: border-box;\n  text-align: center;\n  line-height: 52px;\n  overflow: hidden;\n  cursor: pointer;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -khtml-user-select: none;\n  -moz-user-select: -moz-none;\n  -ms-user-select: none;\n  user-select: none;\n}\n.jkeyboard .jline ul li.uppercase {\n  text-transform: uppercase;\n}\n.jkeyboard .jline ul li:hover, .jkeyboard .jline ul li:active {\n  background-color: #185a82;\n}\n.jkeyboard .jline .return {\n  width: 80px;\n}\n.jkeyboard .jline .space {\n  width: 366px;\n}\n.jkeyboard .jline .numeric_switch {\n  width: 65px;\n}\n.jkeyboard .jline .layout_switch {\n}\n.jkeyboard .jline .shift {\n  width: 60px;\n}\n.jkeyboard .jline .backspace {\n  width: 69px;\n}\n</style>\n\n\n\n\n<style>\nbody {font-family: Arial, Helvetica, sans-serif;}\n\n.nr-dashboard-theme .nr-dashboard-template .md-button:not(:first-of-type) {\n    margin-top: 0px;\n}\n\n/* The Modal (background) */\n.modal {\n    display: none; /* Hidden by default */\n    position: fixed; /* Stay in place */\n    opacity:0.99;\n    z-index: 100; /* Sit on top */\n    left: 0;\n    top: 0;\n    width: 100%; /* Full width */\n    height: 100%; /* Full height */\n    overflow: auto; /* Enable scroll if needed */\n    background-color: rgb(0,0,0); /* Fallback color */\n    background-color: rgba(0,0,0,0.4); /* Black w/ opacity */\n}\n\n/* Modal Content */\n.modal-content {\n    position: fixed;\n    background-color: #fefefe;\n    margin: auto;\n    padding: 0;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%);\n    border: 1px solid #888;\n    width: 720px;\n    max-width: 100%;\n    max-height: 100%;\n    box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);\n    -webkit-animation-name: animate;\n    -webkit-animation-duration: 0.4s;\n    animation-name: animate;\n    animation-duration: 0.4s\n}\n\n/* Add Animation */\n@-webkit-keyframes animate {\n    from {top:100%; opacity:0} \n    to {top:50%; opacity:1}\n}\n\n@keyframes animate {\n    from {top:100%; opacity:0}\n    to {top:50%; opacity:1}\n}\n\n/* The Close Button */\n.close {\n    color: black;\n    float: right;\n    font-size: 28px;\n    font-weight: bold;\n}\n\n.close:hover,\n.close:focus {\n    color: #000;\n    text-decoration: none;\n    cursor: pointer;\n}\n\n.modal-header {\n    padding: 2px 16px;\n    background-color: aliceblue;\n    color: white;\n}\n\n.modal-body {padding: 2px 16px;}\n\n.modal-footer {\n    padding: 2px 16px;\n    background-color: #5cb85c;\n    color: white;\n}\n</style>\n\n<!-- The Modal -->\n<div id=\"myModal\" class=\"modal\">\n\n  <!-- Modal content -->\n  <div class=\"modal-content\">\n      <div class=\"modal-header\">\n      <span class=\"close\" onclick=\"closeModal()\">&times;</span>\n      <h2 id=\"vkeyname\" style=\"background-color: aliceblue !important; color: black !important; text-align: center;\">V-Keyboard</h2>\n    </div>\n    <div class=\"modal-body\">\n        <div id=\"keyboard\"></div>\n        <div>\n        </div>\n    </div>\n  </div>\n</div>\n\n\n<script>\n    // Get the modal\nvar modal = document.getElementById('myModal');\n\n/*\n$('input[type=text]').click(function () {\n    $('#keyboard').unbind().removeData();\n        $('#keyboard').jkeyboard({\n            layout: \"english\",\n            input: $('#'+$(this).attr('id'))\n    });\n});\n\n$('input[type=number]').click(function () {\n    $('#keyboard').unbind().removeData();\n        $('#keyboard').jkeyboard({\n            layout: \"numbers_only\",\n            input: $('#'+$(this).attr('id'))\n    });\n});\n*/\n\nvar inputTags;\nvar inputType;\n\nvar getinputs = function() {\n    inputTags = document.getElementsByTagName(\"input\");\n    for (var i = 0; i < inputTags.length; i++) {\n        inputTags[i].addEventListener('click', openModal, false)\n    }\n}\n\nsetTimeout(function(){ getinputs(); }, 1000);\n\nvar inputTarget;\n\nvar openModal = function() {\n    inputType = event.target.type\n    inputTarget = event.target\n    var layoutName;\n    if (inputType == \"number\"){\n        //console.log(event.target)\n        inputTarget.type = \"text\" //hack because chrome doesn't allow setselection in number inputs\n        layoutName = \"numbers_only\"\n    }else{\n        layoutName = \"english\"\n    }\n    $('#keyboard').unbind().removeData();\n    modal.style.display = \"block\";\n    $('#keyboard').jkeyboard({\n        layout: layoutName,\n        input: $('#'+$(this).attr('id'))\n    });\n}\n\n\n// Get the <span> element that closes the modal\nvar span = document.getElementsByClassName(\"close\")[0];\n\n// When the user clicks on <span> (x), close the modal\n//span.onclick = function(event) {\n  //closeModal()\n//}\n\n// When the user clicks anywhere outside of the modal, close it\nwindow.onclick = function(event) {\n    var source = event.target;\n    if (source == modal || source == span) {\n        closeModal(source)\n    }\n};\n\nvar closeModal = function(source){\n    //console.log(\"closing\")\n    modal.style.display = \"none\";\n   \n    if (inputType == \"number\"){\n        inputTarget.type = \"number\" //hack because chrome doesn't allow selectionstart on number inputs\n    }\n}\n\n</script>\n<script>\n\nvar clickState = 1;\nvar btn = document.querySelector('.VK');\n\nbtn.addEventListener('click', function(){\n\n  if (clickState == 0) {\n    this.textContent = 'V-KeyBoard On';\n    modal = document.getElementById('myModal');\n    clickState = 1;\n  } else {\n    this.textContent = 'V-KeyBoard Off';\n    modal = document.getElementById('empty');\n    clickState = 0;\n  }\n\n});\n</script>\n\n<style>\n.VK{\n    position: fixed;\n    top: 60px;\n    right: 20px;\n    height: 30px;\n}\n</style>\n\n<div id=\"empty\"></div>\n<button class=\"VK\">V-KeyBoard On</button> \n\n\n","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"global","x":680,"y":40,"wires":[[]]},{"id":"2d4a8991.a09e46","type":"ui_group","name":"EZO-RTD","tab":"11cb1d79.3aa993","order":1,"disp":true,"width":"6","collapse":false},{"id":"dbd81c06.40a82","type":"ui_group","name":"EZO-RTD-Config","tab":"11cb1d79.3aa993","order":2,"disp":true,"width":"6","collapse":false},{"id":"396e902b.f95f9","type":"ui_group","name":"Users","tab":"72d86021.1ec47","order":2,"disp":true,"width":"8","collapse":false},{"id":"11cb1d79.3aa993","type":"ui_tab","name":"EZO-RTD","icon":"dashboard","order":6,"disabled":false,"hidden":false},{"id":"72d86021.1ec47","type":"ui_tab","d":true,"name":"Keyboard","icon":"dashboard","order":10,"disabled":false,"hidden":false}]

Flow Info

Created 3 years, 1 month ago
Rating: not yet rated

Actions

Rate:

Node Types

Core
  • change (x2)
  • comment (x8)
  • delay (x4)
  • function (x13)
  • inject (x2)
  • split (x1)
  • switch (x2)
Other

Tags

  • EZO-RTD
  • Atlas-Scientific
  • i2c
  • SonoraTechnical
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option