Skip to content

Instantly share code, notes, and snippets.

@KanyonKris
Last active July 18, 2022 05:20
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save KanyonKris/d7af9f5d6f6923324466c58ea4f0655f to your computer and use it in GitHub Desktop.
Save KanyonKris/d7af9f5d6f6923324466c58ea4f0655f to your computer and use it in GitHub Desktop.
OpenWeatherMap Weather Dashboard with Icons - Landscape

This flow pulls weather data from OpenWeatherMap (OWM) then presents it in a dashboard showing current weather conditions along with forecasts for the next 6 hours and 4 days. The mix of text and icons creates a compact and attractive display.

This version is formatted in a landscape orientation. A portrait version is also available.

image

Starting from Dashboard With Current Conditions And Forecast by djiwondee I made these changes:

One Call

I used the new One Call API from OpenWeatherMap that returns all this data:

  • Minute forecast for 1 hour
  • Hourly forecast for 48 hours
  • Daily forecast for 7 days
  • Historical data for 5 previous days

No Custom Nodes

The OpenWeatherMap custom node is good but until June 2020 it didn't support the new One Call API. So I used standard nodes, which was fairly easy. The HTTP call is simple and JSON data is returned which is perfect for Node-Red.

Weather Icons

I used the "lite" set of weather icons which are now included in Node-Red. No need to install an icon set.

Setup and Configuration

Go to OpenWeatherMap and sign up for a free account to get an API key.

Open the node labeled Settings and change these parameters:

  • Change msg.payload.lat to your latitude.
  • Change msg.payload.lon to your longitude.
  • Change msg.payload.appid to your API key.
  • Change msg.payload.units to imperial or metric.
  • Change msg.payload.lang to your language, for example en for English. List of OWM languages
  • Change msg.payload.hour12 to true to display times in 12 hour am/pm format, or false for 24 hour time format.

NOTE: You can get your latitude and longitude by looking up your town in OpenWeatherMap.

image

Changes

07 Apr 2021

  • Use timezone from OWM to compute times and dates instead of using Node Red server settings.
  • Added msg.payload.lang to support several languages. (If the short weekday names for the 4 day forecast do not match your language, you may need to upgrade to NodeJS 13 or higher when full international support was added for the toLocaleString function, or use the alternate code in the Format forecast data node.)
  • Added msg.payload.hour12 to support 12 or 24 hour time display.
  • Changed name of Set location, appid, units node to Settings.
[
{
"id": "5f633a28.607134",
"type": "tab",
"label": "OWM Landscape",
"disabled": false,
"info": ""
},
{
"id": "180a101a.9cd43",
"type": "ui_button",
"z": "5f633a28.607134",
"name": "IconRefresh",
"group": "70fb931.dfc146c",
"order": 1,
"width": 2,
"height": 2,
"passthru": false,
"label": "",
"tooltip": "Refresh",
"color": "",
"bgcolor": "",
"icon": "fa-refresh fa-4x",
"payload": "true",
"payloadType": "bool",
"topic": "",
"x": 990,
"y": 240,
"wires": [
[
"c5efdf55.db32f"
]
]
},
{
"id": "9c052db7.3c434",
"type": "ui_text",
"z": "5f633a28.607134",
"group": "70fb931.dfc146c",
"order": 5,
"width": 5,
"height": 1,
"name": "Description",
"label": "",
"format": "{{msg.payload.current.weather[0].description}}",
"layout": "row-left",
"x": 990,
"y": 40,
"wires": []
},
{
"id": "ec4eb2d0.974c",
"type": "ui_text",
"z": "5f633a28.607134",
"group": "70fb931.dfc146c",
"order": 3,
"width": 3,
"height": 1,
"name": "Wind",
"label": "",
"format": "{{msg.payload.current.wind_speed}}&nbsp;&nbsp;<i class=\"wi wi-darksky-wind\"></i>&nbsp;&nbsp;{{msg.payload.current.wind_cardinal}}",
"layout": "col-center",
"x": 970,
"y": 120,
"wires": []
},
{
"id": "40f44662.3ecde8",
"type": "ui_text",
"z": "5f633a28.607134",
"group": "70fb931.dfc146c",
"order": 4,
"width": 3,
"height": 1,
"name": "SunriseTime",
"label": "",
"format": "<i class=\"wi wi-owm-01d\"></i>&nbsp;<i class=\"fa fa-arrow-up\"></i>&nbsp;&nbsp;{{msg.payload.current.sunrise}}",
"layout": "row-center",
"x": 990,
"y": 160,
"wires": []
},
{
"id": "5b97953.01fdd6c",
"type": "ui_text",
"z": "5f633a28.607134",
"group": "70fb931.dfc146c",
"order": 6,
"width": 3,
"height": 1,
"name": "SunsetTime",
"label": "",
"format": "<i class=\"wi wi-wu-sunny\"></i>&nbsp;<i class=\"fa fa-arrow-down\"></i>&nbsp;&nbsp;{{msg.payload.current.sunset}}",
"layout": "row-center",
"x": 990,
"y": 200,
"wires": []
},
{
"id": "d284ed2f.c15c8",
"type": "comment",
"z": "5f633a28.607134",
"name": "OpenWeatherMap One Call API for Weather and Forecast",
"info": "",
"x": 250,
"y": 40,
"wires": []
},
{
"id": "9dbf3c95.67c74",
"type": "ui_template",
"z": "5f633a28.607134",
"group": "70fb931.dfc146c",
"name": "Forecast2",
"order": 7,
"width": 10,
"height": 2,
"format": "<div style=\"height: 100%; justify-content: center; align-items: center;\">\n <div layout=\"rowicons\" layout-align=\"space-around start\" ng-repeat=\"data in msg.payload.rowicons\" style=\"font-size:150%;padding-top: 5px;padding-bottom: 5px\">\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell01}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell02}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell03}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell04}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell05}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell06}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell07}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell08}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell09}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell10}}\"></i></span>\n </div>\n <div layout=\"rowtext\" layout-align=\"space-around start\" ng-repeat=\"data in msg.payload.rowtext\" style=\"line-height: 150%\">\n <span flex style=\"color: white;text-align: center;\">{{data.cell01}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell02}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell03}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell04}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell05}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell06}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell07}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell08}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell09}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell10}}</span>\n </div>\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
"x": 980,
"y": 300,
"wires": [
[]
]
},
{
"id": "7adde26c.514aec",
"type": "function",
"z": "5f633a28.607134",
"name": "Format forecast data",
"func": "var fcdata = {};\nvar units=flow.get('units');\nif (units===undefined)\n{\n units=\"imperial\";\n}\n\nfunction formatTemp(high, low){\n if (units == \"imperial\") {\n if (low){\n temp = parseFloat(high).toFixed() + '/' + parseFloat(low).toFixed()\n }\n else {\n temp = parseFloat(high).toFixed() + '°F'\n }\n }\n else { // metric\n if (low){\n temp = parseFloat(high).toFixed() + '/' + parseFloat(low).toFixed()\n }\n else {\n temp = parseFloat(high).toFixed() + '°C'\n }\n }\n return temp;\n}\n\nfunction dayName(unixTime){\n var dateObject = new Date(unixTime * 1000);\n return dateObject.toLocaleString(flow.get('lang'), { timezone: msg.payload.timezone, weekday: 'short'});\n // If the line above is not producing the correct short weekday names for the language set by 'lang',\n // you can try upgrading to NodeJS version 13 or higher (when full international support was added for the toLocaleString function),\n // or you can use the code below which is an example for French short weekday names.\n/* \n switch (dateObject.toLocaleString('en', { timezone: msg.payload.timezone, weekday: 'short'})) {\n case 'Mon':\n return 'Lun';\n case 'Tue':\n return 'Mar';\n case 'Wed':\n return 'Mer';\n case 'Thu':\n return 'Jeu';\n case 'Fri':\n return 'Ven';\n case 'Sat':\n return 'Sam';\n case 'Sun':\n return 'Dim';\n }\n*/\n}\n\nfunction timeConvert(UNIX_timestamp){\n var dateObject = new Date(UNIX_timestamp * 1000);\n if (flow.get('hour12')) { // 12 hour time format\n return dateObject.toLocaleString('en', { timezone: msg.payload.timezone, hour12: true, hour: 'numeric'}).toLowerCase();\n } else { // 24 hour time format\n return dateObject.toLocaleString('en', { timezone: msg.payload.timezone, hour12: false, hour: 'numeric'}) + ':00';\n } \n}\n\n// prepare forecast data for CSS based ui widget\nfcdata.payload = {\n rowtext: {\n \tdata01: {\n cell01: timeConvert(msg.payload.hourly[1].dt),\n cell02: timeConvert(msg.payload.hourly[2].dt),\n cell03: timeConvert(msg.payload.hourly[3].dt),\n cell04: timeConvert(msg.payload.hourly[4].dt),\n cell05: timeConvert(msg.payload.hourly[5].dt),\n cell06: timeConvert(msg.payload.hourly[6].dt),\n cell07: dayName(msg.payload.daily[1].dt),\n cell08: dayName(msg.payload.daily[2].dt),\n cell09: dayName(msg.payload.daily[3].dt),\n cell10: dayName(msg.payload.daily[4].dt),\n \t},\n \tdata02: {\n cell01: formatTemp(msg.payload.hourly[1].temp),\n cell02: formatTemp(msg.payload.hourly[2].temp),\n cell03: formatTemp(msg.payload.hourly[3].temp),\n cell04: formatTemp(msg.payload.hourly[4].temp),\n cell05: formatTemp(msg.payload.hourly[5].temp),\n cell06: formatTemp(msg.payload.hourly[6].temp),\n cell07: formatTemp(msg.payload.daily[1].temp.max, msg.payload.daily[0].temp.min),\n cell08: formatTemp(msg.payload.daily[2].temp.max, msg.payload.daily[1].temp.min),\n cell09: formatTemp(msg.payload.daily[3].temp.max, msg.payload.daily[2].temp.min),\n cell10: formatTemp(msg.payload.daily[4].temp.max, msg.payload.daily[3].temp.min),\n \t}\n },\n rowicons: {\n \tdata01: {\n \t\tcell01: msg.payload.hourly[1].weather[0].icon,\n \t\tcell02: msg.payload.hourly[2].weather[0].icon,\n \t\tcell03: msg.payload.hourly[3].weather[0].icon,\n \t\tcell04: msg.payload.hourly[4].weather[0].icon,\n \t\tcell05: msg.payload.hourly[5].weather[0].icon,\n \t\tcell06: msg.payload.hourly[6].weather[0].icon,\n \t\tcell07: msg.payload.daily[1].weather[0].icon,\n \t\tcell08: msg.payload.daily[2].weather[0].icon,\n \t\tcell09: msg.payload.daily[3].weather[0].icon,\n \t\tcell10: msg.payload.daily[4].weather[0].icon,\n \t}\n }\n}\n\nreturn fcdata;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 760,
"y": 300,
"wires": [
[
"9dbf3c95.67c74"
]
]
},
{
"id": "d8c5527f.7739",
"type": "ui_text",
"z": "5f633a28.607134",
"group": "70fb931.dfc146c",
"order": 2,
"width": 2,
"height": 1,
"name": "Temperature",
"label": "",
"format": "<p style=\"font-size: 200%\">{{msg.payload.current.temp}}</p>",
"layout": "row-left",
"x": 990,
"y": 80,
"wires": []
},
{
"id": "681b2b94.9f7a44",
"type": "http request",
"z": "5f633a28.607134",
"name": "Get OWM data",
"method": "GET",
"ret": "obj",
"paytoqs": true,
"url": "https://api.openweathermap.org/data/2.5/onecall",
"tls": "",
"persist": false,
"proxy": "",
"authType": "",
"x": 520,
"y": 180,
"wires": [
[
"67075190.fc6f3",
"7adde26c.514aec"
]
]
},
{
"id": "c72d96f0.204148",
"type": "inject",
"z": "5f633a28.607134",
"name": "Trigger",
"props": [
{
"p": "payload",
"v": "true",
"vt": "bool"
},
{
"p": "topic",
"v": "",
"vt": "string"
}
],
"repeat": "600",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "true",
"payloadType": "bool",
"x": 120,
"y": 180,
"wires": [
[
"79276db2.a2ce14"
]
]
},
{
"id": "67075190.fc6f3",
"type": "function",
"z": "5f633a28.607134",
"name": "Format current data",
"func": "var icon = {};\n\nvar units = flow.get('units');\nif (units === undefined) {units = 'imperial';}\n\nfunction timeConvert(UNIX_timestamp){\n var dateObject = new Date(UNIX_timestamp * 1000);\n if (flow.get('hour12')) { // 12 hour time format\n return dateObject.toLocaleString('en', { timezone: msg.payload.timezone, hour12: true, hour: 'numeric', minute: '2-digit'}).toLowerCase();\n } else { // 24 hour time format\n return dateObject.toLocaleString('en', { timezone: msg.payload.timezone, hour12: false, hour: 'numeric', minute: '2-digit'});\n } \n}\n\nvar degreesToCardinal = function(deg){\n if (deg>11.25 && deg<=33.75){return \"NNE\";}\n else if (deg>33.75 && deg<56.25){return \"NE\";}\n else if (deg>56.25 && deg<78.75){return \"ENE\";}\n else if (deg>78.75 && deg<101.25){return \"E\";}\n else if (deg>101.25 && deg<123.75){return \"ESE\";}\n else if (deg>123.75 && deg<146.25){return \"SE\";}\n else if (deg>146.25 && deg<168.75){return \"SSE\";}\n else if (deg>168.75 && deg<191.25){return \"S\";}\n else if (deg>191.25 && deg<213.75){return \"SSW\";}\n else if (deg>213.75 && deg<236.25){return \"SW\";}\n else if (deg>236.25 && deg<258.75){return \"WSW\";}\n else if (deg>258.75 && deg<281.25){return \"W\";}\n else if (deg>281.25 && deg<303.75){return \"WNW\";}\n else if (deg>303.75 && deg<326.25){return \"NW\";}\n else if (deg>326.25 && deg<348.75){return \"NNW\";}\n else {return \"N\";}\n}\n\nif (units == \"imperial\")\n{\n msg.payload.current.temp = msg.payload.current.temp.toFixed() + ' °F';\n msg.payload.current.wind_speed = msg.payload.current.wind_speed.toFixed() + ' mph';\n}\nelse // metric units\n{\n msg.payload.current.temp = msg.payload.current.temp.toFixed(1) + ' °C';\n msg.payload.current.wind_speed = msg.payload.current.wind_speed.toFixed(1) + ' m/s';\n}\n\nmsg.payload.current.wind_cardinal = degreesToCardinal(msg.payload.current.wind_deg);\n\nmsg.payload.current.sunrise = timeConvert(msg.payload.current.sunrise);\nmsg.payload.current.sunset = timeConvert(msg.payload.current.sunset);\n\nvar iconString = 'wi-owm-' + msg.payload.current.weather[0].icon + ' wi-4x';\nicon = {\n ui_control: {\n icon: iconString\n }\n}; \n\nreturn [msg, icon];",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 750,
"y": 180,
"wires": [
[
"d8c5527f.7739",
"ec4eb2d0.974c",
"9c052db7.3c434",
"40f44662.3ecde8",
"5b97953.01fdd6c"
],
[
"180a101a.9cd43"
]
]
},
{
"id": "79276db2.a2ce14",
"type": "change",
"z": "5f633a28.607134",
"name": "Settings",
"rules": [
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "set",
"p": "payload.lat",
"pt": "msg",
"to": "40.30",
"tot": "str"
},
{
"t": "set",
"p": "payload.lon",
"pt": "msg",
"to": "-111.69",
"tot": "str"
},
{
"t": "set",
"p": "payload.appid",
"pt": "msg",
"to": "<appID>",
"tot": "str"
},
{
"t": "set",
"p": "payload.units",
"pt": "msg",
"to": "imperial",
"tot": "str"
},
{
"t": "set",
"p": "payload.lang",
"pt": "msg",
"to": "en",
"tot": "str"
},
{
"t": "set",
"p": "hour12",
"pt": "flow",
"to": "true",
"tot": "bool"
},
{
"t": "set",
"p": "units",
"pt": "flow",
"to": "payload.units",
"tot": "msg"
},
{
"t": "set",
"p": "lang",
"pt": "flow",
"to": "payload.lang",
"tot": "msg"
},
{
"t": "set",
"p": "payload.exclude",
"pt": "msg",
"to": "minutely",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 320,
"y": 180,
"wires": [
[
"681b2b94.9f7a44"
]
]
},
{
"id": "16181411.5a365c",
"type": "ui_ui_control",
"z": "5f633a28.607134",
"name": "Update tab",
"events": "all",
"x": 90,
"y": 300,
"wires": [
[
"3f3d5ab9.82b3a6"
]
]
},
{
"id": "c5efdf55.db32f",
"type": "link out",
"z": "5f633a28.607134",
"name": "Refresh",
"links": [
"a6b24cea.e52a7"
],
"x": 1115,
"y": 240,
"wires": []
},
{
"id": "a6b24cea.e52a7",
"type": "link in",
"z": "5f633a28.607134",
"name": "",
"links": [
"c5efdf55.db32f"
],
"x": 155,
"y": 240,
"wires": [
[
"79276db2.a2ce14"
]
]
},
{
"id": "3f3d5ab9.82b3a6",
"type": "switch",
"z": "5f633a28.607134",
"name": "tab focus",
"property": "tab",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 260,
"y": 300,
"wires": [
[
"79276db2.a2ce14"
]
]
},
{
"id": "70fb931.dfc146c",
"type": "ui_group",
"name": "MainGroup",
"tab": "4bb34f67.69a87",
"order": 1,
"disp": false,
"width": 10,
"collapse": false
},
{
"id": "4bb34f67.69a87",
"type": "ui_tab",
"name": "Weather",
"icon": "fa-thermometer-half",
"order": 3,
"disabled": false,
"hidden": false
}
]
@tsprivate
Copy link

tsprivate commented May 15, 2020

Great flow .. however it shows NaN error when 'metric' units are used.. can you adjust ?

@KanyonKris
Copy link
Author

Great flow .. however it shows NaN error when 'metric' units are used.. can you adjust ?

There was a bug affecting metric units. Fixed now. Thanks for the report.

@greendog99
Copy link

This is great, thanks! FYI, I changed the timeConvert() and dayName() functions to account for timezones. Before that, it was showing UTC times, around 7 hours ahead of my timezone (Denver).

var a = new Date((UNIX_timestamp + msg.payload.timezone_offset) * 1000);

@brocktucker
Copy link

Great starting point for exactly what I was trying to do. I made a version that keeps to the "standard" 6 wide format and also fixed the timezone_offset issue like @greendog99 above.

[{"id":"14aa820b.13759e","type":"tab","label":"Weather Forecast DB","disabled":false,"info":""},{"id":"436a7a6d.7fd094","type":"ui_button","z":"14aa820b.13759e","name":"IconRefresh","group":"39b889b4.225626","order":1,"width":"2","height":"2","passthru":false,"label":"","tooltip":"Refresh","color":"","bgcolor":"","icon":"wb_sunny","payload":"true","payloadType":"bool","topic":"","x":990,"y":260,"wires":[["a0cd9669.84b7b8"]]},{"id":"42ab669b.a1f428","type":"ui_text","z":"14aa820b.13759e","group":"39b889b4.225626","order":2,"width":"4","height":1,"name":"Description","label":"","format":"{{msg.payload.current.weather[0].description}}","layout":"row-center","x":990,"y":60,"wires":[]},{"id":"8a95e317.1f684","type":"ui_text","z":"14aa820b.13759e","group":"39b889b4.225626","order":4,"width":"2","height":1,"name":"Wind","label":"","format":"{{msg.payload.current.wind_speed}}<i class=\"wi wi-darksky-wind\"></i>&nbsp;&nbsp;{{msg.payload.current.wind_cardinal}}","layout":"col-center","x":970,"y":140,"wires":[]},{"id":"7e40c16d.7e00f","type":"ui_text","z":"14aa820b.13759e","group":"39b889b4.225626","order":5,"width":3,"height":1,"name":"SunriseTime","label":"","format":"<i class=\"wi wi-owm-01d\"></i>&nbsp;<i class=\"fa fa-arrow-up\"></i>&nbsp;&nbsp;{{msg.payload.current.sunrise}}","layout":"row-center","x":990,"y":180,"wires":[]},{"id":"ee785c35.6b901","type":"ui_text","z":"14aa820b.13759e","group":"39b889b4.225626","order":6,"width":3,"height":1,"name":"SunsetTime","label":"","format":"<i class=\"wi wi-wu-sunny\"></i>&nbsp;<i class=\"fa fa-arrow-down\"></i>&nbsp;&nbsp;{{msg.payload.current.sunset}}","layout":"row-center","x":990,"y":220,"wires":[]},{"id":"55b3a58b.c12e3c","type":"comment","z":"14aa820b.13759e","name":"OpenWeatherMap One Call API for Weather and Forecast","info":"","x":270,"y":40,"wires":[]},{"id":"7dfcf78c.f0aa88","type":"ui_template","z":"14aa820b.13759e","group":"39b889b4.225626","name":"Forecast2","order":7,"width":"6","height":"4","format":"<div style=\"height: 100%; justify-content: center; align-items: center;\">\n <div layout=\"rowicons\" layout-align=\"space-around start\" ng-repeat=\"data in msg.payload.rowicons\" style=\"font-size:150%;padding-top: 5px;padding-bottom: 5px\">\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell01}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell02}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell03}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell04}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell05}}\"></i></span>\n <span flex style=\"color: white;text-align: center\"><i class=\"wi wi-owm-{{data.cell06}}\"></i></span>\n </div>\n <div layout=\"rowtext\" layout-align=\"space-around start\" ng-repeat=\"data in msg.payload.rowtext\" style=\"line-height: 150%\">\n <span flex style=\"color: white;text-align: center;\">{{data.cell01}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell02}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell03}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell04}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell05}}</span>\n <span flex style=\"color: white;text-align: center\">{{data.cell06}}</span>\n </div>\n <div layout=\"rowicons\" layout-align=\"space-around start\" ng-repeat=\"data in msg.payload.rowicons\" style=\"font-size:150%;padding-top: 5px;padding-bottom: 5px\">\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell07}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell08}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell09}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell10}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell11}}\"></i></span>\n <span flex style=\"color: #097479;text-align: center\"><i class=\"wi wi-owm-{{data.cell12}}\"></i></span>\n </div>\n <div layout=\"rowtext\" layout-align=\"space-around start\" ng-repeat=\"data in msg.payload.rowtext\" style=\"line-height: 150%\">\n <span flex style=\"color: #097479;text-align: center\">{{data.cell07}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell08}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell09}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell10}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell11}}</span>\n <span flex style=\"color: #097479;text-align: center\">{{data.cell12}}</span>\n </div>\n</div>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":980,"y":320,"wires":[[]]},{"id":"203b7411.c828dc","type":"function","z":"14aa820b.13759e","name":"Format forecast data","func":"var fcdata = {};\nvar units=flow.get('units');\nif (units===undefined)\n{\n units=\"imperial\";\n}\n\nfunction formatTemp(high, low){\n if (units == \"imperial\") {\n if (low){\n temp = parseFloat(high).toFixed() + '/' + parseFloat(low).toFixed()\n }\n else {\n temp = parseFloat(high).toFixed() + '°F'\n }\n }\n else { // metric\n// temp = parseFloat(temp).toFixed(1) + '°C'\n if (low){\n temp = parseFloat(high).toFixed() + '/' + parseFloat(low).toFixed()\n }\n else {\n temp = parseFloat(high).toFixed() + '°C'\n }\n }\n return temp;\n}\nfunction dayName(unixTime){\n var d = new Date(unixTime * 1000);\n var weekday = new Array(7);\n weekday[0] = \"Sun\";\n weekday[1] = \"Mon\";\n weekday[2] = \"Tue\";\n weekday[3] = \"Wed\";\n weekday[4] = \"Thu\";\n weekday[5] = \"Fri\";\n weekday[6] = \"Sat\";\n\n return weekday[d.getDay()]\n}\nfunction timeConvert(UNIX_timestamp){\n UNIX_timestamp = UNIX_timestamp + msg.payload.timezone_offset;\n var a = new Date(UNIX_timestamp * 1000);\n var hour = a.getHours();\n var suffix = \" am\";\n if (hour >= 12) {\n hour = hour - 12;\n suffix = \" pm\";\n }\n if (hour === 0) {\n hour = 12;\n }\n var min = a.getMinutes();\n if (min < 10) {min = \"0\" + min;}\n// return hour + ':' + min + suffix;\n return hour + suffix;\n}\n// prepare forecast data for CSS based ui widget\n\nfcdata.payload = {\n rowtext: {\n \tdata01: {\n cell01: timeConvert(msg.payload.hourly[1].dt),\n cell02: timeConvert(msg.payload.hourly[2].dt),\n cell03: timeConvert(msg.payload.hourly[3].dt),\n cell04: timeConvert(msg.payload.hourly[4].dt),\n cell05: timeConvert(msg.payload.hourly[5].dt),\n cell06: timeConvert(msg.payload.hourly[6].dt),\n cell07: dayName(msg.payload.daily[1].dt),\n cell08: dayName(msg.payload.daily[2].dt),\n cell09: dayName(msg.payload.daily[3].dt),\n cell10: dayName(msg.payload.daily[4].dt),\n cell11: dayName(msg.payload.daily[5].dt),\n cell12: dayName(msg.payload.daily[6].dt),\n \t},\n \tdata02: {\n cell01: formatTemp(msg.payload.hourly[1].temp),\n cell02: formatTemp(msg.payload.hourly[2].temp),\n cell03: formatTemp(msg.payload.hourly[3].temp),\n cell04: formatTemp(msg.payload.hourly[4].temp),\n cell05: formatTemp(msg.payload.hourly[5].temp),\n cell06: formatTemp(msg.payload.hourly[6].temp),\n cell07: formatTemp(msg.payload.daily[1].temp.max, msg.payload.daily[0].temp.min),\n cell08: formatTemp(msg.payload.daily[2].temp.max, msg.payload.daily[1].temp.min),\n cell09: formatTemp(msg.payload.daily[3].temp.max, msg.payload.daily[2].temp.min),\n cell10: formatTemp(msg.payload.daily[4].temp.max, msg.payload.daily[3].temp.min),\n cell11: formatTemp(msg.payload.daily[5].temp.max, msg.payload.daily[4].temp.min),\n cell12: formatTemp(msg.payload.daily[6].temp.max, msg.payload.daily[5].temp.min),\n \t}\n },\n rowicons: {\n \tdata01: {\n \t\tcell01: msg.payload.hourly[1].weather[0].icon,\n \t\tcell02: msg.payload.hourly[2].weather[0].icon,\n \t\tcell03: msg.payload.hourly[3].weather[0].icon,\n \t\tcell04: msg.payload.hourly[4].weather[0].icon,\n \t\tcell05: msg.payload.hourly[5].weather[0].icon,\n \t\tcell06: msg.payload.hourly[6].weather[0].icon,\n \t\tcell07: msg.payload.daily[1].weather[0].icon,\n \t\tcell08: msg.payload.daily[2].weather[0].icon,\n \t\tcell09: msg.payload.daily[3].weather[0].icon,\n \t\tcell10: msg.payload.daily[4].weather[0].icon,\n \t\tcell11: msg.payload.daily[5].weather[0].icon,\n \t\tcell12: msg.payload.daily[6].weather[0].icon,\n \t}\n }\n}\n\nreturn fcdata;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":760,"y":320,"wires":[["7dfcf78c.f0aa88"]]},{"id":"ba5abfcf.7fcab","type":"ui_text","z":"14aa820b.13759e","group":"39b889b4.225626","order":3,"width":"2","height":1,"name":"Temperature","label":"","format":"<p style=\"font-size: 200%\">{{msg.payload.current.temp}}</p>","layout":"row-center","x":990,"y":100,"wires":[]},{"id":"7d3441cf.2e59d","type":"http request","z":"14aa820b.13759e","name":"Get OWM data","method":"GET","ret":"obj","paytoqs":true,"url":"https://api.openweathermap.org/data/2.5/onecall","tls":"","persist":false,"proxy":"","authType":"","x":480,"y":160,"wires":[["ebe09ce0.f4949","203b7411.c828dc"]]},{"id":"e6a4aa43.adcb28","type":"inject","z":"14aa820b.13759e","name":"Trigger","repeat":"600","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":120,"y":100,"wires":[["1715e035.8c8a3"]]},{"id":"ebe09ce0.f4949","type":"function","z":"14aa820b.13759e","name":"Format current data","func":"var icon = {};\n\nvar units=flow.get('units');\nif (units===undefined)\n{\n units=\"imperial\";\n}\n\nfunction timeConvert(UNIX_timestamp){\n UNIX_timestamp = UNIX_timestamp + msg.payload.timezone_offset;\n var a = new Date(UNIX_timestamp * 1000);\n var hour = a.getHours();\n var suffix = \" am\";\n if (hour >= 12) {\n hour = hour - 12;\n suffix = \" pm\";\n }\n if (hour === 0) {\n hour = 12;\n }\n var min = a.getMinutes();\n if (min < 10) {min = \"0\" + min;}\n return hour + ':' + min + suffix;\n}\n\nvar degreesToCardinal = function(deg){\nif (deg>11.25 && deg<=33.75){\nreturn \"NNE\";\n }else if (deg>33.75 && deg<56.25){\nreturn \"NE\";\n }else if (deg>56.25 && deg<78.75){\nreturn \"ENE\";\n }else if (deg>78.75 && deg<101.25){\nreturn \"E\";\n }else if (deg>101.25 && deg<123.75){\nreturn \"ESE\";\n }else if (deg>123.75 && deg<146.25){\nreturn \"SE\";\n }else if (deg>146.25 && deg<168.75){\nreturn \"SSE\";\n }else if (deg>168.75 && deg<191.25){\nreturn \"S\";\n }else if (deg>191.25 && deg<213.75){\nreturn \"SSW\";\n }else if (deg>213.75 && deg<236.25){\nreturn \"SW\";\n }else if (deg>236.25 && deg<258.75){\nreturn \"WSW\";\n }else if (deg>258.75 && deg<281.25){\nreturn \"W\";\n }else if (deg>281.25 && deg<303.75){\nreturn \"WNW\";\n }else if (deg>303.75 && deg<326.25){\nreturn \"NW\";\n }else if (deg>326.25 && deg<348.75){\nreturn \"NNW\";\n }else{\nreturn \"N\"; \n }\n}\n\nif (units == \"imperial\")\n{\n msg.payload.current.temp = msg.payload.current.temp.toFixed() + ' °F';\n msg.payload.current.wind_speed = msg.payload.current.wind_speed.toFixed() + ' mph';\n}\nelse // metric units\n{\n msg.payload.current.temp = msg.payload.current.temp.toFixed(1) + ' °C';\n msg.payload.current.wind_speed = msg.payload.current.wind_speed.toFixed(1) + ' m/s';\n}\n\nmsg.payload.current.wind_cardinal = degreesToCardinal(msg.payload.current.wind_deg);\nmsg.payload.current.sunrise = timeConvert(msg.payload.current.sunrise);\nmsg.payload.current.sunset = timeConvert(msg.payload.current.sunset);\n\nvar iconString = 'wi-owm-' + msg.payload.current.weather[0].icon + ' wi-4x';\nicon = {\n ui_control: {\n icon: iconString\n }\n}; \n\nreturn [msg, icon];","outputs":2,"noerr":0,"initialize":"","finalize":"","x":720,"y":160,"wires":[["ba5abfcf.7fcab","8a95e317.1f684","42ab669b.a1f428","7e40c16d.7e00f","ee785c35.6b901"],["436a7a6d.7fd094"]]},{"id":"1715e035.8c8a3","type":"change","z":"14aa820b.13759e","name":"Set location, appid, units","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.lat","pt":"msg","to":"28.106022","tot":"str"},{"t":"set","p":"payload.lon","pt":"msg","to":"-80.625024","tot":"str"},{"t":"set","p":"payload.appid","pt":"msg","to":"dfc6c3b831db8c482e72540e382d6485","tot":"str"},{"t":"set","p":"payload.units","pt":"msg","to":"imperial","tot":"str"},{"t":"set","p":"units","pt":"flow","to":"payload.units","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":100,"wires":[["7d3441cf.2e59d"]]},{"id":"834afeff.fcada","type":"ui_ui_control","z":"14aa820b.13759e","name":"Update tab","events":"all","x":110,"y":200,"wires":[["5939ef89.2e00e"]]},{"id":"a0cd9669.84b7b8","type":"link out","z":"14aa820b.13759e","name":"Refresh","links":["3db7aa3d.23eed6"],"x":1115,"y":260,"wires":[]},{"id":"3db7aa3d.23eed6","type":"link in","z":"14aa820b.13759e","name":"","links":["a0cd9669.84b7b8"],"x":155,"y":140,"wires":[["1715e035.8c8a3"]]},{"id":"5939ef89.2e00e","type":"switch","z":"14aa820b.13759e","name":"tab focus","property":"tab","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":280,"y":200,"wires":[["1715e035.8c8a3"]]},{"id":"39b889b4.225626","type":"ui_group","z":"","name":"Weather Forecast","tab":"b6ff010e.c244c","order":1,"disp":true,"width":"6","collapse":false},{"id":"b6ff010e.c244c","type":"ui_tab","z":"","name":"Home","icon":"home","order":1,"disabled":false,"hidden":false}]

@bricodx
Copy link

bricodx commented Nov 25, 2020

Hello, thank you for this flow.
On the node labeled "Set location, appid, units", it may be interesting to add a parameter: "lang". Thus the description of the weather is translated.

Capture2

Capture

@thomasvjohansen
Copy link

thomasvjohansen commented Dec 5, 2020

@bricodx, how did you get it to show 24h "time"? and french day names. I have added the "lang" option.

@bricodx
Copy link

bricodx commented Dec 5, 2020

@thomasvjohansen
to see the hours in 24h mode, you must modify the function timeConvert
timeconvert

and for the name of the days, you must modify the function dayName
dayname

@thomasvjohansen
Copy link

thx for the time solution, and meanwhile I found out I could rename the days. Thx

@thomasvjohansen
Copy link

thomasvjohansen commented Dec 5, 2020

This is my version :-)
mobile friendly in vertical layout
Udklip4

@bricodx
Copy link

bricodx commented Dec 5, 2020

this project is under construction at my place. And I also improve it for mobiles.
domo1

@GeorgyBenelli
Copy link

Снимок экрана 2021-01-05 в 08 05 03

Thanks a lot. This looks good.

@djiwondee
Copy link

djiwondee commented Jan 7, 2021

May be I'm wrong but I believe time for sunset/sunrise comes already in local time based on lan/lon parameter of the API
Fix to show right values is simple in the format Current data node:

function timeConvert(UNIX_timestamp){
 UNIX_timestamp = UNIX_timestamp + msg.payload.timezone_offset;
 var a = new Date(UNIX_timestamp * 1000);
 var hour = a.getHours();
 var suffix = " Uhr"; // set to " am" for imperial measure and uncomment the following lines
/* if (hour >= 12) {
 hour = hour - 12;
 suffix = " pm";
 }
 if (hour === 0) {
 hour = 12;
 } */
 var min = a.getMinutes();
 if (min < 10) {min = "0" + min;}
 return hour + ':' + min + suffix;
}

...

// suntise/sunset is already provided in local time by the API

msg.payload.current.sunrise = timeConvert(msg.payload.current.sunrise - msg.payload.timezone_offset);
msg.payload.current.sunset = timeConvert(msg.payload.current.sunset - msg.payload.timezone_offset);

@smcgann99
Copy link

smcgann99 commented Feb 14, 2021

weather

Modified to use SKYCONS ;-)

@xX-Nexus-Xx
Copy link

xX-Nexus-Xx commented Feb 14, 2021

@smcgann99 .. can you share your code ... it really looks good
image

code for compass (copied from flows.nodered.org)
[{"id":"f869934b.ecd4c","type":"change","z":"85e9250a.5aabf8","name":"","rules":[{"t":"set","p":"payload.speed","pt":"msg","to":"payload.current.wind_speed","tot":"msg"},{"t":"set","p":"payload.speed_unit","pt":"msg","to":"m/s","tot":"str"},{"t":"set","p":"payload.degrees","pt":"msg","to":"payload.current.wind_deg","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1420,"y":600,"wires":[["9656e32b.c76da"]]},{"id":"9656e32b.c76da","type":"ui_template","z":"85e9250a.5aabf8","group":"d584183c.9eff58","name":"Direction","order":5,"width":4,"height":4,"format":"<!--\npass msg.payload.degrees, msg.payload.speed, msg.payload.speed_unit\n-->\n<script>\n // Watch the incoming message and convert the degrees to a human readable compass direction\n (function(scope){\n scope.$watch('msg', function(msg) {\n if(typeof(msg) === \"object\"){\n // Convert the degrees to cardinal directions\n var deg = msg.payload.degrees;\n var dirs = [\"N\",\"NNE\",\"NE\",\"ENE\",\"E\",\"ESE\",\"SE\",\"SSE\",\"S\",\"SSW\",\"SW\",\"WSW\",\"W\",\"WNW\",\"NW\",\"NNW\",\"N\"];\n var idx = Math.round(deg*(dirs.length-1)/360);\n scope.direction = dirs[idx];\n \n \n // Keep track of the highest speed value, giving it 40 as a rough starting point.\n // Works okay for MPH and KTS, but KMH will show purple at a lower speed than the rest\n scope.max_val = (msg.payload.speed > scope.max_val ? msg.payload.speed : 100);\n \n scope.p_speed = msg.payload.speed;\n scope.p_max_val = scope.max_val;\n //var low_colors = [\n // '#FFFFFF', // white\n // '#d6f7ff', // light blue\n // '#85ffd2', // blue green\n // '#61ff6e' // green 10mph\n // ];\n var low_colors = [\n '#000FFF', // white\n '#0007ff', // light blue\n '#000fd2', // blue green\n '#000f6e' // green 10mph\n ]; \n \n var high_colors = [\n '#61ff6e', // green 10mph\n '#d5ff61', // green yellow\n '#fffc61', // yellow\n '#ffe561', // yellow orange 20mph\n '#ffcd61', // orange\n '#ffad61', // orange red\n '#ff7661', // red\n '#ff61dd', // red purple\n '#e261ff' // purple\n ];\n \n if(msg.payload.speed <= 10){\n scope.color = low_colors[Math.round(msg.payload.speed*(low_colors.length-1)/10)];\n }else{\n scope.color = high_colors[Math.round(msg.payload.speed*(high_colors.length-1) / scope.max_val)];\n }\n \n $(\".compass_container\").css(\"background-color\",scope.color);\n }\n \t});\n })(scope);\n \n // Hacks to improve the layout and make it scale\n $(document).ready(function(){\n setTimeout(function () {\n // Remove the auto scrolling from the parent node\n $(\".compass_container\").parent().css(\"overflow\",\"hidden\");\n \n // Adjust the color to match the theme base color by looking up the toolbar header background color;\n $(\".compass_container .triangle\").css(\"border-bottom-color\",$(\"md-toolbar\").css(\"background-color\"));\n \n // Scale the compass into the box that it's being rendered in\n // This CSS hack helps make sure the line & font size scales appropriately\n // Based on the node's grid size\n $(\".compass_container\").each(function(k,v){\n var scaleWidth = $(v).parent().width() / $(v).width();\n var scaleHeight = $(v).parent().height() / $(v).height();\n \n var translateX = ($(v).width() - $(v).parent().width()) / 2;\n var translateY = ($(v).height() - $(v).parent().height()) / 2;\n \n $(v).css(\"transform\",\"translate(-\"+translateX+\"px,-\"+translateY+\"px) scale(\"+scaleWidth+\",\"+scaleHeight+\") \");\n });\n \n $(\".compass_container\").css(\"display\",\"block\"); // Unhide it now that it's resized\n }, 1000);\n });\n</script>\n<style>\n @import url(https://fonts.googleapis.com/css?family=Dosis:200,400,500,600);\n \n .compass_container{\n transition: 1s ease-in-out;\n position: relative;\n display: none;\n width: 500px;\n height: 500px;\n border-radius: 100%;\n font-family: 'Dosis';\n font-size: 80px;\n box-shadow: inset 0px 0px 0px 30px #777; \n background-color: {{color}};\n }\n \n .compass_container .compass_header{\n font-weight: bold;\n position: absolute;\n text-align: center;\n width: 100%;\n font-size: 75%;\n top: -15px\n }\n .compass_container .text_container{\n height: 100%;\n width: 100%;\n padding: 0px;\n display: block;\n border-radius: 100%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n }\n \n .compass_container .arrow{\n transition: 1s ease-in-out;\n width: 100%;\n height: 100%;\n display: block;\n position: absolute;\n top: 0;\n }\n \n .compass_container .arrow .triangle{\n width: 0;\n\t\theight: 0;\n\t\tborder-left: 45px solid transparent;\n\t\tborder-right: 45px solid transparent;\n\t\tborder-bottom: 90px solid black;\n\t\tposition: absolute;\n\t\ttop: -15px;\n\t\tleft: 50%;\n\t\tmargin-left: -45px;\n\t\tz-index: 99;\n }\n</style>\n\n<div class=\"compass_container\" style=\"\">\n <div class=\"compass_header\">N</div>\n \n <div class=\"nr-dashboard-text text_container\">\n \n <div class=\"direction\" style=\"font-size: 120%\">{{direction}}</div>\n <div style=\"flex-direction: row; font-weight: bold;\">\n <span style=\"font-size: 100%\">{{msg.payload.speed}}</span>\n <span style=\"font-size: 75%\">{{msg.payload.speed_unit}}</span>\n </div>\n </div>\n <div class=\"arrow\" style=\"transform: rotate({{msg.payload.degrees}}deg);\">\n <div class=\"triangle\"></div>\n </div>\n</div>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":1640,"y":600,"wires":[[]]},{"id":"d584183c.9eff58","type":"ui_group","name":"MainGroup","tab":"9e238849.652318","order":1,"disp":true,"width":"15","collapse":true},{"id":"9e238849.652318","type":"ui_tab","name":"Home","icon":"fa-home","order":1,"disabled":false,"hidden":false}]

@KevinSzr
Copy link

great flow, but my refresh icon does not update. It shows two arrows instead of the weather icon. What should i do?

@KanyonKris
Copy link
Author

KanyonKris commented Apr 7, 2021

@greendog99 said:

This is great, thanks! FYI, I changed the timeConvert() and dayName() functions to account for timezones. Before that, it was showing UTC times, around 7 hours ahead of my timezone (Denver).

var a = new Date((UNIX_timestamp + msg.payload.timezone_offset) * 1000);

Creating a Date object is supposed to use local time (it does for me). Regardless I changed the code to use the timezone returned from OWM so this shouldn't be an issue anymore.

@KanyonKris
Copy link
Author

KanyonKris commented Apr 7, 2021

djiwondee said:

May be I'm wrong but I believe time for sunset/sunrise comes already in local time based on lan/lon parameter of the API
Fix to show right values is simple in the format Current data node:

function timeConvert(UNIX_timestamp){
 UNIX_timestamp = UNIX_timestamp + msg.payload.timezone_offset;
 var a = new Date(UNIX_timestamp * 1000);
 var hour = a.getHours();
 var suffix = " Uhr"; // set to " am" for imperial measure and uncomment the following lines
/* if (hour >= 12) {
 hour = hour - 12;
 suffix = " pm";
 }
 if (hour === 0) {
 hour = 12;
 } */
 var min = a.getMinutes();
 if (min < 10) {min = "0" + min;}
 return hour + ':' + min + suffix;
}

...

// suntise/sunset is already provided in local time by the API

msg.payload.current.sunrise = timeConvert(msg.payload.current.sunrise - msg.payload.timezone_offset);
msg.payload.current.sunset = timeConvert(msg.payload.current.sunset - msg.payload.timezone_offset);

The OpenWeatherMap API documentation says sunrise and sunset are returned in UTC - https://openweathermap.org/api/one-call-api#parameter

The Date object should use local time. Regardless I changed the code to use the timezone returned from OWM so this shouldn't be an issue anymore.

@KanyonKris
Copy link
Author

great flow, but my refresh icon does not update. It shows two arrows instead of the weather icon. What should i do?

Click the red Deploy button in the upper right corner of node red. Also reload the browser window.

@KanyonKris
Copy link
Author

KanyonKris commented Apr 7, 2021

Thanks for all the feedback. I made some changes:

  • Use timezone from OWM to compute times and dates instead of using Node Red server settings.
  • Added msg.payload.lang to support several languages. (If the short weekday names for the 4 day forecast do not match your language, you may need to upgrade to NodeJS 13 or higher when full international support was added for the toLocaleString function, or use the alternate code in the Format forecast data node.)
  • Added msg.payload.hour12 to support 12 or 24 hour time display.
  • Changed name of Set location, appid, units node to Settings.

@ST33LDI9ITAL
Copy link

4/25/2021, 1:34:56 AMnode: Format current datafunction : (error) "TypeError: Cannot read property 'temp' of undefined" 4/25/2021, 1:34:56 AMnode: Format forecast datafunction : (error) "TypeError: Cannot read property '1' of undefined" 4/25/2021, 1:35:25 AMnode: Format current datafunction : (error) "TypeError: Cannot read property 'temp' of undefined" 4/25/2021, 1:35:25 AMnode: Format forecast datafunction : (error) "TypeError: Cannot read property '1' of undefined"

@KanyonKris
Copy link
Author

4/25/2021, 1:34:56 AMnode: Format current datafunction : (error) "TypeError: Cannot read property 'temp' of undefined" 4/25/2021, 1:34:56 AMnode: Format forecast datafunction : (error) "TypeError: Cannot read property '1' of undefined" 4/25/2021, 1:35:25 AMnode: Format current datafunction : (error) "TypeError: Cannot read property 'temp' of undefined" 4/25/2021, 1:35:25 AMnode: Format forecast datafunction : (error) "TypeError: Cannot read property '1' of undefined"

You're not getting the proper data from OpenWeatherMap. Check that your AppID and other entries are entered correctly in the Settings node.

If you're still having problems, add a Debug node after the Get OWM data node. You should get an object that looks like output documented here - https://openweathermap.org/api/one-call-api

@Jacksonbm1
Copy link

Hello, I received an email from the open weather team marked "OpenWeather One Call service usage alert"

Thank you for choosing OpenWeather!

We have noticed that your account is currently using One Call API quite intensively and exceed the account limit of 1000 calls per day.

We would be happy to discuss migration to appropriate subscription for you.
What are your concrete requirements? How many calls are you planning to issue? What kind of data is needed?

Do you know of a way to stop this?

Here's a screenshot of my usage: https://drive.google.com/file/d/17NCx0AICy0qOoQkwYMXgKnVqCwP8hBL0/view?usp=sharing

Any way to stop this? I don't want to abuse the service? I see we have it set to minutely, and I'm wondering if we can set it to every 2 minutes?

@steifhahn
Copy link

steifhahn commented Jul 10, 2021

djiwondee said:

May be I'm wrong but I believe time for sunset/sunrise comes already in local time based on lan/lon parameter of the API
Fix to show right values is simple in the format Current data node:

function timeConvert(UNIX_timestamp){
 UNIX_timestamp = UNIX_timestamp + msg.payload.timezone_offset;
 var a = new Date(UNIX_timestamp * 1000);
 var hour = a.getHours();
 var suffix = " Uhr"; // set to " am" for imperial measure and uncomment the following lines
/* if (hour >= 12) {
 hour = hour - 12;
 suffix = " pm";
 }
 if (hour === 0) {
 hour = 12;
 } */
 var min = a.getMinutes();
 if (min < 10) {min = "0" + min;}
 return hour + ':' + min + suffix;
}

...

// suntise/sunset is already provided in local time by the API

msg.payload.current.sunrise = timeConvert(msg.payload.current.sunrise - msg.payload.timezone_offset);
msg.payload.current.sunset = timeConvert(msg.payload.current.sunset - msg.payload.timezone_offset);

The OpenWeatherMap API documentation says sunrise and sunset are returned in UTC - https://openweathermap.org/api/one-call-api#parameter

The Date object should use local time. Regardless I changed the code to use the timezone returned from OWM so this shouldn't be an issue anymore.

Time formatting is still an issue for me. Have to add greendog99 solution.
Because sunset and sunrise is still in UTC format.

@HeneryH
Copy link

HeneryH commented Jun 10, 2022

The hourly icons are not visible in the Light theme because they are white on white.

Can the "white" be changed to a not-theme background or something like that the same way the text is colored?

w2

w1

@Strunck
Copy link

Strunck commented Jul 18, 2022

In the function blocks "Format current data" and "Format forecast data", I found an error in the time conversion.
In the functions timeConvert and dayName the call return dateObject.toLocaleString is done with arguments. The second argument is an object with some properties. The property timezone needs to be spelled like this timeZone

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment