Posted here From Nodered to Tulip Custom Widget – Michael Ellerbeck
One way to realtime update a custom Tulip widget (within the Tulip player,or chrome player not test mode) 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
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
An 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 version (or chrome player, not test)
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.