halyssonfreitassenseup
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # How to create a custom plugin for SenseFlow SenseFlow is a platform that enables low-code development for integrations between systems. Although focused on industry solutions, it can be used for non-industry solutions as well. ## What are plugins? Plugins are extensions for the SenseFlow platform, offering new features to users and assisting in the transformation of data inside the flows. A plugin frequently offers a collection of nodes. A node is a visual representation of a feature for the SenseFlow plataform. A node can receive, process and send messages both to the flow and to external applications. Plugins are developed using Javascript or Typescript. In the following steps, you will learn how to create and use a new plugin in a SenseFlow instance. If you are already familiar with the process of creating one, you can skip to [here](#Contributing) to get a template project with most of the configuration set up. ## Starting an example project For your first project, you are tasked with creating a plugin that allows users to capitalize all first letters in a string, and you will be using Typescript to develop it. ### Prerequisites - **Node v14.19.0** - this is the default version that we use to run our SenseFlow instances. You could also use a higher version when running locally, but it's better to adhere to the default; ### Create the package with npm To start, you need to initialize your project with npm. We will be using the `~/examples` folder, but you can choose any folder that you'd like. By default, the package name of our plugins follows the format `@senseup/senseflow-<name-of-the-plugin>`. In this project, we will use `@senseup/senseflow-plugin-text-functions`. > ~$ mkdir examples/senseflow-plugin-text-functions ~$ cd examples/senseflow-plugin-text-functions ~$ npm init ... package name: @senseup/senseflow-plugin-text-functions version: (1.0.0) description: This package has many functions to manipulate text. entry point: (index.js) test command: git repository: keywords: author: SenseUp <squad@senseup.tech> license: (ISC) ... Is this OK? (yes) ~$ ### Installing dependencies ```sh npm i typescript @types/node@^14 @types/node-red@1.2.0 -D ``` ### Create the main files of the project The plugin that you're creating will contain only one node. This node needs to be able to receive a sentence and produce this modified sentence with all first letters capitalized. - src/capitalize-all-first-letters/capitalize-all-first-letters.ts ```typescript= ! import { Node, NodeDef, NodeInitializer, NodeMessage } from "node-red"; // his is the object that represente the node // The decision of make this global is to better writer functions let NODE: Node; // This function is registered below ( NODE.on('input', nodeOnInput) ) // to be the event listener of a input message. function nodeOnInput(msg: NodeMessage) { /** * Here, you will construct all code to treat the input message, * construct the message to output, and output it by NODE.send(msg) */ NODE.send(msg); } const capitalize_all_first_letters: NodeInitializer = function (RED) { function CapitalizeAllFirstLetters(config: NodeDef) { RED.nodes.createNode(this, config) NODE = this NODE.on('input', nodeOnInput) } RED.nodes.registerType("capitalize-all-first-letters", CapitalizeAllFirstLetters) } export = capitalize_all_first_letters ``` - src/capitalize-all-first-letters/capitalize-all-first-letters.html ```html= <script type="text/javascript"> RED.nodes.registerType('capitalize-all-first-letters', { category: 'Senseup Text', color: '#a6bbcf', defaults: { name: { value: "" } }, inputs:1, outputs:1, icon: "file.png", label: function() { return this.name||"capitalize-all-first-letters"; } }); </script> <script type="text/html" data-template-name="capitalize-all-first-letters"> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <input type="text" id="node-input-name" placeholder="Name"> </div> </script> <script type="text/html" data-help-name="capitalize-all-first-letters"> <p>A simple node that converts message payloads into a sentence with the first letters of the words capitalized.</p> </script> ``` > Tip: further details can be found in https://nodered.org/docs/creating-nodes/first-node ### Adding the SenseFlow node registration In your `package.json`, add the following setting: ```json=! "node-red": { "nodes": { "capitalize-all-first-letters": "dist/capitalize-all-first-letters/capitalize-all-first-letters.js" } } ``` This signals the SenseFlow instance that a node of type `capitalize-all-first-letters` is defined in the file `dist/capitalize-all-first-letters/capitalize-all-first-letters.js`. ### Create the TypeScript configuration file This file will instruct the tsc (TypeScript compiler) on how to transpile files from the *.ts (TypeScript) format to the *.js (JavaScript) format. - Create a **tsconfig.json** file in the root of your project and include the configurations below: ```json= { "compilerOptions": { "allowSyntheticDefaultImports": true, "alwaysStrict": true, "esModuleInterop": true, "moduleResolution": "node", "typeRoots": [ "node_modules/@types" ], "rootDir": "src", "module": "CommonJS", "target": "ES5", "importHelpers": true, "lib": [ "ES5", "ES6" ], "outDir": "dist" }, "include": [ "src" ], "exclude": [ "node_modules" ] } ``` > Tip: For more details about `tsconfig.json`: https://www.typescriptlang.org/pt/docs/handbook/tsconfig-json.html ### Including the shell script to aux the build process The next chapter will config the process to call the traspile. This is a append to aux the build process because the traspile only works with *.ts files. We need that other files and folders be copied to the distribution folder. * In the root of the project, create a file `copy_files.sh` : ```bash= #!/bin/bash src=$(pwd)"/src" dist=$(pwd)"/dist" for entry in $(ls src); do # copy folder icons if exists # if you want to do it for other folder that you standardized, # then use some like this code below if [ -d "$src/$entry/icons" ]; then cp -r "$src/$entry/icons" "$dist/$entry" fi # copy files from the root of the node in src to dist # not recursively # if you want to add other extensions, do some like this: (html|xml|json|jpeg) for file in $(ls $src/$entry); do if [[ $file =~ .*\.(html) ]]; then cp $src/$entry/$file $dist/$entry/$file fi done done ``` ### Including the build script - In your `package.json`, inside "scripts", add this line: ```json= "build": "tsc && ./copy_files.sh" ``` When we run `npm run build`, `tsc` will be called to perform the transpilation of our files and the shell script will do the remaining work. > Warn: The shell script file must can be set as executable file in operating system. ### Building our plugin For now, your hierarchy of files should look like this: ![](https://i.imgur.com/TVZ6MCw.png) > Tip: The files `.gitignore`, `CHANGELOG.md` and `README.md` has no importance for the functioning of the plugin. Now we have configured the transpiling process, we will execute that first with direct use of **tsc** command in the console. ```bash $ tsc ``` > Tip: check if you stay in root folder in the console See that now the folder **dist** was created, but only have the **capitalize-all-first-letters.js** file, but we need that exist too **capitalize-all-first-letters.html** file. To be more clear to understand the next step, firstly delete **dist** folder. Now, in the console: ```bash $ npm run build ``` And now see the dist folder with the files extensions *.html and *.js, that are all we have at this moment. > Tip: If the `*.html` was not created in dist folde or the some message like `./copy_files.sh: Permission denied` is show, then execute `chmod 777 copy_files.sh` and try again. The project in this moment for the hierarchy of files/folders looks like: ![](https://i.imgur.com/CBw4H2j.png) ## Installing the plugin In order to test our node, we need to install it in a SenseFlow instance. The recommended way to do it when developing is to do a local install. - Clone the SenseFlow repository: ``` $ git clone https://gitlab.com/multicast/senseup/senseflow $ cd senseflow ``` > Notice: To access the repository you **must be authorized** and have previously **signed the confidentiality agreement** relating to the rights of **SenseUp** projects. Create a **.env** file in the root of the project and paste the enviroment variables listed in the README. > Tip: The environment variables that is given in README file are mocked. The real values must be requested from the **SenseUp** support. > Tip: We recomend to add the environment variable DATAFLOW_TITLE="SenseFlow" ![](https://i.imgur.com/J08QGTF.png) - Install dependencies: ``` $ npm i ``` Start the SenseFlow instance: ``` npm run start ``` > Tip: As some enviroment variables might be required for some of the features in SenseFlow, if you encounter an error while trying to start SenseFlow, please contact **SenseUp** for support. > Tip: If you happen to encounter an error like "address already in use :::1880", then some other application is already using this port. You can solve this in two ways: the first is to kill the application that is using this port; the second is to change the port used by SenseFlow and restart it. If you are unsure whether the application using it is critical or not, choose the second option. - **The first** - kill the application - this is by your responsibility, don't do that if you don't know what application is running and your impact. `$ netstat -lutnp` `$ sudo kill -9 <PID>` - **The second** - alter the port `edit src/index.js` `line: server.listen(1880);` If SenseFlow runs correctly, your console will look like this: ![](https://i.imgur.com/yqmkf5z.png) You can also see the SenseFlow editor running in: http://localhost:1880/ ![](https://i.imgur.com/Xb8aJP2.png) ### Instaling the plugin in the SenseFlow One time that we continue in the **senseflow** folder we will installing the plugin using the relative or entire path to this, like below: > It's very important stop the SenseFlow plataform before instaling by command **Ctrl+c** in the console that runs it. After, you will install like below and start again. ``` $ npm i ../senseflow-plugin-text-functions/ ``` ![](https://i.imgur.com/hWk7eWG.png) Se this above image. In left, the pallet exhibit the *capitalize-all-firt-letters* node. In middle, the flow have three nodes, the first is an input with properties name and payload with value `the best solution for low coding integration is senseUp`. The third node is an debug that output the message payload in debug tab at the right. And, the second node, **our node, that for this moment do nothing we want**, only reruns the message genereted by input node. Because our node do nothing, the output in debug tab is the original message. We will implement this in the next chapter. ## Implementing the node functionality Let's return to the directory of our node project and edit the file **capitalize-all-first-letters.ts** . See the function *nodeOnInput*, this is a function that treat the input message and send a message processed to the output. > Tip: the input message coming from input node is a JavaScript object that has the key **payload** with contains our setence, and the key **payload** will be use by the output node to show the result of our node. Now, update your actual code with this: ```typescript=! function capitalizeAllFirstLetters(str) { var splitStr = str.split(' '); for (var i = 0; i < splitStr.length; i++) { // You do not need to check if i is larger than splitStr length, as your for does that for you // Assign it back to the array splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1); } // Directly return the joined string return splitStr.join(' '); } // This function is registered below ( NODE.on('input', nodeOnInput) ) // to be the event listener of a input message. function nodeOnInput(msg: NodeMessage) { /** * Here, you will construct all code to treat the input message, * construct the message to output, and output it by NODE.send(msg) */ msg.payload = capitalizeAllFirstLetters(msg.payload) NODE.send(msg); } ``` > Tip: Don't get confused this name (capitalizeAllFirstLetters) with another almost the same name. They are different stuffs. One time we have installing our plugin by a local npm installation, the npm create a link to our plugin project. With this in mind, it's not necessary to reinstall, otherwise, is necessary to rebuild our plugin and restart the SenseFlow. - In the root folder of our plugin ``` $ npm run build ``` - In the console where our SenseFlow instance is running ``` Ctrl+C $ npm run start ``` Now, by refresh our SenseFlow (http://localhost:1880/) and again inject message, the formated output in debug tab show what we want. See the right side, the debug tab, in image below. ![](https://i.imgur.com/YhPKHiy.png) ## Adding more nodes to the same plugin Init it with the creation of the new folder `capitalize-all-letters` under the `src` folder. ![](https://i.imgur.com/RqdJMoZ.png) * in package.json update the SenseFlow node registration to: ```json=11 ! "node-red": { "nodes": { "capitalize-all-first-letters": "dist/capitalize-all-first-letters/capitalize-all-first-letters.js", "capitalize-all-letters": "dist/capitalize-all-letters/capitalize-all-letters.js" } }, ``` ### Implementing the new node The new node is a simple node that capitalize all letters. - edit src/capitalize-all-letters/capitalize-all-letters.ts ```typescript=! import { NodeDef, NodeInitializer, NodeMessage } from "node-red"; // This is the object that represente the node // The decision of make this global is to better writer functions let NODE; // This function is registered below ( NODE.on('input', nodeOnInput) ) // to be the event listener of a input message. function nodeOnInput(msg: NodeMessage) { /** * Here, you will construct all code to treat the input message, * construct the message to output, and output it by NODE.send(msg) */ msg.payload = msg.payload.toString().toUpperCase() NODE.send(msg); } const capitalize_all_letters: NodeInitializer = function (RED) { function CapitalizeAllLetters(config: NodeDef) { RED.nodes.createNode(this, config) NODE = this NODE.on('input', nodeOnInput) } RED.nodes.registerType("capitalize-all-letters", CapitalizeAllLetters) } export = capitalize_all_letters ``` - edit src/capitalize-all-letters/capitalize-all-letters.html ```html= <script type="text/javascript"> RED.nodes.registerType('capitalize-all-letters', { category: 'Senseup Text', color: '#a6bbcf', defaults: { name: { value: "" } }, inputs:1, outputs:1, icon: "file.png", label: function() { return this.name||"capitalize-all-letters"; } }); </script> <script type="text/html" data-template-name="capitalize-all-letters"> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <input type="text" id="node-input-name" placeholder="Name"> </div> </script> <script type="text/html" data-help-name="capitalize-all-letters"> <p>A simple node that converts message payloads into a sentence with all letters capitalized.</p> </script> ``` ### Rebuild the project How are we going to rebuild our project, we can delete the folder **dist** and : - In the root folder of our plugin ``` $ npm run build ``` - In the console where our SenseFlow instance is running ``` Ctrl+C $ npm run start ``` ![](https://i.imgur.com/2adHqki.png) Now, as can you see, the two nodes are listed in the palette and we use this to generate two outputs, one with all first letters capitalized and other with all letters capitalized. ## Some UI friendly details In this chapter, we will only work with one node, the *capitalize-all-firt-letters*. If you want, the same alterations can be do for whatever node that you implements. ### Alter the icon In the file `src/capitalize-all-first-letters/capitalize-all-first-letters.html `have one `icon: "file.png"`. The SenseFlow allows to set icons from three fonts: own stock, custom icon, and [Font Awesome icon](https://fontawesome.com/v4/icons/). #### Own Stock In this case, only change `icon: "file.png"` to a desire icon like `icon: "white-globe.svg"` ![](https://i.imgur.com/oPwh5fo.png) #### Custom Icon If you want a personalized icon you can do your own icon or choose some in a repository like it [image *.svg](https://www.svgrepo.com/svg/85270/text). Download it and put in `src/capitalize-all-first-letters/icons/`. After, change `src/capitalize-all-first-letters/capitalize-all-first-letters.html` in the line `icon: "file.png"` to `icon: "text-svgrepo-com.svg"`. In the SenseFlow you will see a new look for you node like this: ![](https://i.imgur.com/dcE8vvr.png) #### Font Awesome icon This is a web repository to *.svg files that the SenseFlow offers a ligthweight manner to access your files. Search a icon that you want, copy your name like `fa-text-width` and put it in this way `icon: "font-awesome/fa-text-width"`. ### Node color This is more simple than icons. Just change in `src/capitalize-all-first-letters/capitalize-all-first-letters.html` the `color: '#a6bbcf'` with another hexadecimal number color, like color: '#D1BDEB''. An indicated tool to generate hexadecimal number color is https://htmlcolorcodes.com/ . ### Rebuild the project and restart the SenseFlow For each one or all alteration done you will need to: - Rebuild ``` $ npm run build ``` - Restart the SenseFlow ``` Ctrl+c $ npm run start ``` This is the new appearance of node if you choose Font Awesome icon. ![](https://i.imgur.com/l7XhFEg.png) ## Adding properties and a input feature The idea here is add a property **text** in `capitalize-all-firt-letters` and implement a button, like a button in left side of input node, that will send a message to the output of our node with this **text** propertie value in message payload key. Besides that, add a property **hasInput** that controls if our node will behave like one that have input port or not. - edit src/capitalize-all-letters/capitalize-all-letters.ts - Lines 4-7, 13 and 45: Defines a config object that from extended NodeDef class. This carry the properties that exists in `capitalize-all-letters.html` below. - Line 5: the property **text** that will replace the text from input node. - Line 6: the property **hasInput** that say wheter our node has or hasn't a input port. - Lines 33-37: This conditional block verify the value of **hasInput**. If don't have input port, the message input will be provided by `CONFIG.text`, that gets from text property in html file. ```typescript= import { Node, NodeDef, NodeInitializer, NodeMessage } from "node-red"; // This interface defines the information from node properties interface EnvVarGetNodeDef extends NodeDef { text?: string hasInput?: string | boolean } // This is the object that represente the node // The decision of make this global is to better writer functions let NODE: Node; // This is the object that bring it to us the information from node properties let CONFIG: EnvVarGetNodeDef; function capitalizeAllFirstLetters(str) { var splitStr = str.split(' '); for (var i = 0; i < splitStr.length; i++) { // You do not need to check if i is larger than splitStr length, as your for does that for you // Assign it back to the array splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1); } // Directly return the joined string return splitStr.join(' '); } // This function is registered below ( NODE.on('input', nodeOnInput) ) // to be the event listener of a input message. function nodeOnInput(msg: NodeMessage) { /** * Here, you will construct all code to treat the input message, * construct the message to output, and output it by NODE.send(msg) */ if (CONFIG.hasInput.toString() == 'true') { msg.payload = capitalizeAllFirstLetters(msg.payload) } else { msg.payload = capitalizeAllFirstLetters(CONFIG.text.toString()) } NODE.send(msg); } const capitalize_all_first_letters: NodeInitializer = function (RED) { function CapitalizeAllFirstLetters(config: NodeDef) { RED.nodes.createNode(this, config) NODE = this CONFIG = config NODE.on('input', nodeOnInput) } RED.nodes.registerType("capitalize-all-first-letters", CapitalizeAllFirstLetters) } export = capitalize_all_first_letters ``` - edit src/capitalize-all-letters/capitalize-all-letters.html - Line 7: declaration of the **text** property - Line 8: declaration of the **hasInput** property - Line 10: start **inputs** port quantity of the node with zero - Lines 75-82: html template that represents the properties **text** and **hasInput** - Lines 53-66: this register an event that happens before node edit dialog shows. Inside it, the property **hasInput** is configured to be a select box, with predefined values, instead to be a simple text input. - Lines 45-52: this register an envent that happens when node edit dialog save button is clicked. This evaluates the **hasInput** propertie and set **inputs** port quantity to zero or one. - Lines 16-44: this is a button like a left button in inject node. The code inside it is the same used in inject node to get properties and send it in CONFIG inside *.ts file. ```html= <script type="text/javascript"> RED.nodes.registerType('capitalize-all-first-letters', { category: 'Senseup Text', color: '#D1BDEB', defaults: { name: { value: "" }, text: { value: "" }, hasInput: { value: false } }, inputs: 0, outputs: 1, icon: "font-awesome/fa-text-width", label: function () { return this.name || "capitalize-all-first-letters"; }, button: { onclick: function () { var label = this._def.label.call(this); if (label.length > 30) { label = label.substring(0, 50) + "..."; } label = label.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); var node = this; $.ajax({ url: "inject/" + this.id, type: "POST", success: function (resp) { RED.notify(node._("inject.success", { label: label }), { type: "success", id: "inject", timeout: 2000 }); }, error: function (jqXHR, textStatus, errorThrown) { if (jqXHR.status == 404) { RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.not-deployed") }), "error"); } else if (jqXHR.status == 500) { RED.notify(node._("common.notification.error", { message: node._("inject.errors.failed") }), "error"); } else if (jqXHR.status == 0) { RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.no-response") }), "error"); } else { RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.unexpected", { status: jqXHR.status, message: textStatus }) }), "error"); } } }); }, }, oneditsave: function () { const hasInput = $("#node-input-hasInput").val() == 'true' if (hasInput) { this.inputs = 1 } else { this.inputs = 0 } }, oneditprepare: function () { $("#node-input-hasInput").typedInput({ types: [ { value: "bool", options: [ { value: "true", label: "TRUE" }, { value: "false", label: "FALSE" }, ] } ] }) } }); </script> <script type="text/html" data-template-name="capitalize-all-first-letters"> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <input type="text" id="node-input-name" placeholder="Name"> </div> <div class="form-row"> <label for="node-input-text"><i class="fa fa-usd"></i> Text</label> <input type="text" id="node-input-text" placeholder="Text"> </div> <div class="form-row"> <label for="node-input-hasInput"><i class="fa fa-tag"></i> has input?</label> <input type="text" id="node-input-hasInput" placeholder="HasInput"> </div> </script> <script type="text/html" data-help-name="capitalize-all-first-letters"> <p>A simple node that converts message payloads into a sentence with all first letters of words capitalized.</p> </script> ``` As you can see, the node dialog exhibit the properties **text** and **hasInput**. Observe that the text property value is another that the value in payload property in inject value and hasInput property has false value. The inject node doesn't wire to our node, and if you try it, you won't be able. The output in debug tab is the formated sentence from text property. > Tip: don't forget to hit the button on the left side of the node ![](https://i.imgur.com/G2AmI70.png) ### Download this project [Link to GitLab](https://gitlab.com/halysson/senseflow.plugin.fext.functions) to download all things that we did from begin to this moment. ## Working with node status and some message details For this, we will do other project, one of work with a state of connected or disconected. A remote instance of Redis will be used. > Tip: create you own free remote instance of Redis database with [Redis Labs](https://app.redislabs.com/#/login), if you need some help, see the [how to](https://hackmd.io/@halyssonfreitassenseup/SkoDYjUlc). The new plugin project has the package name `@senseup/senseflow-redis` and will be three nodes: ```json "redis-connection": "dist/redis-connection/redis-connection.js", "redis-set": "dist/redis-set/redis-set.js", "redis-get": "dist/redis-get/redis-get.js" ``` Only the node `redis-connection` we will implement now, the other two you is able to do with the knowledge acquired so far. - src/redis-connection/redis-connection.js ```typescript= import { Node, NodeDef, NodeInitializer, NodeMessage } from "node-red"; import Redis from 'ioredis' interface redisConnectionNodeDef extends NodeDef { host?: string | undefined; port?: string | undefined; password?: string | undefined; } interface Arguments { arguments: Array<string> } interface redisConnectionNodeMessage extends NodeMessage { command?: string | undefined; key?: string | undefined; arguments?: Arguments | undefined; } let NODE: Node; let CONFIG: redisConnectionNodeDef; function nodeOnInput(msg: redisConnectionNodeMessage) { const args = [msg.key] if (arguments) args.push(...msg.arguments.arguments) redisClient .send_command(msg.command.toString(), args) .then(result => { const response_msg: NodeMessage = { _msgid: msg._msgid, payload: result, topic: (msg.command.toString() + args.reduce((acum, str) => { acum += str + " " return acum }, " ")).trim() } NODE.send(response_msg); }) .catch(err => { const response_msg: NodeMessage = { _msgid: msg._msgid, payload: err, topic: (msg.command.toString() + args.reduce((acum, str) => { acum += str + " " return acum }, " ")).trim() } NODE.send(response_msg); }) } let redisClient: Redis.Redis function connection() { NODE.status({ fill: "yellow", shape: "dot", text: "Connecting ..." }) if (!CONFIG.host || !CONFIG.port || !CONFIG.password) { NODE.status({ fill: "red", shape: "dot", text: "Disconnected" }) if (redisClient) redisClient.quit() return } try { NODE.log(CONFIG.host.toString()) NODE.log(Number(CONFIG.port.toString())) NODE.log(CONFIG.password.toString()) redisClient = new Redis({ host: CONFIG.host.toString(), port: Number(CONFIG.port.toString()), password: CONFIG.password.toString(), lazyConnect: true, autoResubscribe: false, }) redisClient.connect() .catch(async error => { await redisClient.quit() NODE.status({ fill: "red", shape: "dot", text: "Disconnected" }) }) NODE.status({ fill: "green", shape: "dot", text: "Connected" }) } catch (error) { NODE.status({ fill: "red", shape: "dot", text: "Disconnected" }) } } const redis_connection: NodeInitializer = function (RED) { function RedisConnection(config: NodeDef) { RED.nodes.createNode(this, config) NODE = this CONFIG = config NODE.on('input', nodeOnInput) connection() } RED.nodes.registerType("redis-connection", RedisConnection) } export = redis_connection ``` - src/redis-connection/redis-connection.html ```html= <script type="text/javascript"> RED.nodes.registerType('redis-connection', { category: 'Senseup Redis', color: '#FFAAAA', defaults: { name: { value: "" }, host: { value: "" }, port: { value: "" }, password: {type:"password"} }, inputs:1, outputs:1, icon: "font-awesome/fa-plug", label: function() { return this.name||"redis-connection"; } }); </script> <script type="text/html" data-template-name="redis-connection"> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <input type="text" id="node-input-name" placeholder="Name"> </div> <div class="form-row"> <label for="node-input-host"><i class="fa fa-tag"></i> Host</label> <input type="text" id="node-input-host" placeholder="Host"> </div> <div class="form-row"> <label for="node-input-port"><i class="fa fa-tag"></i> Port</label> <input type="text" id="node-input-port" placeholder="Port"> </div> <div class="form-row"> <label for="node-input-password"><i class="fa fa-tag"></i> Password</label> <input type="text" id="node-input-password" placeholder="Password"> </div> </script> <script type="text/html" data-help-name="redis-connection"> <p>A simple node that connects to a Redis endpoint and comunicate with it.</p> </script> ``` ### Install dependencies It needs to install this dependencies: ``` $ npm i ioredis@4.27.5 $ npm i @types/ioredis@4.27.6 ``` The other tasks to do is the same executed is previus chapters. Now, install this in the SenseFlow and you will see our new node displayed in SenseFlow palette. ### Understanding what the plugin does ![](https://i.imgur.com/7ET3QBl.png) The `redis-connection` node has three important properties: **Host**, **Port** and **Password**. In the image, these value are mockeds. ### The node status If these values are valid, the node will connect to a Redis database and show a little green circle with label 'connected'. This is because the line 79 in our *.ts code: ```typescript=79 NODE.status({ fill: "green", shape: "dot", text: "Connected" }) ``` > Tip: try false values in our node properties and find this behavior in the *.ts file. ### The node required message This node receive a json message thats have the follow format: ```json= { "command": "", "key": "", "arguments" :{ "arguments": [ "" ] } } ``` The way we preper this json message is a UI interface providade by input property dialog. > Tip: the other two nodes yet not implemented are to you replace this actual solution using input node. ![](https://i.imgur.com/OkisII5.png) The image above show to us the input (named SET) node properties, in the other way, this prepare the follow json: ```json= { "command": "set", "key": "SenseUp", "arguments" :{ "arguments": [ "The best platform to low coding integration!" ] } } ``` The input (named GET) has a more simple json: ```json= { "command": "get", "key": "SenseUp", } ``` ## Contributing Congratulations, you have finished the tutorial! You can now begin contributing new plugins to the SenseFlow platform. Any new plugin should be created from the [template project](https://gitlab.com/multicast/senseup/senseflow.template.plugin), as it has most of the configuration set up and helps you focus on the features that need to be developed.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully