├── .gitignore ├── LICENSE ├── README.md ├── icons └── redis.png ├── package-lock.json ├── package.json ├── redis.html └── redis.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | /nbproject/private/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 @AvilaCwb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-red-contrib-redis 2 | Node Red client for Redis with pub/sub, list, lua scripting, ssl, cluster, custom commands, instance injection and other commands support. 3 | 4 | Connection Options parameter receives IORedis object or string (https://github.com/luin/ioredis#connect-to-redis). 5 | 6 | Now uses same connection per config name and open new if you set block connection option. 7 | 8 | Roadmap: 9 | - (ok)Stream Support 10 | - (ok)Flow or Global redis instance injection to use on function Node. 11 | - (wip)Better Validations 12 | - (ok)Custom Commands support(Modules), with instance or Cmd. 13 | 14 | See the Sample flow before ask how this module works. 15 | 16 | Please test and make feedback. 17 | 18 | I need contributors... 19 | 20 | 21 | 22 | Redis Commands: 23 | 24 | ![Redis Command](https://github.com/chameleonbr/node-red-examples/raw/master/images/Node-RED_cmd_cfg.png "Redis Command") 25 | 26 | Payload -> Redis 27 | 28 | ![Payload -> Redis](https://github.com/chameleonbr/node-red-examples/raw/master/images/Node-RED_redis_params.png "Payload -> Redis") 29 | 30 | Redis Queue: 31 | 32 | ![Payload -> Redis](https://github.com/chameleonbr/node-red-examples/raw/master/images/Node-RED_in_out.png "Payload -> Redis") 33 | 34 | Sample flow: 35 | ```javascript 36 | [{"id":"31f1fcb.a6a4a04","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":240,"wires":[]},{"id":"80d063ee.78deb8","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":240,"wires":[["4cb7b20e.778d34"]]},{"id":"4cb7b20e.778d34","type":"redis-out","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"rpush","name":"","topic":"test","x":450,"y":200,"wires":[]},{"id":"902b8385.2fdd9","type":"redis-in","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"blpop","name":"","topic":"test","timeout":0,"x":450,"y":240,"wires":[["31f1fcb.a6a4a04"]]},{"id":"6373a8a4.82bad","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"{\"a\":1,\"b\":2}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":200,"wires":[["4cb7b20e.778d34"]]},{"id":"f248c9ea.674658","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"getset","name":"","topic":"timestamp","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":430,"y":300,"wires":[["3351713f.42e916"]]},{"id":"424ef610.bbb3a8","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":300,"wires":[["f248c9ea.674658"]]},{"id":"3351713f.42e916","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":300,"wires":[]},{"id":"ba433622.8c6178","type":"catch","z":"f80412b1.e2ee8","name":"","scope":null,"uncaught":false,"x":460,"y":140,"wires":[["ec2978af.3e0458"]]},{"id":"ec2978af.3e0458","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":140,"wires":[]},{"id":"eccd31de.55e3a","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"set","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":380,"y":360,"wires":[["6ac31b1f.b393c4"]]},{"id":"46f5d0c0.8a7198","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"[\"key\",\"value\"]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":360,"wires":[["eccd31de.55e3a"]]},{"id":"6ac31b1f.b393c4","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":360,"wires":[]},{"id":"9f960d71.749988","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"getset","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":390,"y":420,"wires":[["7cef7ea5.dba3b"]]},{"id":"f16ad786.8b294","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"key","payload":"[\"value\"]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":420,"wires":[["9f960d71.749988"]]},{"id":"7cef7ea5.dba3b","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":420,"wires":[]},{"id":"7e45a50c.240f7c","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"hmset","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":390,"y":540,"wires":[["122ff416.a56b8c"]]},{"id":"488a7f5b.a40628","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"myHash","payload":"{\"a\":1,\"b\":2}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":540,"wires":[["7e45a50c.240f7c"]]},{"id":"122ff416.a56b8c","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":540,"wires":[]},{"id":"aff97bc7.84a578","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":600,"wires":[["1676f695.aa7189"]]},{"id":"2f4295b5.f78d42","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":600,"wires":[]},{"id":"1676f695.aa7189","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"hgetall","name":"","topic":"myHash","params":"{}","paramsType":"json","payloadType":"json","block":false,"x":420,"y":600,"wires":[["2f4295b5.f78d42"]]},{"id":"66098945.141118","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":660,"wires":[["e42d2b3b.6bb"]]},{"id":"e23fbe8.d7505c","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":660,"wires":[]},{"id":"e42d2b3b.6bb","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"sadd","name":"","topic":"mySet","params":"[\"memberA\",\"memberB\",\"memberC\"]","paramsType":"json","payloadType":"json","block":false,"x":410,"y":660,"wires":[["e23fbe8.d7505c"]]},{"id":"2163c482.b8a244","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"get","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":380,"y":480,"wires":[["d2ea99d4.a4c51"]]},{"id":"b5f040ea.800be8","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"key","payload":"[]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":480,"wires":[["2163c482.b8a244"]]},{"id":"d2ea99d4.a4c51","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":480,"wires":[]},{"id":"15f62858.26419","type":"redis-lua-script","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","name":"test","keyval":0,"func":"local text = \"Hello World\"\nreturn text","stored":true,"block":false,"x":370,"y":780,"wires":[["4a40fc2d.b70e2c"]]},{"id":"4f1fc69d.539ee","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":780,"wires":[["15f62858.26419"]]},{"id":"4a40fc2d.b70e2c","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":780,"wires":[]},{"id":"6ebb1fdf.2fce","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":720,"wires":[["5a3d77f4.920fd8"]]},{"id":"bac6944e.4e07","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":720,"wires":[]},{"id":"5a3d77f4.920fd8","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"sismember","name":"","topic":"mySet","params":"[\"memberA\"]","paramsType":"json","payloadType":"json","block":false,"x":430,"y":720,"wires":[["bac6944e.4e07"]]},{"id":"c1c3a5a6.c289f","type":"redis-lua-script","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","name":"test2","keyval":0,"func":"local text = \"Hello2222 World2222\"\nreturn text","stored":false,"block":false,"x":370,"y":840,"wires":[["d8bd4107.683c08"]]},{"id":"9013d649.88c35","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":840,"wires":[["c1c3a5a6.c289f"]]},{"id":"d8bd4107.683c08","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":840,"wires":[]},{"id":"cb44db96.c64bb8","type":"redis-in","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"psubscribe","name":"","topic":"TOPIC:*","timeout":0,"x":460,"y":20,"wires":[["78033f6c.31797"]]},{"id":"78033f6c.31797","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":20,"wires":[]},{"id":"dac5fcff.4c4cb","type":"redis-in","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"subscribe","name":"","topic":"TOPIC:OK","timeout":0,"x":460,"y":80,"wires":[["6f01eb54.501e7c"]]},{"id":"6f01eb54.501e7c","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":80,"wires":[]},{"id":"e0d2924b.210708","type":"redis-out","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"publish","name":"","topic":"TOPIC:OK","x":190,"y":140,"wires":[]},{"id":"d1fc59dc.bd4958","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"{\"a\":1,\"b\":2}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":40,"wires":[["e0d2924b.210708"]]},{"id":"3e05b777.781bd8","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"del","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":380,"y":900,"wires":[["fd036d2b.dc6d38"]]},{"id":"6924006.91553","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"key","payload":"[]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":900,"wires":[["3e05b777.781bd8"]]},{"id":"fd036d2b.dc6d38","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":900,"wires":[]},{"id":"a13d2797.8a7ff","type":"redis-instance","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","name":"","topic":"redis","location":"flow","block":false,"x":130,"y":960,"wires":[]},{"id":"2a93e7ce.341078","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":1000,"wires":[["d89709ef.fd9368"]]},{"id":"d89709ef.fd9368","type":"function","z":"f80412b1.e2ee8","name":"","func":"let redis = context.flow.get('redis');\n\nredis.info().then((data)=>{\n msg.payload = data\n node.send(msg);\n})\n\n/*\nredis.call(\"anycmd\").then((data)=>{\n msg.payload = data\n node.send(msg);\n})*/","outputs":1,"noerr":0,"x":370,"y":1000,"wires":[["f2e9c3a0.8f181"]]},{"id":"f2e9c3a0.8f181","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":1000,"wires":[]},{"id":"a0efbb89.5e42d8","type":"redis-config","z":"","name":"local","options":"{}","cluster":false,"optionsType":"json"}] 37 | ``` 38 | 39 | Interact with Redis Lua script sample flow: 40 | ```javascript 41 | [{"id":"db136c5d713d3cd0","type":"inject","z":"1a15e2364209aeb3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":220,"y":200,"wires":[["5f41ce33e1d008a9"]]},{"id":"4948edd4b85e148f","type":"inject","z":"1a15e2364209aeb3","name":"zadd 1","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 1, \"{\\\"test\\\":1,\\\"hello\\\":\\\"world1\\\", \\\"hexstr\\\": \\\"000102\\\"}\"]","payloadType":"json","x":210,"y":380,"wires":[["f9ccc3e1983f8ec7"]]},{"id":"89734db2e2b504d9","type":"inject","z":"1a15e2364209aeb3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":220,"y":580,"wires":[["ad21bea98339b79e"]]},{"id":"5f41ce33e1d008a9","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: hello world","keyval":0,"func":"\nreturn \"hello world\"","stored":false,"block":false,"x":600,"y":200,"wires":[["44f2c4e99a1e521d"]]},{"id":"44f2c4e99a1e521d","type":"debug","z":"1a15e2364209aeb3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":870,"y":520,"wires":[]},{"id":"29035e2612073aa9","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: ping pong","keyval":0,"func":"local foo = redis.call('ping')\nreturn foo","stored":false,"block":false,"x":590,"y":260,"wires":[["44f2c4e99a1e521d"]]},{"id":"b9f91d2f3fceb024","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: set","keyval":"1","func":"local foo = redis.call('SET', KEYS[1], ARGV[1])\nreturn foo","stored":false,"block":false,"x":570,"y":320,"wires":[["44f2c4e99a1e521d"]]},{"id":"cfb9a1d9d6235c96","type":"inject","z":"1a15e2364209aeb3","name":"set key value","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"key\", \"value\"]","payloadType":"json","x":230,"y":320,"wires":[["b9f91d2f3fceb024"]]},{"id":"f9ccc3e1983f8ec7","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: zadd","keyval":"1","func":"local foo = redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2])\nreturn foo","stored":false,"block":false,"x":580,"y":380,"wires":[["44f2c4e99a1e521d"]]},{"id":"75a5d81a80da3959","type":"inject","z":"1a15e2364209aeb3","name":"zadd 2","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 2, \"{\\\"test\\\":2,\\\"hello\\\":\\\"world2\\\", \\\"hexstr\\\": \\\"030405\\\"}\"]","payloadType":"json","x":210,"y":420,"wires":[["f9ccc3e1983f8ec7"]]},{"id":"c32955b956a9c79a","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: cjson decode","keyval":"1","func":"local foo = cjson.decode(ARGV[2])\n\nreturn foo.test","stored":false,"block":false,"x":600,"y":520,"wires":[["44f2c4e99a1e521d"]]},{"id":"84bd8bb2222e0517","type":"inject","z":"1a15e2364209aeb3","name":"cjson","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 3, \"{\\\"test\\\":3,\\\"hello\\\":\\\"world3\\\", \\\"hexstr\\\": \\\"060708\\\"}\"]","payloadType":"json","x":210,"y":520,"wires":[["c32955b956a9c79a"]]},{"id":"8d8b9d844348773c","type":"inject","z":"1a15e2364209aeb3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":220,"y":260,"wires":[["29035e2612073aa9"]]},{"id":"15959eedb0e32c26","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: substring","keyval":"1","func":"local foo = tonumber(string.sub(KEYS[1], -2, -1))\n\nreturn foo","stored":false,"block":false,"x":590,"y":640,"wires":[["44f2c4e99a1e521d"]]},{"id":"b66f5d8e92e9fbbf","type":"inject","z":"1a15e2364209aeb3","name":"substring","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 3, \"{\\\"test\\\":3,\\\"hello\\\":\\\"world3\\\", \\\"hexstr\\\": \\\"060708\\\"}\"]","payloadType":"json","x":220,"y":640,"wires":[["15959eedb0e32c26"]]},{"id":"2a7fac1573b38b98","type":"inject","z":"1a15e2364209aeb3","name":"zadd 3","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 3, \"{\\\"test\\\":3,\\\"hello\\\":\\\"world3\\\", \\\"hexstr\\\": \\\"060708\\\"}\"]","payloadType":"json","x":210,"y":460,"wires":[["f9ccc3e1983f8ec7"]]},{"id":"ad21bea98339b79e","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: cjson encode","keyval":"0","func":"local foo = {}\n\nfoo.field1 = 1\nfoo.field2 = \"hello world\"\nfoo.field3 = {}\nfoo.field3.name = \"paul\"\n\nreturn cjson.encode(foo)","stored":false,"block":false,"x":600,"y":580,"wires":[["41d9477be860b123"]]},{"id":"41d9477be860b123","type":"json","z":"1a15e2364209aeb3","name":"","property":"payload","action":"","pretty":false,"x":790,"y":580,"wires":[["44f2c4e99a1e521d"]]},{"id":"b6378268209d3f41","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: zcount","keyval":"1","func":"local elementCount = redis.call('ZCOUNT', KEYS[1], \"-inf\", \"+inf\")\n\nreturn elementCount","stored":false,"block":false,"x":580,"y":700,"wires":[["44f2c4e99a1e521d"]]},{"id":"1efddadd63d15077","type":"inject","z":"1a15e2364209aeb3","name":"zcount","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 4, \"{\\\"test\\\":4,\\\"hello\\\":\\\"world4\\\", \\\"hexstr\\\": \\\"090a0b\\\"}\"]","payloadType":"json","x":210,"y":700,"wires":[["b6378268209d3f41"]]},{"id":"e49699a6dde3b6de","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: zrange","keyval":"1","func":"local elementCount = redis.call('ZRANGE', KEYS[1], 0, 3)\n\nreturn elementCount","stored":false,"block":false,"x":580,"y":760,"wires":[["44f2c4e99a1e521d"]]},{"id":"5da7c2d8aea3b596","type":"inject","z":"1a15e2364209aeb3","name":"zrange","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 4, \"{\\\"test\\\":4,\\\"hello\\\":\\\"world4\\\", \\\"hexstr\\\": \\\"090a0b\\\"}\"]","payloadType":"json","x":210,"y":760,"wires":[["e49699a6dde3b6de"]]},{"id":"e9aa29233106c2c8","type":"inject","z":"1a15e2364209aeb3","name":"concat","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 4, \"{\\\"test\\\":4,\\\"hello\\\":\\\"world4\\\", \\\"hexstr\\\": \\\"090a0b\\\"}\"]","payloadType":"json","x":210,"y":820,"wires":[["f513666fe9a2c2ca"]]},{"id":"f513666fe9a2c2ca","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: complex","keyval":"1","func":"local response = \"\"\nlocal elementCount = redis.call('ZRANGE', KEYS[1], 0, 3)\n\nfor i=1,3,1\ndo\n response = response .. cjson.decode(elementCount[i]).hexstr\nend\n\nreturn response","stored":false,"block":false,"x":590,"y":820,"wires":[["44f2c4e99a1e521d"]]},{"id":"08ae710220cd5170","type":"redis-config","name":"Local","options":"{}","cluster":false,"optionsType":"json"}] 42 | ``` 43 | -------------------------------------------------------------------------------- /icons/redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chameleonbr/node-red-contrib-redis/5865f2dc7755b1a81d654b7392015da22ce5021f/icons/redis.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-contrib-redis", 3 | "version": "1.3.9", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "async": { 8 | "version": "3.2.2", 9 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", 10 | "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" 11 | }, 12 | "cluster-key-slot": { 13 | "version": "1.1.0", 14 | "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", 15 | "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" 16 | }, 17 | "debug": { 18 | "version": "4.3.4", 19 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 20 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 21 | "requires": { 22 | "ms": "2.1.2" 23 | } 24 | }, 25 | "denque": { 26 | "version": "1.5.1", 27 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", 28 | "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" 29 | }, 30 | "ioredis": { 31 | "version": "4.28.5", 32 | "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.5.tgz", 33 | "integrity": "sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==", 34 | "requires": { 35 | "cluster-key-slot": "^1.1.0", 36 | "debug": "^4.3.1", 37 | "denque": "^1.1.0", 38 | "lodash.defaults": "^4.2.0", 39 | "lodash.flatten": "^4.4.0", 40 | "lodash.isarguments": "^3.1.0", 41 | "p-map": "^2.1.0", 42 | "redis-commands": "1.7.0", 43 | "redis-errors": "^1.2.0", 44 | "redis-parser": "^3.0.0", 45 | "standard-as-callback": "^2.1.0" 46 | } 47 | }, 48 | "lodash.defaults": { 49 | "version": "4.2.0", 50 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 51 | "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" 52 | }, 53 | "lodash.flatten": { 54 | "version": "4.4.0", 55 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 56 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" 57 | }, 58 | "lodash.isarguments": { 59 | "version": "3.1.0", 60 | "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", 61 | "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" 62 | }, 63 | "ms": { 64 | "version": "2.1.2", 65 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 66 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 67 | }, 68 | "p-map": { 69 | "version": "2.1.0", 70 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", 71 | "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" 72 | }, 73 | "redis-commands": { 74 | "version": "1.7.0", 75 | "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", 76 | "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" 77 | }, 78 | "redis-errors": { 79 | "version": "1.2.0", 80 | "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", 81 | "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" 82 | }, 83 | "redis-parser": { 84 | "version": "3.0.0", 85 | "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", 86 | "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", 87 | "requires": { 88 | "redis-errors": "^1.0.0" 89 | } 90 | }, 91 | "standard-as-callback": { 92 | "version": "2.1.0", 93 | "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", 94 | "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-contrib-redis", 3 | "version": "1.4.0", 4 | "description": "Node Red client for Redis with pub/sub, list, lua scripting and other commands support.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/chameleonbr/node-red-contrib-redis.git" 12 | }, 13 | "keywords": [ 14 | "redis", 15 | "node-red", 16 | "node" 17 | ], 18 | "node-red": { 19 | "nodes": { 20 | "redis": "redis.js" 21 | } 22 | }, 23 | "author": { 24 | "name": "Andre Alexandre Avila" 25 | }, 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/chameleonbr/node-red-contrib-redis/issues" 29 | }, 30 | "homepage": "https://github.com/chameleonbr/node-red-contrib-redis#readme", 31 | "maintainers": [ 32 | { 33 | "name": "chameleonbr", 34 | "email": "chameleonbr@gmail.com" 35 | } 36 | ], 37 | "dependencies": { 38 | "async": "^3.2.0", 39 | "ioredis": "^5.4.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /redis.html: -------------------------------------------------------------------------------- 1 | 35 | 36 | 51 | 52 | 89 | 90 | 121 | 122 | 128 | 129 | 130 | 163 | 164 | 192 | 193 | 199 | 200 | 248 | 249 | 646 | 647 | 651 | 652 | 653 | 733 | 734 | 766 | 767 | 776 | 777 | 778 | 779 | 810 | 811 | 832 | 833 | -------------------------------------------------------------------------------- /redis.js: -------------------------------------------------------------------------------- 1 | module.exports = function (RED) { 2 | "use strict"; 3 | const Redis = require("ioredis"); 4 | const async = require("async"); 5 | let connections = {}; 6 | let usedConn = {}; 7 | 8 | function RedisConfig(n) { 9 | RED.nodes.createNode(this, n); 10 | this.name = n.name; 11 | this.cluster = n.cluster; 12 | if (this.optionsType === "") { 13 | this.options = n.options; 14 | } else { 15 | RED.util.evaluateNodeProperty(n.options, n.optionsType,this,undefined,(err,value) => { 16 | if(!err) { 17 | // Check if value is a string and optionsType is "env" 18 | if (typeof value === 'string' && n.optionsType === "env") { 19 | try { 20 | this.options = JSON.parse(value); // Attempt to parse JSON 21 | } catch (e) { 22 | console.warn("Failed to parse env as JSON string in redis-config node, use plain value:", e); 23 | this.options = value; // Keep the value as is if it's not valid JSON 24 | } 25 | } else { 26 | this.options = value; 27 | } 28 | } 29 | }); 30 | } 31 | } 32 | RED.nodes.registerType("redis-config", RedisConfig); 33 | 34 | function RedisIn(n) { 35 | RED.nodes.createNode(this, n); 36 | this.server = RED.nodes.getNode(n.server); 37 | this.command = n.command; 38 | this.name = n.name; 39 | this.topic = n.topic; 40 | this.obj = n.obj; 41 | this.timeout = n.timeout; 42 | let node = this; 43 | let client = getConn(this.server, n.id); 44 | let running = true; 45 | 46 | node.on("close", async (undeploy, done) => { 47 | node.status({}); 48 | disconnect(node.id); 49 | client = null; 50 | running = false; 51 | done(); 52 | }); 53 | 54 | if (node.command === "psubscribe") { 55 | client.on("pmessage", function (pattern, channel, message) { 56 | var payload = null; 57 | try { 58 | if(node.obj){ 59 | payload = JSON.parse(message); 60 | }else{ 61 | payload = message; 62 | } 63 | } catch (err) { 64 | payload = message; 65 | } finally { 66 | node.send({ 67 | pattern: pattern, 68 | topic: channel, 69 | payload: payload, 70 | }); 71 | } 72 | }); 73 | client[node.command](node.topic, (err, count) => { 74 | node.status({ 75 | fill: "green", 76 | shape: "dot", 77 | text: "connected", 78 | }); 79 | }); 80 | } else if (node.command === "subscribe") { 81 | client.on("message", function (channel, message) { 82 | var payload = null; 83 | try { 84 | if(node.obj){ 85 | payload = JSON.parse(message); 86 | }else{ 87 | payload = message; 88 | } 89 | } catch (err) { 90 | payload = message; 91 | } finally { 92 | node.send({ 93 | topic: channel, 94 | payload: payload, 95 | }); 96 | } 97 | }); 98 | client[node.command](node.topic, (err, count) => { 99 | node.status({ 100 | fill: "green", 101 | shape: "dot", 102 | text: "connected", 103 | }); 104 | }); 105 | } else { 106 | async.whilst( 107 | (cb) => { 108 | cb(null, running); 109 | }, 110 | (cb) => { 111 | client[node.command](node.topic, Number(node.timeout)) 112 | .then((data) => { 113 | if (data !== null && data.length == 2) { 114 | var payload = null; 115 | try { 116 | if(node.obj){ 117 | payload = JSON.parse(data[1]); 118 | }else{ 119 | payload = data[1]; 120 | } 121 | } catch (err) { 122 | payload = data[1]; 123 | } finally { 124 | node.send({ 125 | topic: node.topic, 126 | payload: payload, 127 | }); 128 | } 129 | } 130 | cb(null); 131 | }) 132 | .catch((e) => { 133 | RED.log.info(e.message); 134 | running = false; 135 | }); 136 | }, 137 | () => {} 138 | ); 139 | } 140 | node.status({ 141 | fill: "green", 142 | shape: "dot", 143 | text: "connected", 144 | }); 145 | } 146 | 147 | RED.nodes.registerType("redis-in", RedisIn); 148 | 149 | function RedisOut(n) { 150 | RED.nodes.createNode(this, n); 151 | this.server = RED.nodes.getNode(n.server); 152 | this.command = n.command; 153 | this.name = n.name; 154 | this.topic = n.topic; 155 | this.obj = n.obj; 156 | var node = this; 157 | 158 | let client = getConn(this.server, node.server.name); 159 | 160 | node.on("close", function (done) { 161 | node.status({}); 162 | disconnect( node.server.name); 163 | client = null; 164 | done(); 165 | }); 166 | 167 | node.on("input", function (msg, send, done) { 168 | var topic; 169 | send = send || function() { node.send.apply(node,arguments) } 170 | done = done || function(err) { if(err)node.error(err, msg); } 171 | if (msg.topic !== undefined && msg.topic !== "") { 172 | topic = msg.topic; 173 | } else { 174 | topic = node.topic; 175 | } 176 | if (topic === "") { 177 | done(new Error("Missing topic, please send topic on msg or set Topic on node.")); 178 | } else { 179 | try { 180 | if(node.obj){ 181 | client[node.command](topic, JSON.stringify(msg.payload)); 182 | }else{ 183 | client[node.command](topic, msg.payload); 184 | } 185 | done(); 186 | } catch (err) { 187 | done(err); 188 | } 189 | } 190 | }); 191 | } 192 | RED.nodes.registerType("redis-out", RedisOut); 193 | 194 | function RedisCmd(n) { 195 | RED.nodes.createNode(this, n); 196 | this.server = RED.nodes.getNode(n.server); 197 | this.command = n.command; 198 | this.name = n.name; 199 | this.topic = n.topic; 200 | this.params = n.params; 201 | var node = this; 202 | this.block = n.block || false; 203 | let id = this.block ? n.id : this.server.name; 204 | 205 | let client = getConn(this.server, id); 206 | 207 | node.on("close", function (done) { 208 | node.status({}); 209 | disconnect(id); 210 | client = null; 211 | done(); 212 | }); 213 | 214 | node.on("input", function (msg, send, done) { 215 | let topic = undefined; 216 | send = send || function() { node.send.apply(node,arguments) } 217 | done = done || function(err) { if(err)node.error(err, msg); } 218 | 219 | if (msg.topic !== undefined && msg.topic !== "") { 220 | topic = msg.topic; 221 | } else if (node.topic && node.topic !== "") { 222 | try { 223 | topic = node.topic; 224 | } catch (e) { 225 | topic = undefined; 226 | } 227 | } 228 | let payload = undefined; 229 | 230 | if (msg.payload) { 231 | let type = typeof msg.payload; 232 | switch (type) { 233 | case "string": 234 | if (msg.payload.length > 0) { 235 | payload = msg.payload; 236 | } 237 | break; 238 | case "object": 239 | if (Array.isArray(msg.payload)) { 240 | if (msg.payload.length > 0) { 241 | payload = msg.payload; 242 | } 243 | break; 244 | } 245 | if (Object.keys(msg.payload).length > 0) { 246 | payload = msg.payload; 247 | } 248 | break; 249 | } 250 | } else if ( 251 | node.params && 252 | node.params !== "" && 253 | node.params !== "[]" && 254 | node.params !== "{}" 255 | ) { 256 | try { 257 | payload = JSON.parse(node.params); 258 | } catch (e) { 259 | payload = undefined; 260 | } 261 | } 262 | 263 | let response = function (err, res) { 264 | if (err) { 265 | done(err); 266 | } else { 267 | msg.payload = res; 268 | send(msg); 269 | done(); 270 | } 271 | }; 272 | 273 | if (!payload) { 274 | payload = topic; 275 | topic = undefined; 276 | } 277 | if (topic) { 278 | client.call(node.command, topic, payload, response); 279 | } else if (payload) { 280 | client.call(node.command, payload, response); 281 | } else { 282 | client.call(node.command, response); 283 | } 284 | }); 285 | } 286 | RED.nodes.registerType("redis-command", RedisCmd); 287 | 288 | function RedisLua(n) { 289 | RED.nodes.createNode(this, n); 290 | this.server = RED.nodes.getNode(n.server); 291 | this.func = n.func; 292 | this.name = n.name; 293 | this.keyval = n.keyval; 294 | this.stored = n.stored; 295 | this.sha1 = ""; 296 | this.command = "eval"; 297 | var node = this; 298 | this.block = n.block || false; 299 | let id = this.block ? n.id : n.server.name; 300 | 301 | let client = getConn(this.server, id); 302 | 303 | node.on("close", function (done) { 304 | node.status({}); 305 | disconnect(id); 306 | client = null; 307 | done(); 308 | }); 309 | if (node.stored) { 310 | client.script("load", node.func, function (err, res) { 311 | if (err) { 312 | node.status({ 313 | fill: "red", 314 | shape: "dot", 315 | text: "script not loaded", 316 | }); 317 | } else { 318 | node.status({ 319 | fill: "green", 320 | shape: "dot", 321 | text: "script loaded", 322 | }); 323 | node.sha1 = res; 324 | } 325 | }); 326 | } 327 | 328 | node.on("input", function (msg, send, done) { 329 | send = send || function() { node.send.apply(node,arguments) } 330 | done = done || function(err) { if(err)node.error(err, msg); } 331 | if (node.keyval > 0 && !Array.isArray(msg.payload)) { 332 | throw Error("Payload is not Array"); 333 | } 334 | 335 | var args = null; 336 | if (node.stored) { 337 | node.command = "evalsha"; 338 | args = [node.sha1, node.keyval].concat(msg.payload); 339 | } else { 340 | args = [node.func, node.keyval].concat(msg.payload); 341 | } 342 | client[node.command](args, function (err, res) { 343 | if (err) { 344 | done(err); 345 | } else { 346 | msg.payload = res; 347 | send(msg); 348 | done(); 349 | } 350 | }); 351 | }); 352 | } 353 | RED.nodes.registerType("redis-lua-script", RedisLua); 354 | 355 | function RedisInstance(n) { 356 | RED.nodes.createNode(this, n); 357 | this.server = RED.nodes.getNode(n.server); 358 | this.location = n.location; 359 | this.name = n.name; 360 | this.topic = n.topic; 361 | let id = n.id; 362 | var node = this; 363 | let client = getConn(this.server, id); 364 | 365 | this.context()[node.location].set(node.topic, client); 366 | node.status({ 367 | fill: "green", 368 | shape: "dot", 369 | text: "ready", 370 | }); 371 | 372 | node.on("close", function (done) { 373 | node.status({}); 374 | this.context()[node.location].set(node.topic, null); 375 | disconnect(id); 376 | client = null; 377 | done(); 378 | }); 379 | } 380 | RED.nodes.registerType("redis-instance", RedisInstance); 381 | 382 | function getConn(config, id) { 383 | if (connections[id]) { 384 | usedConn[id]++; 385 | return connections[id]; 386 | } 387 | 388 | let options = config.options; 389 | 390 | if (!options) { 391 | return config.error( 392 | "Missing options in the redis config - Are you upgrading from old version?", 393 | null 394 | ); 395 | } 396 | try { 397 | if (config.cluster) { 398 | connections[id] = new Redis.Cluster(options); 399 | } else { 400 | connections[id] = new Redis(options); 401 | } 402 | 403 | connections[id].on("error", (e) => { 404 | config.error(e, null); 405 | }); 406 | 407 | if (usedConn[id] === undefined) { 408 | usedConn[id] = 1; 409 | } 410 | return connections[id]; 411 | } catch (e) { 412 | config.error(e.message, null); 413 | } 414 | } 415 | 416 | function disconnect(id) { 417 | if (usedConn[id] !== undefined) { 418 | usedConn[id]--; 419 | } 420 | if (connections[id] && usedConn[id] <= 0) { 421 | connections[id].disconnect(); 422 | delete connections[id]; 423 | } 424 | } 425 | }; 426 | --------------------------------------------------------------------------------