JSON Api weather data

I fear that you are still stuck in C programming mode :wink:

The easy way to get an arbitrary number of results from an array is to use the slice function. In your case, create your output arrays as normal and then take a slice of them into your output messages instead of the whole variable.

JavaScript is a MUCH more flexible language than C. That has positives and negatives of course. In general though, you will be rather less concerned about optimisation of code and more about readability. Bearing in mind that JavaScript assigns variables by reference rather than value, it really doesn't hurt performance to do intermediate variable assignments. (At least not normally but that is a topic for a different discussion).

Thanks both of you @Steve-Mcl and @TotallyInformation.

Maybe my mindset was in C language. I get the idea of using the slice as mention above.

But i dont get how to use that function when I got this ending (.data.instant.details.air_temperature) after the array.

msg.payload.properties.timeseries[15].data.instant.details.air_temperature;

Send the output of your function to a debug node then read the following canned reply...

There’s a great page in the docs that will explain how to use the debug panel to find the right path to any data item.

https://nodered.org/docs/user-guide/messages

Thanks @Steve-Mcl

Testing with this code:

var rTemperature;
var Reading = 0;

var originalArray = msg.payload.properties.timeseries;
var newArray = originalArray.slice(1,15);//get items 0 ~ 20

msg.payload = {
    'ArrayX': newArray
}
return msg;

Gives me this result:
image

Definitely on the right path here. And can see that the data I want out is here:

payload.ArrayX[0].data.instant.details.air_temperature

So, i think I need to use this new array, in the code that @TotallyInformation posted earlier. But dont get how to use it :confused:

Which code is that?

what are you trying to achieve?

I want to make an output array containing only air temperature for as many arrays that are listed :slight_smile:

So if i Set
var newArray = originalArray.slice(7,21);

I want an array to conain all temperatures for this (7 to 21) time period.

var originalArray = msg.payload.properties.timeseries;
var sliced = originalArray.slice(1, 15);

//filter out items without air_temperature 
var filtered = sliced.filter(e => {
    try {
        return (e.data.instant.details.air_temperature != null);
    } catch (ex) { }
    return false; //exclude item
});

//re-map the items to just an array of air_temperature
var temps = filtered.map(e => {
    try {
        return e.data.instant.details.air_temperature;
    } catch (ex) {
        return null;
    }
});

msg.payload = temps;
return msg;

that ↑ should return "an array" of temperatures for the slice you selected.

NOTE: This is untested as you have provided no test data.

Though I doubt this is actually what you need. (objects are better than an array of values as you can keep additional context with an object)

Thank you so much! This is exact what i needed, and it works perfectly.

This is my Node-Red code:

[{"id":"eb41f62a.95ba98","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"75755866.201688","type":"OpcUa-Client","z":"eb41f62a.95ba98","endpoint":"38f7b58f.008a3a","action":"write","deadbandtype":"a","deadbandvalue":1,"time":10,"timeUnit":"s","certificate":"n","localfile":"","securitymode":"None","securitypolicy":"None","name":"","x":1140,"y":120,"wires":[[]]},{"id":"a1b662e9.e803","type":"OpcUa-Item","z":"eb41f62a.95ba98","item":"ns=4;s=|var|WAGO 750-8212 PFC200 G2 2ETH RS.Application.gvl_OpcUA.rTemperature_1h","datatype":"Float Array","value":"","name":"OPC","x":830,"y":100,"wires":[["75755866.201688"]]},{"id":"f81cd48d.23e4a8","type":"inject","z":"eb41f62a.95ba98","name":"","props":[{"p":"headers","v":"{\"User-Agent\":\"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0\"}","vt":"json"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"str","x":190,"y":100,"wires":[["4ae770a8.b0b25"]]},{"id":"4ae770a8.b0b25","type":"http request","z":"eb41f62a.95ba98","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://api.met.no/weatherapi/locationforecast/2.0/complete?altitude=75&lat=59.0234&lon=6.0413","tls":"","persist":false,"proxy":"","authType":"","x":370,"y":100,"wires":[["94e0fbd1.9af298","78504d54.e46254","1dc7854a.d71ddb","b20c9e7e.00aa","44ca5608.b504b8","1e086a2a.a9c4f6","65462d81.bbcca4","230afe63.3a8562"]]},{"id":"94e0fbd1.9af298","type":"function","z":"eb41f62a.95ba98","name":"Temperature 1h","func":"var originalArray = msg.payload.properties.timeseries;\nvar sliced = originalArray.slice(0, 85);\n\n//filter out items without air_temperature \nvar filtered = sliced.filter(e => {\n    try {\n        return (e.data.instant.details.air_temperature != null);\n    } catch (ex) { }\n    return false; //exclude item\n});\n\n//re-map the items to just an array of air_temperature\nvar Temperature_1h = filtered.map(e => {\n    try {\n        return e.data.instant.details.air_temperature;\n    } catch (ex) {\n        return null;\n    }\n});\n\nmsg.payload = Temperature_1h;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":660,"y":100,"wires":[["a1b662e9.e803"]]},{"id":"b8936261.c1c2c","type":"OpcUa-Item","z":"eb41f62a.95ba98","item":"ns=4;s=|var|WAGO 750-8212 PFC200 G2 2ETH RS.Application.gvl_OpcUA.rPrecipitation_1h","datatype":"Float Array","value":"","name":"OPC","x":830,"y":160,"wires":[["75755866.201688"]]},{"id":"78504d54.e46254","type":"function","z":"eb41f62a.95ba98","name":"Precipitation 1h","func":"var originalArray = msg.payload.properties.timeseries;\nvar sliced = originalArray.slice(0, 85);\n\n//filter out items without air_temperature \nvar filtered = sliced.filter(e => {\n    try {\n        return (e.data.next_1_hours.details.precipitation_amount != null);\n    } catch (ex) { }\n    return false; //exclude item\n});\n\n//re-map the items to just an array of air_temperature\nvar Precipitation_1h = filtered.map(e => {\n    try {\n        return e.data.next_1_hours.details.precipitation_amount;\n    } catch (ex) {\n        return null;\n    }\n});\n\nmsg.payload = Precipitation_1h;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":660,"y":160,"wires":[["b8936261.c1c2c"]]},{"id":"c16de603.9be4a8","type":"OpcUa-Item","z":"eb41f62a.95ba98","item":"ns=4;s=|var|WAGO 750-8212 PFC200 G2 2ETH RS.Application.gvl_OpcUA.rProbability_1h","datatype":"Float Array","value":"","name":"OPC","x":830,"y":220,"wires":[["75755866.201688"]]},{"id":"1dc7854a.d71ddb","type":"function","z":"eb41f62a.95ba98","name":"Probability 1h","func":"var originalArray = msg.payload.properties.timeseries;\nvar sliced = originalArray.slice(0, 85);\n\n//filter out items without air_temperature \nvar filtered = sliced.filter(e => {\n    try {\n        return (e.data.next_1_hours.details.probability_of_precipitation != null);\n    } catch (ex) { }\n    return false; //exclude item\n});\n\n//re-map the items to just an array of air_temperature\nvar Probability_1h = filtered.map(e => {\n    try {\n        return e.data.next_1_hours.details.probability_of_precipitation;\n    } catch (ex) {\n        return null;\n    }\n});\n\nmsg.payload = Probability_1h;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":660,"y":220,"wires":[["c16de603.9be4a8"]]},{"id":"6f9a2322.b558ac","type":"OpcUa-Item","z":"eb41f62a.95ba98","item":"ns=4;s=|var|WAGO 750-8212 PFC200 G2 2ETH RS.Application.gvl_OpcUA.rTemperature_6h_max","datatype":"Float Array","value":"","name":"OPC","x":830,"y":400,"wires":[["75755866.201688"]]},{"id":"b20c9e7e.00aa","type":"function","z":"eb41f62a.95ba98","name":"Tempereture 6h max","func":"var originalArray = msg.payload.properties.timeseries;\nvar sliced = originalArray.slice(0, 85);\n\n//filter out items without air_temperature \nvar filtered = sliced.filter(e => {\n    try {\n        return (e.data.next_6_hours.details.air_temperature_max != null);\n    } catch (ex) { }\n    return false; //exclude item\n});\n\n//re-map the items to just an array of air_temperature\nvar Temperature_6h_max = filtered.map(e => {\n    try {\n        return e.data.next_6_hours.details.air_temperature_max;\n    } catch (ex) {\n        return null;\n    }\n});\n\nmsg.payload = Temperature_6h_max;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":660,"y":400,"wires":[["6f9a2322.b558ac"]]},{"id":"f53a0c51.6485c","type":"OpcUa-Item","z":"eb41f62a.95ba98","item":"ns=4;s=|var|WAGO 750-8212 PFC200 G2 2ETH RS.Application.gvl_OpcUA.rPrecipitation_6h","datatype":"Float Array","value":"","name":"OPC","x":830,"y":280,"wires":[["75755866.201688"]]},{"id":"44ca5608.b504b8","type":"function","z":"eb41f62a.95ba98","name":"Precipitation 6h","func":"var originalArray = msg.payload.properties.timeseries;\nvar sliced = originalArray.slice(0, 85);\n\n//filter out items without air_temperature \nvar filtered = sliced.filter(e => {\n    try {\n        return (e.data.next_6_hours.details.precipitation_amount != null);\n    } catch (ex) { }\n    return false; //exclude item\n});\n\n//re-map the items to just an array of air_temperature\nvar Precipitation_6h = filtered.map(e => {\n    try {\n        return e.data.next_6_hours.details.precipitation_amount;\n    } catch (ex) {\n        return null;\n    }\n});\n\nmsg.payload = Precipitation_6h;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":660,"y":280,"wires":[["f53a0c51.6485c"]]},{"id":"4e21e000.5598a","type":"OpcUa-Item","z":"eb41f62a.95ba98","item":"ns=4;s=|var|WAGO 750-8212 PFC200 G2 2ETH RS.Application.gvl_OpcUA.rProbability_6h","datatype":"Float Array","value":"","name":"OPC","x":830,"y":340,"wires":[["75755866.201688"]]},{"id":"1e086a2a.a9c4f6","type":"function","z":"eb41f62a.95ba98","name":"Probability 6h","func":"var originalArray = msg.payload.properties.timeseries;\nvar sliced = originalArray.slice(0, 85);\n\n//filter out items without air_temperature \nvar filtered = sliced.filter(e => {\n    try {\n        return (e.data.next_6_hours.details.probability_of_precipitation != null);\n    } catch (ex) { }\n    return false; //exclude item\n});\n\n//re-map the items to just an array of air_temperature\nvar Probability_6h = filtered.map(e => {\n    try {\n        return e.data.next_6_hours.details.probability_of_precipitation;\n    } catch (ex) {\n        return null;\n    }\n});\n\nmsg.payload = Probability_6h;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":660,"y":340,"wires":[["4e21e000.5598a"]]},{"id":"65462d81.bbcca4","type":"debug","z":"eb41f62a.95ba98","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":650,"y":40,"wires":[]},{"id":"49198fa4.c79ce","type":"OpcUa-Item","z":"eb41f62a.95ba98","item":"ns=4;s=|var|WAGO 750-8212 PFC200 G2 2ETH RS.Application.gvl_OpcUA.rTemperature_6h_min","datatype":"Float Array","value":"","name":"OPC","x":830,"y":460,"wires":[["75755866.201688"]]},{"id":"230afe63.3a8562","type":"function","z":"eb41f62a.95ba98","name":"Tempereture 6h min","func":"var originalArray = msg.payload.properties.timeseries;\nvar sliced = originalArray.slice(0, 85);\n\n//filter out items without air_temperature \nvar filtered = sliced.filter(e => {\n    try {\n        return (e.data.next_6_hours.details.air_temperature_min != null);\n    } catch (ex) { }\n    return false; //exclude item\n});\n\n//re-map the items to just an array of air_temperature\nvar Temperature_6h_min = filtered.map(e => {\n    try {\n        return e.data.next_6_hours.details.air_temperature_min;\n    } catch (ex) {\n        return null;\n    }\n});\n\nmsg.payload = Temperature_6h_min;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":660,"y":460,"wires":[["49198fa4.c79ce"]]},{"id":"38f7b58f.008a3a","type":"OpcUa-Endpoint","z":"","endpoint":"opc.tcp://10.0.0.115:4840","secpol":"None","secmode":"None","login":false}]

And this is how it looks when precented to my opcUA server:

And in the server (Wago PLC) i now can use the data as i wanted. Works perfect :slight_smile:

If you or anyone else has any suggestions for improvement for the Node-red code I'd be grateful. Even though its working perfectly, maybe you know how to make it more compact, or more readable.

Thanks again! :slight_smile:

Thanks again for this code, @Steve-Mcl

I get the function of it, but i dont quite get all the functions in it. I am reading about filter, try, catch etc. But not 100% getting the idea, specially "e =>" and "ex" and "!= null".

Any chance you could walk me trough your code? It works perfecly as i want, but i also want to learn and know how the code is running.

Thank you :slight_smile:

e => is the same as function(e) {}

=> is an arrow function

example...

//filter out items without air_temperature 
var filtered = sliced.filter(e => {
    try {
        return (e.data.instant.details.air_temperature != null);
    } catch (ex) { }
    return false; //exclude item
});

is pretty much shorthand for ...

var filtered = sliced.filter(function(e) {
    try {
        return (e.data.instant.details.air_temperature != null);
    } catch (ex) { }
    return false; //exclude item
});

ex...

ex is the variable for the caught exception
see try...catch
e.g...

try {
        var x = 1/0; cause a divide by zero exception
    } catch (ex) { 
       console.log(ex); // << log the exception
    }

!= null

!= means "not equal to"
null means "null" or "undefined"

It is to ensure e.data.instant.details.air_temperature is not null

Thank you @Steve-Mcl. Makes more sense now :slight_smile:

And, now Im stuck again. I cannot run a array of string into the OPCUA client. Therefore I want to convert array of string, to a array og number (int).

When using your code on the array of string, I get this out:
image

So I want to change out every word with a unique number. For example I want all "heavyrain" to be "4". And every "lightrain" to be "3".

Any chance you could help me out here as well? :slight_smile:

EDIT:
I am also ofc trying to this myself, but aint getting anywhere.. I tried this:

if (Symbol_1h == "heavyrain"){
    StringConv=1;
}
else{
    StringConv=Symbol_1h;
}

msg.payload = StringConv;
return msg;

Modify the function to perform a lookup...

var originalArray = msg.payload.properties.timeseries;
var sliced = originalArray.slice(1, 15);

//filter out items without air_temperature 
var filtered = sliced.filter(e => {
    try {
        return (e.data.instant.details.air_temperature != null);
    } catch (ex) { }
    return false; //exclude item
});

var nameToNumber = function(name) {
    switch (name) {
        case: "heavyrain":
            return 4;
        case: "lightrain":
            return 3;
        case: "ADD-MORE-DEFS": //<< TODO
            return 2;//<< TODO
    }
    return 0; //default to zero
}

//re-map the items to just an array of air_temperature
var temps = filtered.map(e => {
    try {
        let val = nameToNumber(e.data.instant.details.air_temperature)
        return val ;
    } catch (ex) {
        return 0;  //<<return 0 instead of null if above fails
    }
});

msg.payload = temps;
return msg;

NOTE: Untested

Thank you @Steve-Mcl. I will read about the look up function.

When testing i get those error:

Can you see whats missing?

Typo

change the lookup function to ...

var nameToNumber = function(name) {
    switch (name) {
        case "heavyrain":
            return 4;
        case "lightrain":
            return 3;
        case "ADD-MORE-DEFS": //<< TODO
            return 2;//<< TODO
    }
    return 0; //default to zero
}

PS, the error text in your screen shot tells you.

If you were to fix this for your self, a quick search for "JavaScript switch" would have shown you how it should be written.

You dont learn much copy+pasting other peoples code.

Thanks. I am fully aware that I dont learn much from copy/paste. After I want to try making this whole code from scratch.

I get that the switch case look for the word in the case structure, and replace it with the number. But I dont get what the function around it does, and not what the function name is for. I will continue reading about functions.

That makes it re-usable (callable).

A function shoule have a name so that you can call it.

This...

var nameToNumber = function(name) {
    switch (name) {
        case "heavyrain":
            return 4;
        case "lightrain":
            return 3;
        case "ADD-MORE-DEFS": //<< TODO
            return 2;//<< TODO
    }
    return 0; //default to zero
}

is quite similar to ..

function nameToNumber(name) {
    switch (name) {
        case "heavyrain":
            return 4;
        case "lightrain":
            return 3;
        case "ADD-MORE-DEFS": //<< TODO
            return 2;//<< TODO
    }
    return 0; //default to zero
}

but one of them is hoisted (the other is not) - for a learning point for you to look up.

Allright, so when you call nameToNumber , you get the result from the switch.

And the switch should lookup the payload where heavyrain etc is stored.

Still not working, maybe ill get it some day :stuck_out_tongue:

Add this to the inside the nameToNumber function ...

node.warn(name)

e.g...

function nameToNumber(name) {
    node.warn(name); // <<<<<<<<<<
    switch (name) {
        case "heavyrain":
            return 4;
        case "lightrain":
            return 3;
        case "ADD-MORE-DEFS": //<< TODO
            return 2;//<< TODO
    }
    return 0; //default to zero
}

then look at the entries in the debug sidebar.

This lists up all the words i want to change out with numbers

image

And now do you see why it doesnt work ?