Websocket. How to update URL or submit msg.payload from HTML?

Good morning.
I am using the airport weather (METAR) example flow from here;
https://flows.nodered.org/flow/8851bb4aa09dcdf3c4d3
It works great and I really like it.

Just trying to add a way to change the airport code and have it refresh the page on the click of a submit button.
Have spent around 5 hours trying to make it work via two options.

  1. Add a text entry box and submit button in the HTML code that is served up, but I cant find a way to reload the page with the new msg.payload inserted back into the start of the flow.

  2. Found some examples in the forums here where people have launched to URLs with buttons in the dashboard, but I can't figure out how to reverse engineer that code to be a text box or how to have the Node-RED dashboard launch the websocket rather than a web URL in a new tab.

I think I like option 1 the best, it seems really intuitive and clean UI, just type in the new code, click the button and the METAR is loaded and the dials update.
But how do you reload the websocket with the new code from within the websocket page?

The last thing I am working on is that this flow polls the API all the time, even when no one is looking at the websocket page. I think I know how to add a catch node that is looking at the worldmap node and only updates when there are 1 or more connections, but if anyone has some pointers on that, I'd love to hear your thoughts.

Thanks for your help.

In the flow you have the "HTML Page" node and in there you have the websocket connection named "connection" that is declared var = connection;
In the code there is just code prepared to receive websocket messages but no code to actually send out from the web page. I would suggest that you add a function that will allow you to send out strings via websockets. Typically you could have a function like this (taken from one of my own projects) in the code:

	function doSend(strng) {
		if (connection.readyState == 1) {
			connection.send(strng);
		}
	}

When you would like to send, you can just call the function with the string as parameter like this using a button as example:

<div style="text-align:center"><button id="p19" onMouseDown="doSend('KPHX')" style="background-color:yellow; height:60px; width:280px">&nbsp;Airport code&nbsp;</button></div>

You could use the same idea for your input field and maybe you need to change format of what you actually send out. However I'm not sure if this helps. Just looking quickly at the code it seems you have to change the "context.global.station". So the websocket message should make or initiate such a change and then initiate the call to the metar service if I have understood correctly. If you share your flow as it is, it might be easier to help

I looked into this a bit further. Here is a suggestion that works when I test it. It gives you some ideas how to use websockets and change airport codes, in this case with fixed buttons on the top row

Here is the template code you can try in the "HTML Page" node. In addition, change the connection line directly to the "Call METAR Service" node and add a debug node (to check what is coming out as websocket messages) like in the picture below

PS Just be aware that the meters can't show negative values. Like the meter for the temperature. So I just modified the code a bit for the meter:

{
    "radial" : 0,
    "value" : {{payload.temperature}},
    "max" : 50,
    "min" : -50,
    "unit" : "°C",
    "title" : "Temperature"
}

And here is the template code for the web page

<!doctype html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>METAR Dashboard</title>
    <style>
        body {
            font-family: "arial";
        }
        table.led {
            width:1300px;
        }
        td.led {
            width: 60px;
            max-width: 60px;
            overflow: hidden;
            text-align: center;
            font-size: 70%
        }
        .on { 
            border:4px inset;
            color:#369;
            background:#f9d543; 
        }
            
        .off {
            border:4px outset;
            color:#369;
            background:#f9d543; 
        }
    </style>
</head>
<body onload="init();">
<center>
    <table border="2" width="100%" height="10%">
  <tr><!-- Row 1 -->
        <td><div style="text-align:center"><button id="p19" class="off" onMouseDown="doSend('KPHX', this)" style="background-color:grey; color:white; height:60px; width:280px">&nbsp;KPHX&nbsp;</button></div></td>
        <td><div style="text-align:center"><button id="p21" class="off" onMouseDown="doSend('LOWI', this)" style="background-color:grey; color:white; height:60px; width:280px">&nbsp;LOWI&nbsp;</button></div></td>
        <td><div style="text-align:center"><button id="p20" class="off" onMouseDown="doSend('EDDH', this)" style="background-color:grey; color:white; height:60px; width:280px">&nbsp;EDDH&nbsp;</button></div></td>
  </tr>
</table>
</center>

<p>
  <div id="flash"></div>
</p>
<canvas id="gaugeCanvas0">No canvas in your browser...sorry...</canvas>
<canvas id="gaugeCanvas1"></canvas>
<canvas id="gaugeCanvas2"></canvas>
<canvas id="gaugeCanvas3"></canvas>
<canvas id="gaugeCanvas4"></canvas>
<canvas id="gaugeCanvas5"></canvas>
<canvas id="gaugeCanvas6"></canvas>
<canvas id="gaugeCanvas7"></canvas>
<table class="led">
    <tr id="leds">
        <td class="led"><canvas id="led0" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led1" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led2" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led3" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led4" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led5" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led6" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led7" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led8" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led9" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led10" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led11" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led12" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led13" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led14" width="50" height="50"></canvas></td>
        <td class="led"><canvas id="led15" width="50" height="50"></canvas></td>
    </tr>
    <tr id="labels">
        <td class="led" id="lable0"></td>
        <td class="led" id="lable1"></td>
        <td class="led" id="lable2"></td>
        <td class="led" id="lable3"></td>
        <td class="led" id="lable4"></td>
        <td class="led" id="lable5"></td>
        <td class="led" id="lable6"></td>
        <td class="led" id="lable7"></td>
        <td class="led" id="lable8"></td>
        <td class="led" id="lable9"></td>
        <td class="led" id="lable10"></td>
        <td class="led" id="lable11"></td>
        <td class="led" id="lable12"></td>
        <td class="led" id="lable13"></td>
        <td class="led" id="lable14"></td>
        <td class="led" id="lable15"></td>
        <td class="led" id="lable16"></td>
    </tr>
</table>
<body>
<script type=text/javascript src="https://rawgit.com/HanSolo/SteelSeries-Canvas/master/tween.js"></script>
<script type=text/javascript src="https://rawgit.com/HanSolo/SteelSeries-Canvas/master/steelseries.js"></script>
<script>
    var radials = [];
    var leds = [];
    var connection;

    function init()
    {
        for (var i=0;i<7;i++) {
            radials[i] = new steelseries.Radial('gaugeCanvas'+i, {
                frameDesign: steelseries.FrameDesign.TILTED_GRAY,
                gaugeType: steelseries.GaugeType.TYPE4,
                backgroundColor : steelseries.BackgroundColor.WHITE,
                ledVisible: false,
                minValue:0,
                maxValue:100,
                size: 301,
            });
            radials[i].setThresholdVisible(false);
            radials[i].setValue(0);
        }
        radials[7] = new steelseries.WindDirection('gaugeCanvas7', {
                frameDesign: steelseries.FrameDesign.TILTED_GRAY,
                gaugeType: steelseries.GaugeType.TYPE4,
                backgroundColor : steelseries.BackgroundColor.WHITE,
                size: 301,
                lcdVisible: false
            });
        for (i=0;i<16;i++) {
            leds[i] = new steelseries.Led('led'+i, {size: 50});
        }
    }

    connect();

    function onConnect() {
        leds[0].setLedOnOff(true);
        console.log("connected");
        connection.send("Request Data");
    };

    function onClose() {
        leds[0].setLedOnOff(false);
        console.log("disconnected");
        setTimeout(function() { connect()},5000);
    }

    function doSend(strng, el){
        if(el.className == "on") {
            el.className="off";
        	if (connection.readyState == 1) {
        		connection.send(strng);
        	}
        } 
        else {
            el.className="on";
        	if (connection.readyState == 1) {
        		connection.send(strng);
        	}
        } 
        return false;
    }

	function onMessage(msg) {
        flash(leds[1]);
        console.log(msg);
        var data=JSON.parse(msg.data);
        var radial = radials[data.radial];
        var led = leds[data.led];
        if (led && data.status) {
            if (data.status == 'flash')
                flash(led,data.duration || 400);
            else
                led.setLedOnOff(data.status.toUpperCase()=="ON");

        }
        if (led && data.color) {
            led.setLedColor(steelseries.LedColor[data.color.toUpperCase()+"_LED"]);
        }
        if (radial && data.max) {
            radial.setMaxValue(data.max);
        }
        if (radial && data.min) {
            radial.setMinValue(data.min);
        }
        if (radial && data.value) {
            if (radial.setValueAnimated) {
                radial.setValueAnimated(data.value);
            } else if (radial.setValueAnimatedLatest) {
                radial.setValueAnimatedLatest(data.value);
                radial.setValueAnimatedAverage(data.value);
            } else {
                radial.setValue(data.value);
            }
        }
        if (radial && data.odo) {
            radial.setOdoValue(data.odo);
        }
        if (data.title) {
            if (radial)
                radial.setTitleString(data.title);
            if (led)
                document.getElementById("lable"+data.led).innerHTML = data.title;

        }
        if (radial && data.unit) {
            radial.setUnitString(data.unit)
        }
        if (data.flash) {
                document.getElementById("flash").innerHTML = data.flash;
        }
    }

    function connect(cause) {
        if (cause)
            console.log(cause.errorMessage);
        const hostname = window.location.hostname || "raspi2"
        const port = window.location.port || 1880;
        const url = "ws://" + hostname + ":" + port + "/ws/dashboard";
        connection=new WebSocket(url);
        connection.onopen=onConnect;
        connection.onclose=onClose;
        // Log errors
        connection.onerror = function (error) {
            flash(leds[2]);
            console.log('WebSocket Error ' + error);
        };

        // Log messages from the server
        connection.onmessage = onMessage;
    }

    function flash(led,duration) {
        led.setLedOnOff(true);
        setTimeout(function() { led.setLedOnOff(false)},duration || 400);
    }
</script>

In some places in the code I also changed how to set the global station value. So maybe better, try my complete flow

[{"id":"69ab454f.9654bc","type":"http request","z":"b7846ba4.571b18","name":"Call METAR Service","method":"GET","paytoqs":"ignore","url":"http://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString={{{payload}}}","tls":"","persist":false,"proxy":"","authType":"","x":310,"y":450,"wires":[["907cdf5f.6f832"]]},{"id":"5873ed2f.a78c14","type":"inject","z":"b7846ba4.571b18","name":"Weather report","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"600","crontab":"","once":false,"onceDelay":"","topic":"","payload":"/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString=EDDH","payloadType":"str","x":268,"y":226,"wires":[["a165de81.5e9a2"]]},{"id":"907cdf5f.6f832","type":"xml","z":"b7846ba4.571b18","name":"","x":477,"y":450,"wires":[["b32637a5.4cd9c8","9d97e266.62682","d28f1f9b.2d70e","157b122.fea84ee","a5d3fe25.5a2c"]]},{"id":"159d93c2.ea626c","type":"comment","z":"b7846ba4.571b18","name":"NOAA Weather data","info":"Retrieving Finningly Airport METAR feed","x":257,"y":182,"wires":[]},{"id":"b32637a5.4cd9c8","type":"function","z":"b7846ba4.571b18","name":"Parse METAR","func":"var METARdata = msg.payload.response.data[0].METAR[0];\nvar windkt = METARdata.wind_speed_kt[0];\nvar windkph = (windkt*1.852);\nvar winddir = METARdata.wind_dir_degrees[0]\nvar gusts = windkt; //if wind_gust_kt is not present, value defaults to wind_speed_kt\n   if (typeof METARdata.wind_gust_kt != \"undefined\") {\n\t   gusts = METARdata.wind_gust_kt[0];\n       }\nvar visibility = METARdata.visibility_statute_mi[0]*1.852;       \nvar altim = METARdata.altim_in_hg[0];\nvar pressure = Math.round(altim * 33.8637526); //convert hg to mb\nvar dew = METARdata.dewpoint_c[0];\ndew = Math.round(dew);\nvar tempC = METARdata.temp_c[0];\nvar tempF = ((tempC*1.8)+32);\n\n//tempC=5;\n//dew = 15;\n//windkt=1;\n\n//August-Roche-Magnus approximation to calculate rh_hum\nvar constA = 17.625;\nvar constB = 243.04;\nvar rh_numer = 10000.0*Math.exp((constA*eval(dew))/(eval(dew)+constB));\nvar rh_denom = Math.exp((constA*eval(tempC))/(eval(tempC)+constB));\nvar rh_hum   = (rh_numer/rh_denom);\nvar humidityemon = Math.round(rh_hum);\nvar humidity = (humidityemon/100);\n\n//Wind Chill Index Calculations using the 'Joint Action Group for Temp Indices' (JAG TI) formula\nvar chill=(13.12+0.6215*tempC-11.37*Math.pow(windkph,0.16)+0.3965*tempC*Math.pow(windkph,0.16));\n\n//Calculate Apparent Temperature\n//firstly, Calculate Vapor pressure in kPa\nvar vaporp = ((6.11*Math.pow(10,(7.5*dew/(237.3 + dew))))/10);\n//apply Steadman's Apparent Temperature formula\nvar apptemp = -2.7+(1.04*tempC)+(2*vaporp)-(windkt*0.3343886);\n        \n//Calculation of 'feels like' temperature by using the 'chill factor' if\n//the temp is below 10degC, or 'apparent temp' if over 20degC, or if between\n//10 and 20degC using a linear interpolation of both.\nvar feels;\nif (tempC < 10.0) {\n\tfeels=chill;\n\t}\n\telse if (tempC > 20.0) {\n\tfeels=apptemp;\n\t}\n\t\telse {\n\t\tvar calcA=((tempC-10)/10);\n\t\tvar calcB=1-calcA;\n\t\tfeels=((apptemp*calcA)+(chill*calcB));\n\t\t}\n\nmsg.payload = { \n    wind: windkt,\n    gusts: gusts,\n    direction: winddir,\n    humidity: humidity,\n    dewpoint: dew,\n    pressure: pressure,\n    temperature: tempC,\n    visibility: visibility,\n    feels:feels\n    };\nreturn msg;","outputs":1,"x":642,"y":520,"wires":[["45cccff9.ba333","e852b626.17ad48","526f8ae7.ad9074","59c4f7f.fa63b08","d5bffc9a.2a4","1cfe6d9e.e30192","23f2644c.dc0d9c","a5f62102.5a09e"]]},{"id":"7885c297.877a3c","type":"http response","z":"b7846ba4.571b18","name":"","x":730,"y":1077,"wires":[]},{"id":"586fdf52.a7902","type":"template","z":"b7846ba4.571b18","name":"HTML Page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!doctype html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>METAR Dashboard</title>\n    <style>\n        body {\n            font-family: \"arial\";\n        }\n        table.led {\n            width:1300px;\n        }\n        td.led {\n            width: 60px;\n            max-width: 60px;\n            overflow: hidden;\n            text-align: center;\n            font-size: 70%\n        }\n        .on { \n            border:4px inset;\n            color:#369;\n            background:#f9d543; \n        }\n            \n        .off {\n            border:4px outset;\n            color:#369;\n            background:#f9d543; \n        }\n    </style>\n</head>\n<body onload=\"init();\">\n<center>\n    <table border=\"2\" width=\"100%\" height=\"10%\">\n  <tr><!-- Row 1 -->\n        <td><div style=\"text-align:center\"><button id=\"p17\" class=\"off\" onMouseDown=\"doSend('ESSA', this)\" style=\"background-color:grey; color:white; height:60px; width:280px\">&nbsp;ESSA&nbsp;</button></div></td>\n        <td><div style=\"text-align:center\"><button id=\"p18\" class=\"off\" onMouseDown=\"doSend('LFPG', this)\" style=\"background-color:grey; color:white; height:60px; width:280px\">&nbsp;LFPG&nbsp;</button></div></td>\n        <td><div style=\"text-align:center\"><button id=\"p19\" class=\"off\" onMouseDown=\"doSend('KPHX', this)\" style=\"background-color:grey; color:white; height:60px; width:280px\">&nbsp;KPHX&nbsp;</button></div></td>\n        <td><div style=\"text-align:center\"><button id=\"p20\" class=\"off\" onMouseDown=\"doSend('LOWI', this)\" style=\"background-color:grey; color:white; height:60px; width:280px\">&nbsp;LOWI&nbsp;</button></div></td>\n        <td><div style=\"text-align:center\"><button id=\"p21\" class=\"off\" onMouseDown=\"doSend('EDDH', this)\" style=\"background-color:grey; color:white; height:60px; width:280px\">&nbsp;EDDH&nbsp;</button></div></td>\n  </tr>\n</table>\n</center>\n\n<p>\n  <div id=\"flash\"></div>\n</p>\n<canvas id=\"gaugeCanvas0\">No canvas in your browser...sorry...</canvas>\n<canvas id=\"gaugeCanvas1\"></canvas>\n<canvas id=\"gaugeCanvas2\"></canvas>\n<canvas id=\"gaugeCanvas3\"></canvas>\n<canvas id=\"gaugeCanvas4\"></canvas>\n<canvas id=\"gaugeCanvas5\"></canvas>\n<canvas id=\"gaugeCanvas6\"></canvas>\n<canvas id=\"gaugeCanvas7\"></canvas>\n<table class=\"led\">\n    <tr id=\"leds\">\n        <td class=\"led\"><canvas id=\"led0\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led1\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led2\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led3\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led4\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led5\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led6\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led7\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led8\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led9\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led10\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led11\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led12\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led13\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led14\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led15\" width=\"50\" height=\"50\"></canvas></td>\n    </tr>\n    <tr id=\"labels\">\n        <td class=\"led\" id=\"lable0\"></td>\n        <td class=\"led\" id=\"lable1\"></td>\n        <td class=\"led\" id=\"lable2\"></td>\n        <td class=\"led\" id=\"lable3\"></td>\n        <td class=\"led\" id=\"lable4\"></td>\n        <td class=\"led\" id=\"lable5\"></td>\n        <td class=\"led\" id=\"lable6\"></td>\n        <td class=\"led\" id=\"lable7\"></td>\n        <td class=\"led\" id=\"lable8\"></td>\n        <td class=\"led\" id=\"lable9\"></td>\n        <td class=\"led\" id=\"lable10\"></td>\n        <td class=\"led\" id=\"lable11\"></td>\n        <td class=\"led\" id=\"lable12\"></td>\n        <td class=\"led\" id=\"lable13\"></td>\n        <td class=\"led\" id=\"lable14\"></td>\n        <td class=\"led\" id=\"lable15\"></td>\n        <td class=\"led\" id=\"lable16\"></td>\n    </tr>\n</table>\n<body>\n<script type=text/javascript src=\"https://rawgit.com/HanSolo/SteelSeries-Canvas/master/tween.js\"></script>\n<script type=text/javascript src=\"https://rawgit.com/HanSolo/SteelSeries-Canvas/master/steelseries.js\"></script>\n<script>\n    var radials = [];\n    var leds = [];\n    var connection;\n\n    function init()\n    {\n        for (var i=0;i<7;i++) {\n            radials[i] = new steelseries.Radial('gaugeCanvas'+i, {\n                frameDesign: steelseries.FrameDesign.TILTED_GRAY,\n                gaugeType: steelseries.GaugeType.TYPE4,\n                backgroundColor : steelseries.BackgroundColor.WHITE,\n                ledVisible: false,\n                minValue:0,\n                maxValue:100,\n                size: 301,\n            });\n            radials[i].setThresholdVisible(false);\n            radials[i].setValue(0);\n        }\n        radials[7] = new steelseries.WindDirection('gaugeCanvas7', {\n                frameDesign: steelseries.FrameDesign.TILTED_GRAY,\n                gaugeType: steelseries.GaugeType.TYPE4,\n                backgroundColor : steelseries.BackgroundColor.WHITE,\n                size: 301,\n                lcdVisible: false\n            });\n        for (i=0;i<16;i++) {\n            leds[i] = new steelseries.Led('led'+i, {size: 50});\n        }\n    }\n\n    connect();\n\n    function onConnect() {\n        leds[0].setLedOnOff(true);\n        console.log(\"connected\");\n        connection.send(\"Request Data\");\n    };\n\n    function onClose() {\n        leds[0].setLedOnOff(false);\n        console.log(\"disconnected\");\n        setTimeout(function() { connect()},5000);\n    }\n\n    function doSend(strng, el){\n        if(el.className == \"on\") {\n            el.className=\"off\";\n        \tif (connection.readyState == 1) {\n        \t\tconnection.send(strng);\n        \t}\n        } \n        else {\n            el.className=\"on\";\n        \tif (connection.readyState == 1) {\n        \t\tconnection.send(strng);\n        \t}\n        } \n        return false;\n    }\n\n\tfunction onMessage(msg) {\n        flash(leds[1]);\n        console.log(msg);\n        var data=JSON.parse(msg.data);\n        var radial = radials[data.radial];\n        var led = leds[data.led];\n        if (led && data.status) {\n            if (data.status == 'flash')\n                flash(led,data.duration || 400);\n            else\n                led.setLedOnOff(data.status.toUpperCase()==\"ON\");\n\n        }\n        if (led && data.color) {\n            led.setLedColor(steelseries.LedColor[data.color.toUpperCase()+\"_LED\"]);\n        }\n        if (radial && data.max) {\n            radial.setMaxValue(data.max);\n        }\n        if (radial && data.min) {\n            radial.setMinValue(data.min);\n        }\n        if (radial && data.value) {\n            if (radial.setValueAnimated) {\n                radial.setValueAnimated(data.value);\n            } else if (radial.setValueAnimatedLatest) {\n                radial.setValueAnimatedLatest(data.value);\n                radial.setValueAnimatedAverage(data.value);\n            } else {\n                radial.setValue(data.value);\n            }\n        }\n        if (radial && data.odo) {\n            radial.setOdoValue(data.odo);\n        }\n        if (data.title) {\n            if (radial)\n                radial.setTitleString(data.title);\n            if (led)\n                document.getElementById(\"lable\"+data.led).innerHTML = data.title;\n\n        }\n        if (radial && data.unit) {\n            radial.setUnitString(data.unit)\n        }\n        if (data.flash) {\n                document.getElementById(\"flash\").innerHTML = data.flash;\n        }\n    }\n\n    function connect(cause) {\n        if (cause)\n            console.log(cause.errorMessage);\n        const hostname = window.location.hostname || \"raspi2\"\n        const port = window.location.port || 1880;\n        const url = \"ws://\" + hostname + \":\" + port + \"/ws/dashboard\";\n        connection=new WebSocket(url);\n        connection.onopen=onConnect;\n        connection.onclose=onClose;\n        // Log errors\n        connection.onerror = function (error) {\n            flash(leds[2]);\n            console.log('WebSocket Error ' + error);\n        };\n\n        // Log messages from the server\n        connection.onmessage = onMessage;\n    }\n\n    function flash(led,duration) {\n        led.setLedOnOff(true);\n        setTimeout(function() { led.setLedOnOff(false)},duration || 400);\n    }\n</script>","x":576,"y":1077,"wires":[["7885c297.877a3c"]]},{"id":"7ada2666.8525d8","type":"http in","z":"b7846ba4.571b18","name":"","url":"/metar","method":"get","x":246,"y":1077,"wires":[["6ca08ddb.935f74","154822ee.eab7dd"]]},{"id":"7e009dd0.81ff64","type":"websocket out","z":"b7846ba4.571b18","name":"","server":"8a103fc6.75efc","client":"","x":1123,"y":958,"wires":[]},{"id":"45cccff9.ba333","type":"template","z":"b7846ba4.571b18","name":"Temp2Radial","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"radial\" : 0,\n    \"value\" : {{payload.temperature}},\n    \"max\" : 50,\n    \"min\" : -30,\n    \"unit\" : \"°C\",\n    \"title\" : \"Temperature\"\n}\n","x":875,"y":522,"wires":[["7e009dd0.81ff64"]]},{"id":"e852b626.17ad48","type":"template","z":"b7846ba4.571b18","name":"Wind2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 6,\n    \"value\" : {{payload.wind}},\n    \"max\" : 80,\n    \"unit\" : \"kt\",\n    \"title\" : \"Wind\"\n}\n","x":872,"y":847,"wires":[["7e009dd0.81ff64"]]},{"id":"526f8ae7.ad9074","type":"template","z":"b7846ba4.571b18","name":"Hum2Radial","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"radial\" : 2,\n    \"value\" : {{payload.humidity}},\n    \"max\" : 100,\n    \"unit\" : \"%\",\n    \"title\" : \"Humidity\"\n}\n","x":876,"y":631,"wires":[["7e009dd0.81ff64"]]},{"id":"59c4f7f.fa63b08","type":"template","z":"b7846ba4.571b18","name":"Pres2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 4,\n    \"value\" : {{payload.pressure}},\n    \"min\" : 900,\n    \"max\" : 1100,\n    \"unit\" : \"hPa\",\n    \"title\" : \"Pressure\"\n}\n","x":874,"y":736,"wires":[["7e009dd0.81ff64"]]},{"id":"27b65972.d849a6","type":"websocket in","z":"b7846ba4.571b18","name":"","server":"8a103fc6.75efc","client":"","x":269,"y":957,"wires":[["e6a7ed4a.19581","d22555b4.da6768","74cf36a6.5b93a8"]]},{"id":"d5bffc9a.2a4","type":"template","z":"b7846ba4.571b18","name":"Dew2Radial","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"radial\" : 3,\n    \"value\" : {{payload.dewpoint}},\n    \"max\" : 50,\n    \"min\" : -50,\n    \"unit\" : \"°C\",\n    \"title\" : \"Dew\"\n}\n","x":874,"y":687,"wires":[["7e009dd0.81ff64"]]},{"id":"1cfe6d9e.e30192","type":"template","z":"b7846ba4.571b18","name":"Feels2Radial","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"radial\" : 1,\n    \"value\" : {{payload.feels}},\n    \"max\" : 50,\n    \"min\" : -50,\n    \"unit\" : \"°C\",\n    \"title\" : \"Feels\"\n}\n","x":877,"y":575,"wires":[["7e009dd0.81ff64"]]},{"id":"23f2644c.dc0d9c","type":"template","z":"b7846ba4.571b18","name":"Vis2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 5,\n    \"value\" : {{payload.visibility}},\n    \"max\" : 10,\n    \"unit\" : \"km\",\n    \"title\" : \"Visibility\"\n}\n","x":873,"y":788,"wires":[["7e009dd0.81ff64"]]},{"id":"a5f62102.5a09e","type":"template","z":"b7846ba4.571b18","name":"Dir2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 7,\n    \"value\" : {{payload.direction}}\n}\n","x":873,"y":898,"wires":[["7e009dd0.81ff64"]]},{"id":"9d97e266.62682","type":"function","z":"b7846ba4.571b18","name":"to VFR LED","func":"var report=msg.payload.response.data[0].METAR[0];\nvar conditions=report.flight_category;\nmsg.payload = {\n    \"led\" : 3,\n    \"status\" : \"ON\",\n    \"color\" :  conditions == \"VFR\" ? \"green\" : conditions == \"MVFR\" ? \"yellow\" : \"red\",\n    \"title\" : \"VFR\"\n}\nreturn msg;","outputs":1,"x":927,"y":451,"wires":[["2e8a4e74.d175b2"]]},{"id":"2e8a4e74.d175b2","type":"websocket out","z":"b7846ba4.571b18","name":"","server":"8a103fc6.75efc","client":"","x":1216,"y":451,"wires":[]},{"id":"e6a7ed4a.19581","type":"function","z":"b7846ba4.571b18","name":"Init Status LEDs","func":"msgs = [];\n\nmsgs.push({payload:{ \n    \"title\" : \"Connect\",\n    \"led\" : 0,\n    \"color\" : \"blue\"\n}});\nmsgs.push({payload:{ \n    \"title\" : \"Error\",\n    \"led\" : 2,\n    \"status\" : \"flash\",\n    \"color\" : \"red\"\n}});\nmsgs.push({payload:{ \n    \"title\" : \"Data\",\n    \"led\" : 1,\n    \"status\" : \"flash\",\n    \"color\" : \"yellow\"\n}});\nreturn [msgs];","outputs":1,"x":553,"y":957,"wires":[["7e009dd0.81ff64"]]},{"id":"d28f1f9b.2d70e","type":"function","z":"b7846ba4.571b18","name":"Extract raw METAR","func":"msg.payload=msg.payload.response.data[0].METAR[0].raw_text[0];\nreturn msg;","outputs":1,"x":676,"y":256,"wires":[["d2e6ac6e.2d195","bcc1aaf2.433e58","1af39e86.e50c61"]]},{"id":"d2e6ac6e.2d195","type":"function","z":"b7846ba4.571b18","name":"to NOSIG LED","func":"msg.payload = {\n    \"led\" : 4,\n    \"status\" : msg.payload.indexOf(\"NOSIG\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"green\",\n    \"title\" : \"NOSIG\"\n}\nreturn msg;","outputs":1,"x":954,"y":137,"wires":[["2e8a4e74.d175b2"]]},{"id":"bcc1aaf2.433e58","type":"function","z":"b7846ba4.571b18","name":"to CAVOK LED","func":"msg.payload = {\n    \"led\" : 5,\n    \"status\" : msg.payload.indexOf(\"CAVOK\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"green\",\n    \"title\" : \"CAVOK\"\n}\nreturn msg;","outputs":1,"x":952,"y":94,"wires":[["2e8a4e74.d175b2"]]},{"id":"1af39e86.e50c61","type":"function","z":"b7846ba4.571b18","name":"to Flash Message","func":"msg.payload = {\n    \"flash\" : msg.payload\n}\nreturn msg;","outputs":1,"x":960,"y":400,"wires":[["2e8a4e74.d175b2"]]},{"id":"3a58c564.c5a73a","type":"comment","z":"b7846ba4.571b18","name":"Send HTML Page","info":"","x":265,"y":1029,"wires":[]},{"id":"293a8f70.d6c57","type":"comment","z":"b7846ba4.571b18","name":"Respond to ping message","info":"","x":292,"y":907,"wires":[]},{"id":"a2f94449.5d06b8","type":"function","z":"b7846ba4.571b18","name":"to DIZZELE LED","func":"msg.payload = {\n    \"led\" : 6,\n    \"status\" : msg.payload.indexOf(\"DZ\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"yellow\",\n    \"title\" : \"Dizzle\"\n}\nreturn msg;","outputs":1,"x":952,"y":178,"wires":[["2e8a4e74.d175b2"]]},{"id":"285562db.d7aa9e","type":"function","z":"b7846ba4.571b18","name":"to RAIN LED","func":"msg.payload = {\n    \"led\" : 7,\n    \"status\" : msg.payload.indexOf(\"RA\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"yellow\",\n    \"title\" : \"Rain\"\n}\nreturn msg;","outputs":1,"x":955,"y":223,"wires":[["2e8a4e74.d175b2"]]},{"id":"bf5ce258.40a32","type":"function","z":"b7846ba4.571b18","name":"to TS LED","func":"msg.payload = {\n    \"led\" : 8,\n    \"status\" : msg.payload.indexOf(\"TS\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"red\",\n    \"title\" : \"Thunderstorm\"\n}\nreturn msg;","outputs":1,"x":956,"y":272,"wires":[["2e8a4e74.d175b2"]]},{"id":"6ca08ddb.935f74","type":"function","z":"b7846ba4.571b18","name":"Set Station","func":"if (msg.payload.station) {\n    global.set(\"station\", msg.payload.station);\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":407,"y":1077,"wires":[["586fdf52.a7902"]]},{"id":"a165de81.5e9a2","type":"function","z":"b7846ba4.571b18","name":"Set Metar Station","func":"msg.payload = global.get(\"station\") || \"EDDH\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":310,"y":320,"wires":[["69ab454f.9654bc"]]},{"id":"154822ee.eab7dd","type":"debug","z":"b7846ba4.571b18","name":"","active":true,"console":"false","complete":"false","x":420,"y":1143,"wires":[]},{"id":"157b122.fea84ee","type":"debug","z":"b7846ba4.571b18","name":"","active":false,"console":"false","complete":"payload.response.data","x":706,"y":475,"wires":[]},{"id":"633ddaa6.9cc224","type":"function","z":"b7846ba4.571b18","name":"to FG LED","func":"msg.payload = {\n    \"led\" : 9,\n    \"status\" : msg.payload.indexOf(\"FG\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"red\",\n    \"title\" : \"Fog\"\n}\nreturn msg;","outputs":1,"x":957,"y":315,"wires":[["2e8a4e74.d175b2"]]},{"id":"52947201.ad6b8c","type":"function","z":"b7846ba4.571b18","name":"to SN LED","func":"msg.payload = {\n    \"led\" : 10,\n    \"status\" : msg.payload.indexOf(\"SN\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"red\",\n    \"title\" : \"Snow\"\n}\nreturn msg;","outputs":1,"x":956,"y":357,"wires":[["2e8a4e74.d175b2"]]},{"id":"a5d3fe25.5a2c","type":"function","z":"b7846ba4.571b18","name":"Extract WX String","func":"var wx=msg.payload.response.data[0].METAR[0].wx_string;\nmsg.payload=wx ? wx.join(): \"\";\nreturn msg;","outputs":1,"x":677,"y":142,"wires":[["a2f94449.5d06b8","285562db.d7aa9e","bf5ce258.40a32","633ddaa6.9cc224","52947201.ad6b8c"]]},{"id":"d22555b4.da6768","type":"debug","z":"b7846ba4.571b18","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":540,"y":890,"wires":[]},{"id":"74cf36a6.5b93a8","type":"change","z":"b7846ba4.571b18","name":"","rules":[{"t":"set","p":"station","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":680,"wires":[["69ab454f.9654bc"]]},{"id":"8a103fc6.75efc","type":"websocket-listener","path":"/ws/dashboard","wholemsg":"false"}]

@krambriw Thanks so much for taking the time to look at my question.

I can see where you have added the buttons in the HTML.
I can also see the function in the <script> that you added (the doSend).

Unfortunately your flow does not work.

The buttons and dials load up Ok.

But the HTTP get node is never called. The buttons do nothing.
The debug node simply shows an 'empty' on first connect.
image

No data is ever loaded.

That's strange since it works fine here?? And, obvioulsy, you did try to click on the buttons? And nothing came out to the debug?

Was this the complete flow I posted?

Ok, very interesting that it works for you.
It should not make a difference, but I am running Node-RED v1.2.7 on a Windows 10 PC.

I imported the whole flow exactly from this thread.
I figured I would see what you have done and then try and covert it from buttons to a text field (as you know, there are thousands of airports, so I cant have a button for each).

Since it works for you and not for me, this perhaps explains why I have sunk over 5 hours into trying to solve this problem to no avail.... There must be something different about my websocket configuration.

If it helps rule anything out, I tried @krambriw flow on a windows 10 node-red - it works (or at least the gauges change when I click the buttons)

1 Like

Just to verify nothing strange is with your websocket connection, could you modify the doSend function and see if we get anything out when you click the buttons?

    function doSend(strng, el){
        if(el.className == "on") {
            el.className="off";
        } 
        else {
            el.className="on";
        } 
  		connection.send(strng);
        return false;
    }

I made the change and cant see any difference in the webpage, the debug tab or anywhere for that matter.
Buttons and gauges still load.
Pressing buttons does nothing (You see them depress - a black highlight around them).

Should actually be kind of blue

Share your flow and I will verify if I get the same as you

But first maybe try with an even simpler function:

    function doSend(strng, el){
     	connection.send(strng);
        return false;
    }

Same results with that simple function.
Nothing.
Buttons and gauges load, but no data shows up, I don't see the METAR string under the buttons, nothing in the debug tab.

Don't spend any more time on this Walter, clearly something on my end is not working that has nothing to do with your code and I need to track that down.

Thanks so much for you help.

OK, fine, you'll find it, I'm sure

Got it working.

Import the flow from the very first post.
In the 'HTML Page' Template node, add the following code straight after the body tag;

<form action>
  <label for="apname">4 character airport code:</label>
  <input type="text" name=station>
  <input type="submit" value="Submit"><br><br>
</form>

Now when you click the 'Submit' button (with a valid airport code) the URL will update and page will update with the new airport METAR.

There is no need to call a script function, or loop the msg.payload back into the flow, just update the URL of the websocket with the HTML code.

Thanks everyone for your help, it got me pointed in the right direction.

Well, part of, just wanted to comment. In this case you are NOT using websockets, you just update the global station variable. Unless you have fixed it somehow, not known to me, you will have to wait for the automatic page update being triggered after you have entered a new airport code

To overcome, or to make things happen "instantly", you can add a line between two nodes and a delay node (short delay) like in the picture below. After this change, the page update happens as expected, directly after you submit a new airport code. (Surely you could instead make the inject node trigger more frequently but I think it is nicer to get the page update on demand :wink: )

For those following along....
Updating the URL is instant. (From a user perspective, it would be very confusing to enter a code and have nothing happen for a random length of time).
Also, from my first post, I wanted to remove the inject button constantly polling the API even when no one was connected.
Since updating the URL / webpage is instant, I have removed the inject node.
The downside of this is that the user needs to refresh their web browser if they want to check if a new METAR has come in for that airport.... Typically most users tell me they simply want to check the METAR for the airport one time and just get the latest information. (METARs are not live updating weather).

The webpage now looks like this;

I added UTC time so the user could see how old the current METAR is.

The flow looks like this;

And here is the flow as it currently stands. I think I have everything working the way that makes sense. Already had some thumbs up from a few users, so its been a worthwhile little exercise.

[{"id":"69ab454f.9654bc","type":"http request","z":"95e2d5d0.00fa2","name":"Call METAR Service","method":"GET","url":"http://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString={{{payload}}}","x":280,"y":420,"wires":[["907cdf5f.6f832"]]},{"id":"907cdf5f.6f832","type":"xml","z":"95e2d5d0.00fa2","name":"","x":470,"y":420,"wires":[["b32637a5.4cd9c8","9d97e266.62682","d28f1f9b.2d70e","a5d3fe25.5a2c"]]},{"id":"b32637a5.4cd9c8","type":"function","z":"95e2d5d0.00fa2","name":"Parse METAR","func":"var METARdata = msg.payload.response.data[0].METAR[0];\nvar windkt = METARdata.wind_speed_kt[0];\nvar windkph = (windkt*1.852);\nvar winddir = METARdata.wind_dir_degrees[0]\nvar gusts = windkt; //if wind_gust_kt is not present, value defaults to wind_speed_kt\n   if (typeof METARdata.wind_gust_kt != \"undefined\") {\n\t   gusts = METARdata.wind_gust_kt[0];\n       }\nvar visibility = METARdata.visibility_statute_mi[0]*1.852;       \nvar altim = METARdata.altim_in_hg[0];\nvar pressure = Math.round(altim * 33.8637526); //convert hg to mb\nvar dew = METARdata.dewpoint_c[0];\ndew = Math.round(dew);\nvar tempC = METARdata.temp_c[0];\nvar tempF = ((tempC*1.8)+32);\n\n//tempC=5;\n//dew = 15;\n//windkt=1;\n\n//August-Roche-Magnus approximation to calculate rh_hum\nvar constA = 17.625;\nvar constB = 243.04;\nvar rh_numer = 10000.0*Math.exp((constA*eval(dew))/(eval(dew)+constB));\nvar rh_denom = Math.exp((constA*eval(tempC))/(eval(tempC)+constB));\nvar rh_hum   = (rh_numer/rh_denom);\nvar humidityemon = Math.round(rh_hum);\nvar humidity = (humidityemon/100);\n\n//Wind Chill Index Calculations using the 'Joint Action Group for Temp Indices' (JAG TI) formula\nvar chill=(13.12+0.6215*tempC-11.37*Math.pow(windkph,0.16)+0.3965*tempC*Math.pow(windkph,0.16));\n\n//Calculate Apparent Temperature\n//firstly, Calculate Vapor pressure in kPa\nvar vaporp = ((6.11*Math.pow(10,(7.5*dew/(237.3 + dew))))/10);\n//apply Steadman's Apparent Temperature formula\nvar apptemp = -2.7+(1.04*tempC)+(2*vaporp)-(windkt*0.3343886);\n        \n//Calculation of 'feels like' temperature by using the 'chill factor' if\n//the temp is below 10degC, or 'apparent temp' if over 20degC, or if between\n//10 and 20degC using a linear interpolation of both.\nvar feels;\nif (tempC < 10.0) {\n\tfeels=chill;\n\t}\n\telse if (tempC > 20.0) {\n\tfeels=apptemp;\n\t}\n\t\telse {\n\t\tvar calcA=((tempC-10)/10);\n\t\tvar calcB=1-calcA;\n\t\tfeels=((apptemp*calcA)+(chill*calcB));\n\t\t}\n\nmsg.payload = { \n    wind: windkt,\n    gusts: gusts,\n    direction: winddir,\n    humidity: humidity,\n    dewpoint: dew,\n    pressure: pressure,\n    temperature: tempC,\n    visibility: visibility,\n    feels:feels\n    };\nreturn msg;","outputs":1,"x":635,"y":490,"wires":[["45cccff9.ba333","e852b626.17ad48","526f8ae7.ad9074","59c4f7f.fa63b08","d5bffc9a.2a4","1cfe6d9e.e30192","23f2644c.dc0d9c","a5f62102.5a09e"]]},{"id":"7885c297.877a3c","type":"http response","z":"95e2d5d0.00fa2","name":"","x":700,"y":1047,"wires":[]},{"id":"586fdf52.a7902","type":"template","z":"95e2d5d0.00fa2","name":"HTML Page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!doctype html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>METAR Dashboard</title>\n    <style>\n        body {\n            font-family: \"arial\";\n        }\n        table.led {\n            width:1300px;\n        }\n        td.led {\n            width: 60px;\n            max-width: 60px;\n            overflow: hidden;\n            text-align: center;\n            font-size: 70%\n        }\n        .on { \n            border:4px inset;\n            color:#369;\n            background:#f9d543; \n        }\n            \n        .off {\n            border:4px outset;\n            color:#369;\n            background:#f9d543; \n        }\n    </style>\n</head>\n<body onload=\"init();\" style=\"background-color:#154360;\">\n\n\n<form action>\n  <label for=\"apname\" style=color:#C0C0C0>4 character airport code:</label>\n  <input type=\"text\" name=station>\n  <input type=\"submit\" value=\"Submit\"><p style=color:#C0C0C0>UTC: <span id=\"datetime\"></span></p>\n</form>\n<p>\n  <div id=\"flash\" style=\"color:#C0C0C0;\"></div>\n</p>\n\n<canvas id=\"gaugeCanvas0\">No canvas in your browser...sorry...</canvas>\n<canvas id=\"gaugeCanvas1\"></canvas>\n<canvas id=\"gaugeCanvas2\"></canvas>\n<canvas id=\"gaugeCanvas3\"></canvas>\n<canvas id=\"gaugeCanvas4\"></canvas>\n<canvas id=\"gaugeCanvas5\"></canvas>\n<canvas id=\"gaugeCanvas6\"></canvas>\n<canvas id=\"gaugeCanvas7\"></canvas>\n<table class=\"led\">\n    <tr id=\"leds\">\n        <td class=\"led\"><canvas id=\"led0\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led1\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led2\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led3\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led4\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led5\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led6\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led7\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led8\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led9\" width=\"50\" height=\"50\"></canvas></td>\n        <td class=\"led\"><canvas id=\"led10\" width=\"50\" height=\"50\"></canvas></td>\n\n    </tr>\n    <tr id=\"labels\" style=color:#C0C0C0>\n        <td class=\"led\" id=\"lable0\"></td>\n        <td class=\"led\" id=\"lable1\"></td>\n        <td class=\"led\" id=\"lable2\"></td>\n        <td class=\"led\" id=\"lable3\"></td>\n        <td class=\"led\" id=\"lable4\"></td>\n        <td class=\"led\" id=\"lable5\"></td>\n        <td class=\"led\" id=\"lable6\"></td>\n        <td class=\"led\" id=\"lable7\"></td>\n        <td class=\"led\" id=\"lable8\"></td>\n        <td class=\"led\" id=\"lable9\"></td>\n        <td class=\"led\" id=\"lable10\"></td>\n\n    </tr>\n</table>\n<body>\n<script type=text/javascript src=\"https://rawgit.com/HanSolo/SteelSeries-Canvas/master/tween.js\"></script>\n<script type=text/javascript src=\"https://rawgit.com/HanSolo/SteelSeries-Canvas/master/steelseries.js\"></script>\n\n<script>\nfunction twoDig(val) {return (('0'+val).slice(-2));}\nvar d = new Date();\ndocument.getElementById(\"datetime\").innerHTML = twoDig(d.getUTCHours())+':'+twoDig(d.getUTCMinutes())+':'+twoDig(d.getUTCSeconds())+'Z ' + twoDig(d.getUTCDate())+'-'+twoDig((d.getUTCMonth()+1))+'-'+d.getUTCFullYear();\n</script>\n\n<script>\n    var radials = [];\n    var leds = [];\n    var connection;\n\n    function init()\n    {\n        for (var i=0;i<7;i++) {\n            radials[i] = new steelseries.Radial('gaugeCanvas'+i, {\n                frameDesign: steelseries.FrameDesign.TILTED_GRAY,\n                gaugeType: steelseries.GaugeType.TYPE4,\n                backgroundColor : steelseries.BackgroundColor.WHITE,\n                ledVisible: false,\n                minValue:0,\n                maxValue:100,\n                size: 301,\n            });\n            radials[i].setThresholdVisible(false);\n            radials[i].setValue(0);\n        }\n        radials[7] = new steelseries.WindDirection('gaugeCanvas7', {\n                frameDesign: steelseries.FrameDesign.TILTED_GRAY,\n                gaugeType: steelseries.GaugeType.TYPE4,\n                backgroundColor : steelseries.BackgroundColor.WHITE,\n                size: 301,\n                lcdVisible: false\n            });\n        for (i=0;i<16;i++) {\n            leds[i] = new steelseries.Led('led'+i, {size: 50});\n        }\n    }\n\n    connect();\n\n    function onConnect() {\n        leds[0].setLedOnOff(true);\n        console.log(\"connected\");\n        connection.send(\"Request Data\");\n    };\n\n    function onClose() {\n        leds[0].setLedOnOff(false);\n        console.log(\"disconnected\");\n        setTimeout(function() { connect()},5000);\n    }\n\n    function doSend(strng, el){\n        if(el.className == \"on\") {\n            el.className=\"off\";\n        \tif (connection.readyState == 1) {\n        \t\tconnection.send(strng);\n        \t}\n        } \n        else {\n            el.className=\"on\";\n        \tif (connection.readyState == 1) {\n        \t\tconnection.send(strng);\n        \t}\n        } \n        return false;\n    }\n\n\tfunction onMessage(msg) {\n        flash(leds[1]);\n        console.log(msg);\n        var data=JSON.parse(msg.data);\n        var radial = radials[data.radial];\n        var led = leds[data.led];\n        if (led && data.status) {\n            if (data.status == 'flash')\n                flash(led,data.duration || 400);\n            else\n                led.setLedOnOff(data.status.toUpperCase()==\"ON\");\n\n        }\n        if (led && data.color) {\n            led.setLedColor(steelseries.LedColor[data.color.toUpperCase()+\"_LED\"]);\n        }\n        if (radial && data.max) {\n            radial.setMaxValue(data.max);\n        }\n        if (radial && data.min) {\n            radial.setMinValue(data.min);\n        }\n        if (radial && data.value) {\n            if (radial.setValueAnimated) {\n                radial.setValueAnimated(data.value);\n            } else if (radial.setValueAnimatedLatest) {\n                radial.setValueAnimatedLatest(data.value);\n                radial.setValueAnimatedAverage(data.value);\n            } else {\n                radial.setValue(data.value);\n            }\n        }\n        if (radial && data.odo) {\n            radial.setOdoValue(data.odo);\n        }\n        if (data.title) {\n            if (radial)\n                radial.setTitleString(data.title);\n            if (led)\n                document.getElementById(\"lable\"+data.led).innerHTML = data.title;\n\n        }\n        if (radial && data.unit) {\n            radial.setUnitString(data.unit)\n        }\n        if (data.flash) {\n                document.getElementById(\"flash\").innerHTML = data.flash;\n        }\n    }\n\n    function connect(cause) {\n        if (cause)\n            console.log(cause.errorMessage);\n        const hostname = window.location.hostname || \"raspi2\"\n        const port = window.location.port || 1880;\n        const url = \"ws://\" + hostname + \":\" + port + \"/ws/dashboard\";\n        connection=new WebSocket(url);\n        connection.onopen=onConnect;\n        connection.onclose=onClose;\n        // Log errors\n        connection.onerror = function (error) {\n            flash(leds[2]);\n            console.log('WebSocket Error ' + error);\n        };\n\n        // Log messages from the server\n        connection.onmessage = onMessage;\n    }\n\n    function flash(led,duration) {\n        led.setLedOnOff(true);\n        setTimeout(function() { led.setLedOnOff(false)},duration || 400);\n    }\n</script>","x":546,"y":1047,"wires":[["7885c297.877a3c"]]},{"id":"7ada2666.8525d8","type":"http in","z":"95e2d5d0.00fa2","name":"","url":"/metar","method":"get","x":216,"y":1047,"wires":[["6ca08ddb.935f74"]]},{"id":"7e009dd0.81ff64","type":"websocket out","z":"95e2d5d0.00fa2","name":"","server":"8a103fc6.75efc","client":"","x":1093,"y":928,"wires":[]},{"id":"45cccff9.ba333","type":"template","z":"95e2d5d0.00fa2","name":"Temp2Radial","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"radial\" : 0,\n    \"value\" : {{payload.temperature}},\n    \"max\" : 50,\n    \"min\" : -10,\n    \"unit\" : \"°C\",\n    \"title\" : \"Temperature\"\n}\n","x":845,"y":492,"wires":[["7e009dd0.81ff64"]]},{"id":"e852b626.17ad48","type":"template","z":"95e2d5d0.00fa2","name":"Wind2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 6,\n    \"value\" : {{payload.wind}},\n    \"max\" : 80,\n    \"unit\" : \"kt\",\n    \"title\" : \"Wind\"\n}\n","x":842,"y":817,"wires":[["7e009dd0.81ff64"]]},{"id":"526f8ae7.ad9074","type":"template","z":"95e2d5d0.00fa2","name":"Hum2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 2,\n    \"value\" : {{payload.humidity}},\n    \"max\" : 100,\n    \"unit\" : \"%\",\n    \"title\" : \"Humidity\"\n}\n","x":846,"y":601,"wires":[["7e009dd0.81ff64"]]},{"id":"59c4f7f.fa63b08","type":"template","z":"95e2d5d0.00fa2","name":"Pres2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 4,\n    \"value\" : {{payload.pressure}},\n    \"min\" : 900,\n    \"max\" : 1100,\n    \"unit\" : \"hPa\",\n    \"title\" : \"Pressure\"\n}\n","x":844,"y":706,"wires":[["7e009dd0.81ff64"]]},{"id":"27b65972.d849a6","type":"websocket in","z":"95e2d5d0.00fa2","name":"","server":"8a103fc6.75efc","client":"","x":239,"y":927,"wires":[["e6a7ed4a.19581","a165de81.5e9a2"]]},{"id":"d5bffc9a.2a4","type":"template","z":"95e2d5d0.00fa2","name":"Dew2Radial","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"radial\" : 3,\n    \"value\" : {{payload.dewpoint}},\n    \"max\" : 50,\n    \"min\" : -10,\n    \"unit\" : \"°C\",\n    \"title\" : \"Dew\"\n}\n","x":844,"y":657,"wires":[["7e009dd0.81ff64"]]},{"id":"1cfe6d9e.e30192","type":"template","z":"95e2d5d0.00fa2","name":"Feels2Radial","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"radial\" : 1,\n    \"value\" : {{payload.feels}},\n    \"max\" : 50,\n    \"min\" : -10,\n    \"unit\" : \"°C\",\n    \"title\" : \"Feels\"\n}\n","x":847,"y":545,"wires":[["7e009dd0.81ff64"]]},{"id":"23f2644c.dc0d9c","type":"template","z":"95e2d5d0.00fa2","name":"Vis2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 5,\n    \"value\" : {{payload.visibility}},\n    \"max\" : 10,\n    \"unit\" : \"km\",\n    \"title\" : \"Visibility\"\n}\n","x":843,"y":758,"wires":[["7e009dd0.81ff64"]]},{"id":"a5f62102.5a09e","type":"template","z":"95e2d5d0.00fa2","name":"Dir2Radial","field":"payload","format":"handlebars","template":"{\n    \"radial\" : 7,\n    \"value\" : {{payload.direction}}\n}\n","x":843,"y":868,"wires":[["7e009dd0.81ff64"]]},{"id":"9d97e266.62682","type":"function","z":"95e2d5d0.00fa2","name":"to VFR LED","func":"var report=msg.payload.response.data[0].METAR[0];\nvar conditions=report.flight_category;\nmsg.payload = {\n    \"led\" : 3,\n    \"status\" : \"ON\",\n    \"color\" :  conditions == \"VFR\" ? \"green\" : conditions == \"MVFR\" ? \"yellow\" : \"red\",\n    \"title\" : \"VFR\"\n}\nreturn msg;","outputs":1,"x":913,"y":421,"wires":[["2e8a4e74.d175b2"]]},{"id":"2e8a4e74.d175b2","type":"websocket out","z":"95e2d5d0.00fa2","name":"","server":"8a103fc6.75efc","client":"","x":1186,"y":421,"wires":[]},{"id":"e6a7ed4a.19581","type":"function","z":"95e2d5d0.00fa2","name":"Init Status LEDs","func":"msgs = [];\n\nmsgs.push({payload:{ \n    \"title\" : \"Connect\",\n    \"led\" : 0,\n    \"color\" : \"blue\"\n}});\nmsgs.push({payload:{ \n    \"title\" : \"Error\",\n    \"led\" : 2,\n    \"status\" : \"flash\",\n    \"color\" : \"red\"\n}});\nmsgs.push({payload:{ \n    \"title\" : \"Data\",\n    \"led\" : 1,\n    \"status\" : \"flash\",\n    \"color\" : \"yellow\"\n}});\nreturn [msgs];","outputs":1,"x":523,"y":927,"wires":[["7e009dd0.81ff64"]]},{"id":"d28f1f9b.2d70e","type":"function","z":"95e2d5d0.00fa2","name":"Extract raw METAR","func":"msg.payload=msg.payload.response.data[0].METAR[0].raw_text[0];\nreturn msg;","outputs":1,"x":646,"y":226,"wires":[["d2e6ac6e.2d195","bcc1aaf2.433e58","1af39e86.e50c61"]]},{"id":"d2e6ac6e.2d195","type":"function","z":"95e2d5d0.00fa2","name":"to NOSIG LED","func":"msg.payload = {\n    \"led\" : 4,\n    \"status\" : msg.payload.indexOf(\"NOSIG\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"green\",\n    \"title\" : \"NOSIG\"\n}\nreturn msg;","outputs":1,"x":921,"y":107,"wires":[["2e8a4e74.d175b2"]]},{"id":"bcc1aaf2.433e58","type":"function","z":"95e2d5d0.00fa2","name":"to CAVOK LED","func":"msg.payload = {\n    \"led\" : 5,\n    \"status\" : msg.payload.indexOf(\"CAVOK\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"green\",\n    \"title\" : \"CAVOK\"\n}\nreturn msg;","outputs":1,"x":922,"y":64,"wires":[["2e8a4e74.d175b2"]]},{"id":"1af39e86.e50c61","type":"function","z":"95e2d5d0.00fa2","name":"to Flash Message","func":"msg.payload = {\n    \"flash\" : msg.payload\n}\nreturn msg;","outputs":1,"x":934,"y":370,"wires":[["2e8a4e74.d175b2"]]},{"id":"3a58c564.c5a73a","type":"comment","z":"95e2d5d0.00fa2","name":"Send HTML Page","info":"","x":235,"y":999,"wires":[]},{"id":"293a8f70.d6c57","type":"comment","z":"95e2d5d0.00fa2","name":"Respond to ping message","info":"","x":262,"y":877,"wires":[]},{"id":"a2f94449.5d06b8","type":"function","z":"95e2d5d0.00fa2","name":"to DIZZELE LED","func":"msg.payload = {\n    \"led\" : 6,\n    \"status\" : msg.payload.indexOf(\"DZ\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"yellow\",\n    \"title\" : \"Dizzle\"\n}\nreturn msg;","outputs":1,"x":930,"y":148,"wires":[["2e8a4e74.d175b2"]]},{"id":"285562db.d7aa9e","type":"function","z":"95e2d5d0.00fa2","name":"to RAIN LED","func":"msg.payload = {\n    \"led\" : 7,\n    \"status\" : msg.payload.indexOf(\"RA\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"yellow\",\n    \"title\" : \"Rain\"\n}\nreturn msg;","outputs":1,"x":910,"y":200,"wires":[["2e8a4e74.d175b2"]]},{"id":"bf5ce258.40a32","type":"function","z":"95e2d5d0.00fa2","name":"to TS LED","func":"msg.payload = {\n    \"led\" : 8,\n    \"status\" : msg.payload.indexOf(\"TS\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"red\",\n    \"title\" : \"Thunderstorm\"\n}\nreturn msg;","outputs":1,"x":911,"y":242,"wires":[["2e8a4e74.d175b2"]]},{"id":"6ca08ddb.935f74","type":"function","z":"95e2d5d0.00fa2","name":"Set Station","func":"if (msg.payload.station) {\n    context.global.station=msg.payload.station;\n}\nreturn msg;","outputs":1,"x":377,"y":1047,"wires":[["586fdf52.a7902"]]},{"id":"a165de81.5e9a2","type":"function","z":"95e2d5d0.00fa2","name":"Set Metar Station","func":"msg.payload=context.global.station || \"EDDH\";\nreturn msg;","outputs":1,"x":270,"y":640,"wires":[["69ab454f.9654bc"]]},{"id":"633ddaa6.9cc224","type":"function","z":"95e2d5d0.00fa2","name":"to FG LED","func":"msg.payload = {\n    \"led\" : 9,\n    \"status\" : msg.payload.indexOf(\"FG\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"red\",\n    \"title\" : \"Fog\"\n}\nreturn msg;","outputs":1,"x":911,"y":285,"wires":[["2e8a4e74.d175b2"]]},{"id":"52947201.ad6b8c","type":"function","z":"95e2d5d0.00fa2","name":"to SN LED","func":"msg.payload = {\n    \"led\" : 10,\n    \"status\" : msg.payload.indexOf(\"SN\") > -1 ? \"ON\" : \"OFF\",\n    \"color\" : \"red\",\n    \"title\" : \"Snow\"\n}\nreturn msg;","outputs":1,"x":913,"y":327,"wires":[["2e8a4e74.d175b2"]]},{"id":"a5d3fe25.5a2c","type":"function","z":"95e2d5d0.00fa2","name":"Extract WX String","func":"var wx=msg.payload.response.data[0].METAR[0].wx_string;\nmsg.payload=wx ? wx.join(): \"\";\nreturn msg;","outputs":1,"x":647,"y":112,"wires":[["a2f94449.5d06b8","285562db.d7aa9e","bf5ce258.40a32","633ddaa6.9cc224","52947201.ad6b8c"]]},{"id":"8a103fc6.75efc","type":"websocket-listener","path":"/ws/dashboard","wholemsg":"false"}]

Thanks everyone for your assistance.

1 Like

If you are happy, fine with me. However, I think your solution is browser dependent. I have tested it successfully in Safari (on my mac) but I cannot see that it works with Chrome (on my mac and win box) and Edge. If not important, just ignore

I never know what browsers my visitors are going to be using, so I can only test in what I have;
Win - Chrome
Win - Firefox
Win - Edge
Win - Safari
Win - Brave
Linux - Chrome
Linux - Firefox
iPad and iPhone (not mine - I'm an Android guy).

All checked and working as expected.

I'm still puzzled why you are seeing different behavior than I am. I always thought Node-RED behaved the same no matter where it was installed, I am not so sure after this experience.

Yes, it is strange. Maybe because I am running your flow on a Raspberry Pi (but browsing from clinets over the network). I will try your flow on a Linux laptop running Debian, maybe behaves differently!??