More Dashboard Goodness

tmp4ECCLast week, I got as far as some new buttons on Node-Red Dashboard along with fancy colour change and vibrate - you'll see some new buttons if you head off back to that entry.

So, what’s those  round things with numbers in them?  No,  not more gauges – but CONTROLS – a finger control for DIY Nest Displays! Well, several of them in fact.

Thanks to lots of work and some great feedback you'll see in here not to mention a little inspiration, we can now both input to the knob and output from it. This has SO much potential (not this example but the general idea - we can use most JQUERY controls in here ). We have had SO many issues along the way, interaction between knobs, not saving on refresh etc… lots of people have contributed – including ideas from the guys at IBM  -and today, reader Jorgen Antonsen provided the last piece of the puzzle.

So here is the library – demos in there – I picked the second demo – and I really think it is a winner. Here’s what we did:

In node-red settings.js I have a directory I can put stuff in like Javascript and css that won’t get wiped on an update of the dashboard.

httpStatic: '/home/pi/.node-red/public',

Now… you could create in there, say, folders for js and css like I did originally – but that would be a mistake – because they’ll conflict with the directories that Node-Red itself uses… so I called mine myjs and mycss.

Looking at the source of that page link I sent you, you’ll find a link to jquery.knob.min.js – grab that – and put it in the myjs folder. Now you have it available.

Here is what I put in my test page to bring up the nice (now simplified) controls you see above.  The second template is merely a duplicate of the first and so on – the ability to SCALE to the size of the template is new – and I’ve used 98% instead of 100% in the code because though 100% worked here in the demo – on my phone one of the knobs resized at 100%  - now it is perfect. I’d like to hear from anyone who gets a different result.

tmp7998

<script src="/myjs/jquery.knob.min.js"></script>

<script>
    (function(scope){
        scope.$watch('msg', function(data) {
            scope.msgSend = false;
            $("#jqueryknob" + scope.$id).val(data.payload);
            $("#jqueryknob" + scope.$id).change();
            scope.msgSend = true;
        });
        scope.msgSend = true;
        scope.value = 0;
        scope.sendData = function(data){scope.send({payload:data})};   
        scope.init= function(){
            $("#jqueryknob" + scope.$id).knob({
                change : function (value) {
                    //console.log("change: " + value);
                },
                release : function (value) { 
                    if(scope.msgSend)scope.sendData(value);
                },
                cancel : function () {
                },
                draw : function () { }
            }); 
        }
        //Wait for angular to assign id to DOM element
        setTimeout(function(){
            scope.init();
        },200)
    })(scope);
     
</script>

<input  id="jqueryknob{{$id}}"
        class="cDial1" 
        data-width="98%" 
        data-height="98%"  
        data-cursor=true 
        data-thickness=.4
        data-linecap=round
        value="{{msg.payload ?  msg.payload : 0}}"
>

 

So - if you SET either of these controls by using the mouse/finger,  they will set outputs... and survive a refresh. Spot on.

Manual injection will update the display without sending anything out of the template – while finger/mouse control of the knob will send output values out of the node. This is the expected behaviour.

Variations on these controls can be used to set temperature, time bands (see the other examples) and so much more and once we all understand how this is working – using other JQUERY controls should be easy.

Now – you see in the code above where it says data width and height 100% – that causes issues for the standard template – but as we’ve already cleared out all the padding from the template – why not go the whole hog and get rid of any scroll bars… Beware of altering data-thickness – that is what causes a problem if too thick (just make the item a little smaller).

This needs to go into a style – in a template BEFORE our knobs..

.nr-dashboard-template {
padding: 0;
margin: 0;
overflow-x: hidden;
overflow-y: hidden;
}

I’ve manually altered some colours – but I’m looking forward to someone coming up with a means to separately inject msg.fsColour so we can dynamically change this depending on value (thermostats!)

Been a good day, really.

more buttonsComing up next – controlling the colour!

Meanwhile,  if you want MORE buttons – check out this flow to put into Node-Red… on a page all on it’s own I suggest.

https://bitbucket.org/snippets/scargill/XXGB4

This should get you going! See image on the right

Facebooktwittergoogle_pluspinterestlinkedin

55 thoughts on “More Dashboard Goodness

    1. You can do that - but what you can't seem to do is what is needed - which is to send something out of the node.... like msg.payload containing the value... any attempts to put anything like that in the above function causes the whole lot to fail.

        1. Thanks for that - I don't know how new the latest comment is - but apparently the solution doesn't work. I know the Node-Red guys want to get the latest Node-Red out but this aspect of the templates appears to be VERY POORLY documented and even less understood - I have lots of people in here who often privately help with issues or comment in here and in 3 days NOBODY has come near an answer while two attempts to get help in the Node-Red Google groups have failed. The point about the templates SURELY is IO. If it is just inputting then you may as well use a standard component like a dial Personally I feel I need a much better idea of getting variables (not just "hello there") in and out of Node-Red Dashboard templates for it to fullfil what I think is great potential. If you spot any other items please DO link them in here.

  1. Good job getting the $watch thing working so we can get msg.payload into the knob widget.

    I sent a few hours last night trying various approaches (send, $apply, etc) to get knob data out to msg.payload, but no joy. I'm a JavaScript and Angular novice and don't understand how all the piece fit together, so it's semi-blind trial and error.

    Frustrating. We're so close. Just need to get data back to node-red (from a JS script) and we're set. I'll try a few more things tonight, maybe I'll get lucky.

      1. Try this:

        {{msg.payload}}

        var sc;
        (function(scope) {
        scope.$watch('msg.payload', function(data) {
        console.log(data);
        $("#input1").val(data);

        });

        sc = scope;
        })(scope);

        scope.sendData = function(data){
        sc.send({payload:data});
        }

          1. Hi Jorgen

            Well I've replaced the code - as you'll see if you refresh your screen - with yours - as it is no better or worse than mine - i.e. it does not crash the imagery - but it does not actually work. I am FAIRLY sure I have copied your stuff correctly but PLEASE tell me if I am wrong - the image is slightly different - there's an input box which does nothing now... rotating the dial still works - but still no output from the node. Did I make a mistake in the transcription?

              1. Tested this with the knob and it works 🙂

                <!--

                {{msg.payload}}

                $(".dial").knob({
                'change' : function (v) { }
                });

                var sc;
                (function(scope) {
                scope.$watch('msg.payload', function(data) {
                $("#freddy").val(data);
                $("#freddy").change();
                });
                sc = scope;
                })(scope);

                var sendData = function(data){
                sc.send({payload:data});
                }

                $(function($) {

                $(".knob").knob({
                change : function (value) {
                sendData(value);
                //console.log("change : " + value);
                },
                release : function (value) {
                $("#george").val(value);
                //console.log(this.$.attr('value'));
                },
                cancel : function () {
                // console.log("cancel : ", this);
                },
                /*format : function (value) {
                return value + '%';
                },*/

                draw : function () { }
                });
                });

    1. Jorgen - can you please refresh and check the blog code as one of us has made a mistake - probably me - using the code which LOOKS like your modification - I can't even get the knob to display...

        1. My goodness - you've done it Jorgen. I've made a slight change to work on release only because otherwise you get a LOT of data.

          Can I ask you to take one more look.. with the two boxes in the template UN-ticked, normal practice would be that INPUTS are not passed through to the output - and in this case sadly that is now happening regardless - in other words if you manually inject input into the node- it is being passed through to the output - can you think of a way to stop this happening so that this complies with what you would normally expect from a template - if we can do this - I think we're onto a winner.

          1. AND there is a VERY good reason why we need to separate input from output - because that output needs feeding back into the input - and right now that would make a permanent feedback loop.

            So change the value manually with an inject - and refresh the page - lets say a value of 66. The value will survive the refresh. Now dial a value - and it will show the new value - but refresh the page and the value will revert to the manually adjusted value... Hence we need a loop back to ensure that the value is exactly as it would be had we manually injected something - I do that with other controls - but in this case as manual inputs are being passed directly out - we can't do that.

            Thought Jorgen? I won't even ATTEMPT to alter the code as I'm still working on just exactly what you did. ("scope" always gives me the shivers).

  2. Well done, Jorgen! I tried to use the send function but couldn't figure out the syntax / scoping, but you got it.

    Pete, it's working as I expect so maybe I'm not understanding you. Here's what I'm seeing:

    In the Template dialog I have:
    [ ] Pass through messages from input
    [X] Add output messages to stored state

    My template code is the same as what Jorgen posted except, as you suggested, I moved the sendData(value); function to the release function so it only sends data to msg.payload when the user releases the dial.

    Over on the dashboard webpage, when I turn the dial to 70, msg.payload is set to 70 and shows up in node-red. And in node-red when I inject a value of 55 into the template node, the knob on the dashboard changes to 55 and I get 55 out of the template node in msg.payload. Furthermore, if the knob is set to 60 it stays set to 60 even after I reload the page. If you're not seeing this same behavior, let me know and I'll share my exact code.

    So will the above described behavior work for you? If not, please explain again what needs to change.

    1. Here's the template code I'm using:

      var sc;
      (function(scope) {
      scope.$watch('msg.payload', function(data) {
      $("#freddy").val(data);
      $("#freddy").change();
      });
      sc = scope;
      })(scope);

      var sendData = function(data){
      sc.send({payload:data});
      }

      $(".dial").knob({
      change : function (value) {
      //console.log("change : " + value);
      },
      release : function (value) {
      sendData(value);
      console.log("release : " + value);
      //console.log(this.$.attr('value'));
      },
      cancel : function () {
      // console.log("cancel : ", this);
      },
      /*format : function (value) {
      return value + '%';
      },*/
      draw : function () { }
      });

    2. Hi

      Now bear in mind I was out last night and slightly overservered so I'm slow this morning and catching up with emails.... with the settings as you SAY , ie the second tickbox ticked - indeed the status is preserved - so NEARLY there - but if you inject something into the input - the KNOB updates AND something comes out of the output - that is NOT the correct behaviour. You said yourself "Pass messages through from input" is NOT ticked - and yet the message is being passed through - if this is to be our starting point for many more such widgets - it would be great to get this right now. Do you see what I mean?

    1. There is another problem guys - REPLICATE the setup of the template - and inputs... so you have TWO of these KNOBS. I changed the name of the second input and references to "freddy2" and..... they are still interacting... possibly as we refer to the CLASS. Anyone care to experiment...

  3. Here's an image of the template node properties with the code, with an inset showing the simple flow of nodes I'm using. This is on node-red 0.15.2.

  4. Hi I have been playing with this for several days and I am afraid I cannot offer up any definitive answer however I thought I would offer up my current scenario in the hope it may provide inspiration. I had thought the appcache was causing the reason why dialled values would not persist over a reload but that seems to be eliminated.

    Currently I have taken the suggested function code for freddy and freddy2 and pasted it into a clean copy of the latest node -red. I get the following

    1 No interaction between the two knobs regardless of whether values are injected or dialled and whenever pages are reloaded. However these are carried out with Add messages to stored state unticked.

    2 Ticking the stored state or one of them has no effect on the interaction between the two or on the persistence of dialled values through reload. As soon as I turn on storage of values in the second knob I get a constant stream of changes to the value of the second knob and this will continue until the stack overflows. However it still does not interact with the first knob.

    3 A possible further complication is with the watch placed on the scope. It rejects any input that matches the last payload value entered by design. However if you have dialled in a value since the last payload injection and then try to repeat the payload injection, it is ignored and the value retained at the last dialled value.

    4 Nothing I have tried will persist dialled values after a reload regardless of stored state settings.

  5. OK, I think I have it. Let me address the issues:

    If a msg.payload is sent to a knob it will update the knob but not send it's value back to node-red as a msg.payload. I did this by using the variable msgOut which only send data out msg.payload when the user turns the knob.

    Having independent knobs requires an address be sent out in msg.payload, otherwise how will the template know which knob should be updated? This is done by formatting the msg.payload as X-ZZZ. Where X is the address (index) of the knob you want to control. And ZZZ is the data (value) you want to send to it. In the example code below I have two knobs (1 and 2). If you want to send 55 to knob 1 just set msg-payload to "1-55". The msg.payload also comes back from the knobs formatted this way so you'll need to parse it tell which knob sent what data.

    To give it a go, copy the code found at the link below into a template (make sure "Pass through messages from input" is not checked). I recommend adding a debug node to the output of the template node so you can see what comes back from the knobs. And add 2 inject nodes, one set to "1-65" and the other to "2-45" then see what happens to the knobs.

    https://gist.github.com/KanyonKris/3deffe7db3e0afd35f617e6a44e43b75

    1. So Chris... bearing in mind I have just got out of bed... dumping your code in there - it DOES appear to work - well done. I put mine in separate templates with different names so I'm still not entirely sure why they didn't work - but I'll work backwards from your mod to the code.

      Two things that come to mind... firstly - you have specified colours in the two units yet to me they look identical? Comments?

      Secondly - there is still an issue - perhaps we can remove that by separating them into two templates?? The issue is - when you SET the units manually (by moving the slider around- OR if you then force the output back into the input - either way you would expect the VALUE to survive a refresh of the screen ( I have "add message to stored state" set). In fact only the SECOND gauge does this - the first one reduces back to zero every time.

      Want to have an experiment with two separate templates and let us know? Again - nearly there 🙂

      1. Pete, good testing and feedback.

        I changed the knob colors - one is green, the other blue. Let me know if you see different.

        I had to make some major changes to get the dials to keep their settings through a reload. I hope this new direction is OK with you. I'll state up front that someone who knows all these layers of code could probably make this all a lot more seamless such that any widget you drop in the template node would work just like the native widgets (buttons, graphs, sliders, etc.). But I'm a novice and coding at the template level is all I can handle at this point.

        What is persisting across a reload is the msg.payload. So I put all of the knob values in msg.payload. I wasn't happy with the X-ZZZ format (simple but too rigid) so I went to JSON since (more flexible, defacto for Javascript). If you load up the code from the link below you should see the knobs stay set across reloads. The new format looks like this:

        {"Dial1":76,Dial2":37}

        If you inject this into the template it will set Dial1 to 76 and Dial2 to 37 - be sure to set the inject node Payload type to JSON. This is also the format that data comes out of the template node. It's a bit messier but node-red has pretty good support for JSON so it shouldn't be too hard to deal with.

        With this current code, one downside is that from node-red you need to set both dials together (just like the JSON example above). This isn't ideal and I think I can add some code so that you only have to send the template the name of the knob you want to set and the new value, I'll work on this next.

        https://gist.github.com/KanyonKris/e7a65e1b685e23b110c267bd1f848919

        1. Right -I've taken Dave's inputs - the look a little nicer - and your code - changing the include so it works on my system (that caught me out) - and all works well EXCEPT

          Given manual injection - only the LAST KNOB UPDATED survives a refresh. I've also tried separating them out into two templates - and ensuring they use different vars - and I'm STILL getting that interaction..

          Want to take a look?

          1. Pete, first, I cannot reproduce what you are seeing with only the last knob updated from manual injection surviving a reload. Are you injecting data for both knobs? This must be done or the knob that wasn't updated goes to zero after a reload. The format is:

            {"Dial1":"76","Dial2":"37"}

            Make sure the inject node Payload is set to JSON mode using the drop down menu, you should see {} there.

            I've looked at the code some more and tried to think up some other ways of doing things, but right now I can't do everything, some are mutually exclusive. Let me explain.

            msg.payload is the one data storage I have. So if I want to have the knobs keep their settings after a reload, msg.payload must be loaded up with all of the knob settings, that's one requirement.

            Another nice thing is to only output the msg.payload when the user changes a knob, NOT when node-red tells a knob to change. I use a variable called msgOut to control when the javascript in the template should send data to msg.payload or not, and it is working to accomplish this requirement.

            It would be nice if node-red did not have to send the values for all knobs just to update one knob, but this would mean that either only one knob would stay the same value over a reload, or that msg.payload would be sent back to node-red.

            I hope this makes sense. Please comment on what would be the least annoying behavior. And certainly feel free to throw out ideas.

            1. Hi Chris

              I'm thinking about what you're saying about sending info for both knobs and yes that makes absolute sense.

              I guess what I would like to see and have been unable to create from your mods to the code is this - a single template with a single knob (otherwise positioning can be painful) that can be replicated - ie 2 or more templates with individual knobs that do not interact.

              If we can get a template down to the minimum and understandable - with a single knob in it - then it should be no problem for us - and others to replace that knob with any other useful JQuery widget.

              I tried separating the two out - but still ended up with interaction.

                1. Jorgen does it again! And Pete, you tried to set me on the right track (of separate templates for each control) but I didn't get it - I was concerned the multiple copies of javascript in the template wouldn't work together, but seems they work fine.

  6. Pete, you mentioned color control. Do you mean being able to change the color of the knob from node-red or in the template javascript code? I'll start tinkering.

    1. Hi

      So here's where I think this should go next while keeping that code to a minimum so it can be adapted to other JQUERY widgets... but first - the second scope.msgSend that Mike pointed out was spelled incorrectly - it was that way IN MY WORKING VERSION - so can we get rid of that - anything to simplify this...

      So it seems to me that following on from DaveCJ's thoughts about colour - instead of pulling in just the msg.payload, if we watch for msg - then that opens up the ability to control, say msg.fgColour etc.. but when I had a simple go at it I could not figure out how to control the colour without altering the value of the knob.... I tried putting in a NULL msg.payload but it set a value of 100 - this is merely my inexperience at nodeJS I reckon.

      There are other things and we could get ambitions but I think as a minimum we need colour (you could have 3 of these as RGB controls - or similar) - and the one I'd really like is to somehow take in the size of the GRID (I don't know how to get to that) and have it resize the KNOB to suit! We have the height and width in there and you can resize them.....

      Those two features along would make this a great template - and provide a basis for using other widgets using similar code.

      1. If you want the knobs to be different static colors, it's easiest to just change the attributes for each knob in the HTML. But I can see some cases for dynamic color changing, ie for a thermostat control the knob could change from grey to red to indicate the heater is on, and change to blue to indicate the cooler is on. I like your approach and agree that if the whole msg object can be sent to the template and access by javascript code it would open up more capabilities, like changing colors. I'll tinker and see how it goes.

        I agree that it would be nice if the knobs could flow to fill the space efficiently for various sized displays. I believe Angular is supposed to do this, but not sure how it works. This may be beyond my ability, but I'll look into it.

        1. This is why it is "good to talk" - DAMNED good idea - use it as a large thermostat and have the colours change dynamically - indeed - the background could change as you move it - from cool blue to red at heat - and the centre bit could be coloured according to the ACTUAL temperature - well, it's worth a tinker anyway!

  7. The funny thing is - MrShark just sent those links and just as I was about to press on one - I thought to myself - I wonder if it is just a case of setting the input to be 100% instead of a fixed figure.

    And so it was... except that anything over 88% put up horizontal and vertical bars - so I took another look at my CSS for the actual template - sure enough - padding at zero - BUT nothing stopping scroll bars.

    And as if by magic..

    .nr-dashboard-template {
    padding: 0;
    margin: 0;
    overflow-x: hidden;
    overflow-y: hidden;
    }

    I will update the blog so anyone reading this and wondering what I'm talking about - we DID have a size issue - now - resizing the template - resizes the KNOB. This just gets more exciting by the minute. Blog update coming up.

    1. I didn't get far tonight, took a while to get up to the latest.

      My knobs were too small, I had to add "height: 100px;" to the block. Is that how you made yours larger?

      Also, how do you get two or more knobs side by side? If each knob has to be in a separate template node they get put in individual s.

        1. Hi

          Firstly - I removed the DIV statement from the code as you can see - so no need for any alterations - what you should of course do is in the template - rather than auto size set it - so where I have 3 knobs in a row I set the size to 2x2 - and don't forget that addition to the CSS. All just works a treat.

          1. I don't know what you mean by "removed the DIV", but when I changed from "auto" in the template to 3x3 the 2 knobs I've been tinkering with moved side-by-side and look perfect. Thanks.

  8. In testing out the big flow of all the buttons, in the group 2 part all the buttons
    will turn permanently yellow if they are tapped on quickly.
    Its very strange.
    Also all the buttons require at least one tap to "load", nothing works on the first press. After that the button does what its supposed to do.

    1. The buttons all work first time for me but I DID notice that if you hammer them quickly enough they could turn yellow - I guess the solution is not to hammer the buttons - though it would be nice to know why they do that.

Leave a Reply

Your email address will not be published. Required fields are marked *