From Nodered to Tulip Custom Widget

One way to realtime update a custom Tulip widget (within the Tulip player (or chrome web player)) is to make an update to a Tulip Table Record.

Let’s begin

Create a tulip table, lets call it guage_test. And create a record of ID 1 value 50

(I realized I spelled gauge incorrectly on the table, sorry deal with it 🙂

To get data into the table lets create a tulip Bot, lets call it table_bot and give it tables:read and write permissions. Save the secret, because you can’t view it again.

Fire up node red and add the node-red-tulip-api to your Palette

https://flows.nodered.org/node/@tulip/node-red-tulip-api

Drop the tulip-tables node and then configure the API portion with the values from creating the BOT.

Now set it to update a record, you will need a Table ID. Easiest way I know is to get it from the Url of the table. Copy the unique id (everything after table/) and paste into Table ID

And set the Record ID to 1, you could do this dynamically of course, this is just a simple demo.

Next we need to get the field name, it will be a unique id appended to your field you created. You can use nodered to get this. Simply leave the request body as {} and it will return you the current record.

Add an inject before and a debug after to catch the return message. Sorry tiny image, but it tells us our ‘value’ is referred to by “jnifa_value”

Great, now we can craft the request body

Edit your inject node, change to msg.body then change to output of JSON.

Use the {“name_of_your_value”: 1}

Now if you deploy and click the inject node you should see in the debug area your update record.

If you look at your tulip table it will reflect the new value as well.

Great, so now we have data flowing into a tultip table. Now let’s widgetify it.

Go to the custom widget page and create a new one. I used this cool animated gauge http://codepen.io/naikus/pen/BzkoLL to start

In the html section paste

<div id="gauge6" class="gauge-container six">
  <span class="label">.six</span>
</div>

In the javascript section paste the below. This uses a loadScript to load gauge.min.js async and then calls a ‘main’ function to create the gauge and the getValue function. There is probably a better way to do this (let me know what it is 🙂

async function loadScripts(...urls) {
  for (const thisUrl of urls) {
    console.log(`Loading script "${thisUrl}"...`);

    const response = await fetch(thisUrl);
    const responseBody = await response.text();

    const scriptElm = document.createElement("script");
    const inlineCode = document.createTextNode(responseBody);
    scriptElm.appendChild(inlineCode);
    document.body.appendChild(scriptElm);

    console.log(`Script "${thisUrl}" successfully loaded`);
    main();
  }
}

loadScripts('https://rawgit.com/naikus/svg-gauge/master/dist/gauge.min.js');

function main() {
    
var gauge6 = Gauge(
  document.getElementById("gauge6"), {
    max: 100,
    dialStartAngle: 90.01,
    dialEndAngle: 89.99,
    dialRadius: 10,
    showValue: false,
    value: 50
  }
);


getValue('value',(tempVar) => {
    // console.debug(tempVar);
   gauge6.setValueAnimated(tempVar, 1);
})
 
    
}

Finally, paste in the CSS

body {
  background-color: rgba(0,0,0,0.8);
  color: #999;
  font-family: Hevletica, sans-serif;
}

.info {
  clear: both;
  padding: 10px;
  font-size: 0.9em;
}
a.link {
  color: rgb(47, 227, 255);
  text-decoration: none;
}

.warn {
  font-size: 0.8em;
  background-color: darken(orange, 15%);
  color: #fff;
  padding: 10px;
}

/* ------ Default Style ---------- */
.gauge-container {
  width: 150px;
  height: 150px;
  display: block;
  float: left;
  padding: 10px;
  background-color: #222;
  margin: 7px;
  border-radius: 3px;
  position: relative;
}
.gauge-container > .label {
  position: absolute;
  right: 0;
  top: 0;
  display: inline-block;
  background: rgba(0,0,0,0.5);
  font-family: monospace;
  font-size: 0.8em;
  padding: 5px 10px;
}
.gauge-container > .gauge .dial {
  stroke: #334455;
  stroke-width: 2;
  fill: rgba(0,0,0,0);
}
.gauge-container > .gauge .value {
  stroke: rgb(47, 227, 255);
  stroke-width: 2;
  fill: rgba(0,0,0,0);
}
.gauge-container > .gauge .value-text {
  fill: rgb(47, 227, 255);
  font-family: sans-serif;
  font-weight: bold;
  font-size: 0.8em;
}




/* ------- Alternate Style ------- */
.wrapper {
  height: 100px;
  float: left;
  margin: 7px;
  overflow: hidden;
}
.wrapper > .gauge-container {
  margin: 0;
}
.gauge-container.two {
}
.gauge-container.two > .gauge .dial {
  stroke: #334455;
  stroke-width: 10;
}
.gauge-container.two > .gauge .value {
  stroke: orange;
  stroke-dasharray: none;
  stroke-width: 13;
}
.gauge-container.two > .gauge .value-text {
  fill: #ccc;
  font-weight: 100;
  font-size: 1em;
}



/* ------- Alternate Style ------- */
.gauge-container.three {
}
.gauge-container.three > .gauge .dial {
  stroke: #334455;
  stroke-width: 2;
}
.gauge-container.three > .gauge .value {
  stroke: #C9DE3C;
  stroke-width: 5;
}
.gauge-container.three > .gauge .value-text {
  fill: #C9DE3C;
} 



/* ----- Alternate Style ----- */
.gauge-container.four > .gauge .dial {
  stroke: #334455;
  stroke-width: 10;
}
.gauge-container.four > .gauge .value {
  stroke: #F32450;
  stroke-dasharray: none;
  stroke-width: 10;
}
.gauge-container.four > .gauge .value-text {
  fill: #F32450;
  transform: translate3d(26%, 20%, 0);
  display: inline-block;
}
.gauge-container.four .value-text {
  color: #F32450;
  font-weight: 100;
  position: absolute;
  bottom: 18%;
  right: 10%;
  display: inline-block;
} 



/* ----- Alternate Style ----- */
.gauge-container.five > .gauge .dial {
  stroke: #334455;
  stroke-width: 5;
}
.gauge-container.five > .gauge .value {
  stroke: #F8774B;
  stroke-dasharray: 25 1;
  stroke-width: 5;
}
.gauge-container.five > .gauge .value-text {
  fill: #F8774B;
  font-size: 0.7em;
}



/* ----- Alternate Style ----- */
  .gauge-container.six > .gauge .dial {
    stroke: #334455;
    fill: "#334455";
    stroke-width: 20;
  }
  .gauge-container.six > .gauge .value {
    stroke: #F8774B;
    stroke-width: 20;
  }
  .gauge-container.six > .gauge .value-text {
    fill: #FF6DAF;
    font-size: 0.7em;
  }


.gauge-container.seven > .gauge .dial {
  stroke: transparent;
  stroke-width: 5;
  transform: scale(0.9,0.9) translate3d(5.5px, 5.5px, 0);
  fill: rgba(148, 112, 57, 0.42);
}
.gauge-container.seven > .gauge .value {
  stroke: #F8774B;
  stroke-dasharray: none;
  stroke-width: 5;
}

Now, create a prop called value. If you type in a number you should see the gauge update.

Great, now lets create a Tulip App. Make sure to use an emoji in the name because its cool

Lets create a table record

Add a trigger on app open

Then drop our Gauge widget onto the app and set the Value prop to the Table Record

Now run the App, this update only works in the tulip player (or web chrome) version (not test mode)

Now, head back to the nodered flow, change the value you are updating and inject it and watch in amazement as the little gauge does an animated change.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s