Met.no weather API plugin

UPDATE: version v1.3 now availalable in github

v1.3
Added daily high and low temperatures.
Added wind direction as an arrow character.
Altitude truncated to integer as it should be given as whole meters.

v1.2
-Better compliancy to met.no Terms Of Service: Using If-Modified-Since headers for queries.

v1.1
-Added “now” values for current conditions. In reality these values are from forecast point which is closest in time as met.no API does not return any realtime measured values.
-Better compliancy to met.no Terms Of Service: coordinates truncated to 4 decimals, more information to user agent header.
-Fixed sanity checks for coordinates.
-Added handling for other than status 200 responses from server.

v1.0
-Initial release.


Plug-in for providing weather information from met.no API.

Source code with instructions can be found from github:

Example of using data in HR:
image

2 Likes

I was testing more my code and found out that following does not work:

    if(plugin.Settings["Latitude"] > 90.0) {
        plugin.Settings["Latitude"] = 90.0;
    }

Is it so that plugin.Settings cannot be written?

Correct. At this time those accessors are read-only. There’s no way to update them in your plugin code.

OK, I can copy settings to other variables and use those.

By testing I found that
plugin.PollingInterval
on the other can be modified in plugin code.

The reason why I started looking the code is that I read met.no terms of service more thoroughly and they set limitations how many decimals coordinates a query can have, how often queries should be made etc. I will update the plugin code soon to comply better with their TOS.

I would just have the plugin handle that. In “onConnect”, read the value from “plugin.Settings” & then save it into local variable. Apply the decimal precision you need to that local variable. Use that in your query request. You don’t need to use the exact value in “plugin.Settings”. You can have the plugin format it as needed.

Next issue. Instructions say to read “Expires” from response headers. When looking with Chrome the headers look like this:

Accept-Ranges: bytes
Access-Control-Allow-Headers: Origin
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: *
Age: 0
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 2535
Content-Type: application/json
Date: Mon, 15 Feb 2021 15:32:07 GMT
Expires: Mon, 15 Feb 2021 16:02:24 GMT
Last-Modified: Mon, 15 Feb 2021 15:32:07 GMT
Server: nginx/1.10.3 (Ubuntu)
Vary: Accept, Accept-Encoding
Via: 1.1 varnish (Varnish/6.2)
X-Backend-Host: b_ybs_api3_a4_api_met_no_locationforecast
X-Varnish: 1039989456

In my plugin code I can access for example Date and Server, but Expires gives undefined. Any idea why?

I have no idea. You should get every header back. There isn’t any internal filter. I do not understand how you can read the Date header but not the Expires header. You need to use the exact character casing. Are you sure you are using the exact character casing for that header?

I made this test code:

console.log("Accept-Ranges: " + response.headers["Accept-Ranges"]);
console.log("Access-Control-Allow-Headers: " + response.headers["Access-Control-Allow-Headers"]);
console.log("Access-Control-Allow-Methods: " + response.headers["Access-Control-Allow-Methods"]);
console.log("Access-Control-Allow-Origin: " + response.headers["Access-Control-Allow-Origin"]);
console.log("Age: " + response.headers["Age"]);
console.log("Connection: " + response.headers["Connection"]);
console.log("Content-Encoding: " + response.headers["Content-Encoding"]);
console.log("Content-Length: " + response.headers["Content-Length"]);
console.log("Content-Type: " + response.headers["Content-Type"]);
console.log("Date: " + response.headers["Date"]);
console.log("Expires: " + response.headers["Expires"]);
console.log("Last-Modified: " + response.headers["Last-Modified"]);
console.log("Server: " + response.headers["Server"]);
console.log("Vary: " + response.headers["Vary"]);
console.log("Via: " + response.headers["Via"]);
console.log("X-Backend-Host: " + response.headers["X-Backend-Host"]);
console.log("X-Varnish: " + response.headers["X-Varnish"]);

And the result is:

Accept-Ranges: bytes
Access-Control-Allow-Headers: Origin
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: *
Age: 1825
Connection: keep-alive
Content-Encoding: undefined
Content-Length: undefined
Content-Type: undefined
Date: Mon, 15 Feb 2021 16:19:13 GMT
Expires: undefined
Last-Modified: undefined
Server: nginx/1.10.3,(Ubuntu)
Vary: Accept,Accept-Encoding
Via: 1.1 varnish (Varnish/6.2)
X-Backend-Host: b_ybs_api3_b3_api_met_no_locationforecast
X-Varnish: 1043425423 1043947199

Quite many undefined.
Of course I cannot be sure what is included in the headers. How to look what is inside headers? I tried JSON.stringify and for(p in response.headers) loop but they do not work.

OK. Thanks for the info. That was very helpful. I found the issue. This will be fixed in the next 3.19.0.0 release. For whatever reason, the current version is not adding any Content-Headers in the response object. This includes the Expires header. It isn’t a casing issue or anything you can fix. You will have to wait for the update.

1 Like

Hi Vpow
I use your excellent plugin because it gives me a more accurate forecast than OpenWeather.
But I have a wish if you want to develop your plugin.

It would be nice if you could also show what some weather data looks like “right now”.
For example. “cloud_area_fraction” and “next_1_hour / summary / symbol_code”.
Then I could show the current weather on the display.

I have tried to edit your plugin, but my knowledge is not really enough.

“Right now” values added, have a look on github development branch:

If you have previously created plugin remember to synchronize device to create new attributes.

Wpow, thanks for the quick response. It works great!

EDIT…
I am trying to learn how to modify this weather plugin and need help with a problem.
How do I most easily truncate a text that contains appendix “_day”, “_night” and “_polartwilight”?

I want to edit the text “clearsky_day”, “clearsky_night”, “clearsky_polartwilight” so that the text only becomes “clearsky”?
The purpose is to then translate the word “clearsky” into Swedish (or another language).

if(ssymbol == “clearsky”) {device.weather_now = “Klar himmel”};
if(ssymbol == “cloudy”) {device.weather_now = “Molnigt”};

Jahå, your question is more about javascript. This is actually the first time I have used javascript. So far I have not considered it as a proper programming language and let’s say the experiences so far have done little to change my opinion.

Anyways, here is one way working in HR to check substring existence in javascript:

if(ssymbol.indexOf("clearsky") !== -1) {
   // doyourstuff;
}

Vpow, thanks again! I had seen that code before, but did not understand how it could do the trick, but it did :slight_smile:

if(ssymbol.indexOf(“clearsky”) !== -1) {device.weather_now = “Klar himmel”};

Test results with Designer version 3.19 show that many headers are now included, for example “Expires” which I was after, but still not all headers.

At least these are missing:

Content-Encoding: undefined
Content-Length: undefined

There might be others, I did not run thorough testing.

Another remaining issue is how to know content of received headers i.e. list keys. I am not a Javascript expert but is there some method missing somewhere? All js tricks found from web to list keys/properties of the header object return either nothing or give fault.

“Content-Length” is working for me. Regarding “Content-Encoding”, it’s probably included in your “Content-Type” header. It will most likely look something like this:

Content-Type: application/json; charset="utf-8"

In this example, you can see from the Content-Type header that this uses UTF-8 encoding.

Pretty sure they do not work :slight_smile:

I use this test url: http://eu.httpbin.org/gzip

which returns in headers:

Content-Encoding: gzip
Content-Length: 417

But HR shows them as undefined.

They work for me when I test them with my Roku. The only one that doesn’t work is “Content-Encoding” since the encoding is included in the “Content-Type” header. So again, please check your “Content-Type” header. The encoding may be defined there.

var response = http.get("http://" + plugin.Settings["Host"] + ":8060/query/active-app");
console.log("Content-Type: " + response.headers["Content-Type"]);
console.log("Content-Length: " + response.headers["Content-Length"]);
console.log("Content-Encoding: " + response.headers["Content-Encoding"]);

Test code in plugin:

var response = http.get("http://eu.httpbin.org/gzip");
console.log("Content-Encoding: " + response.headers["Content-Encoding"]);
console.log("Content-Length: " + response.headers["Content-Length"]);
console.log("Content-Type: " + response.headers["Content-Type"]);

Result:

Content-Encoding: undefined
Content-Length: undefined
Content-Type: application/json

To verify what is really going in network I use Wireshark capture:

Hypertext Transfer Protocol
    HTTP/1.1 200 OK\r\n
        [Expert Info (Chat/Sequence): HTTP/1.1 200 OK\r\n]
            [HTTP/1.1 200 OK\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Response Version: HTTP/1.1
        Status Code: 200
        [Status Code Description: OK]
        Response Phrase: OK
    Date: Tue, 23 Feb 2021 17:51:24 GMT\r\n
    Content-Type: application/json\r\n
    Content-Length: 195\r\n
        [Content length: 195]
    Connection: keep-alive\r\n
    Server: gunicorn/19.9.0\r\n
    Content-Encoding: gzip\r\n
    Access-Control-Allow-Origin: *\r\n
    Access-Control-Allow-Credentials: true\r\n
    \r\n
    [HTTP response 1/1]
    [Time since request: 0.148783000 seconds]
    [Request in frame: 63]
    [Request URI: http://eu.httpbin.org/gzip]
    Content-encoded entity body (gzip): 195 bytes -> 230 bytes
    File Data: 230 bytes

So server is sending Content-Length and Content-Encoding but for some reason HR cannot show them. These all are own headers and cannot be included in each others.

I don’t know exactly why your URL is behaving differently than mine but I have found the cause. The Designer’s simulator uses the WinHttpHandler for its HTTP Request & for some reason those headers are not included for your URL when I use this library. If I use the standard HttpHandler those are returned. The reason the Home Remote uses WinHttpHandler is because it supports HTTP/2 & I think there were a few other performance reasons too. This should only be an issue in the simulator. This should not be an issue in the apps.

I suppose I can update the simulator to use the old HttpHandler for its web requests but then we lose those performance enhancements with the new WinHttpHandler. We also lose HTTP/2 support. I actually need HTTP/2 for a few integrations. The Amazon Alexa API only works with HTTP/2.

Do you need these headers for the simulator? Might I ask too, why exactly do you need to read the content-length & encoding headers? The library already returns you a nice JSON object that is easy to parse. You shouldn’t ever need to decode the data yourself.