Skip to content

Instantly share code, notes, and snippets.

@KanyonKris
Last active April 8, 2021 02:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KanyonKris/e8176d72ac8af8fbb9fde4adb4008317 to your computer and use it in GitHub Desktop.
Save KanyonKris/e8176d72ac8af8fbb9fde4adb4008317 to your computer and use it in GitHub Desktop.
OpenWeatherMap Weather Dashboard with Icons - Portrait

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 6 days. The mix of text and icons creates a compact and attractive display.

This version is formatted in a portrait orientation. A landscape 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 it doesn't use 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": "f74c934.b1f607",
"type": "tab",
"label": "OWM Portrait",
"disabled": false,
"info": ""
},
{
"id": "976d517.6a183b",
"type": "change",
"z": "f74c934.b1f607",
"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": "metric",
"tot": "str"
},
{
"t": "set",
"p": "payload.lang",
"pt": "msg",
"to": "en",
"tot": "str"
},
{
"t": "set",
"p": "hour12",
"pt": "flow",
"to": "false",
"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": 280,
"y": 160,
"wires": [
[
"b1ca106b.5c71d"
]
]
},
{
"id": "b1ca106b.5c71d",
"type": "http request",
"z": "f74c934.b1f607",
"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": 460,
"y": 160,
"wires": [
[
"db5d66a9.2b0838",
"f92d0eac.0841b"
]
]
},
{
"id": "edb282aa.2e84c",
"type": "inject",
"z": "f74c934.b1f607",
"name": "Trigger",
"repeat": "600",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "true",
"payloadType": "bool",
"x": 100,
"y": 160,
"wires": [
[
"976d517.6a183b"
]
]
},
{
"id": "fbae9b4f.b0a078",
"type": "link in",
"z": "f74c934.b1f607",
"name": "",
"links": [
"a4476346.61958"
],
"x": 115,
"y": 220,
"wires": [
[
"976d517.6a183b"
]
]
},
{
"id": "747088c5.64c398",
"type": "switch",
"z": "f74c934.b1f607",
"name": "tab focus",
"property": "tab",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 260,
"y": 280,
"wires": [
[
"976d517.6a183b"
]
]
},
{
"id": "db5d66a9.2b0838",
"type": "function",
"z": "f74c934.b1f607",
"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 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": 700,
"y": 160,
"wires": [
[
"4200040.ae40ffc",
"a8a68dfc.a928",
"1467b772.ae71f9",
"73ad239f.64e1bc",
"5a05012f.ef0f9"
],
[
"d7e66d60.81ae7"
]
]
},
{
"id": "f92d0eac.0841b",
"type": "function",
"z": "f74c934.b1f607",
"name": "Format forecast data",
"func": "var 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() + '°F'\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() + '°C'\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\nfor (i=1;i<7;i++) {\n msg.payload.hourly[i].time = timeConvert(msg.payload.hourly[i].dt);\n msg.payload.hourly[i].temperature = formatTemp(msg.payload.hourly[i].temp);\n msg.payload.daily[i].day = dayName(msg.payload.daily[i].dt);\n msg.payload.daily[i].hilo = formatTemp(msg.payload.daily[i].temp.max, msg.payload.daily[i-1].temp.min);\n}\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 700,
"y": 540,
"wires": [
[
"5be6a7ff.ff8f78",
"33210b7f.0feed4",
"188dfee1.7c3e11",
"cecfedca.f74b1",
"c843a03c.3b6c6",
"da3b0fb9.94476",
"c781fdac.2b344",
"77ad26d4.b80728",
"6e9c7bbd.f35514",
"6e6f86ef.4f3e78",
"27156e50.a61182",
"2050e0d3.3408a"
]
]
},
{
"id": "49577c9f.fd0214",
"type": "ui_ui_control",
"z": "f74c934.b1f607",
"name": "Update tab",
"events": "all",
"x": 90,
"y": 280,
"wires": [
[
"747088c5.64c398"
]
]
},
{
"id": "4200040.ae40ffc",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 3,
"width": 4,
"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": 950,
"y": 100,
"wires": []
},
{
"id": "a8a68dfc.a928",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 4,
"width": 6,
"height": 1,
"name": "Description",
"label": "",
"format": "{{msg.payload.current.weather[0].description}}",
"layout": "row-center",
"x": 970,
"y": 140,
"wires": []
},
{
"id": "1467b772.ae71f9",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"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": 970,
"y": 180,
"wires": []
},
{
"id": "73ad239f.64e1bc",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"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": 970,
"y": 220,
"wires": []
},
{
"id": "d7e66d60.81ae7",
"type": "ui_button",
"z": "f74c934.b1f607",
"name": "IconRefresh",
"group": "699b821c.069cdc",
"order": 1,
"width": 2,
"height": 2,
"passthru": false,
"label": "",
"tooltip": "Refresh",
"color": "",
"bgcolor": "#333333",
"icon": "fa-refresh fa-4x",
"payload": "true",
"payloadType": "bool",
"topic": "",
"topicType": "str",
"x": 970,
"y": 260,
"wires": [
[
"a4476346.61958"
]
]
},
{
"id": "5be6a7ff.ff8f78",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 7,
"width": 3,
"height": 1,
"name": "Hour1",
"label": "",
"format": "<font color=#0EB8C0>{{msg.payload.hourly[1].time}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.hourly[1].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.hourly[1].temperature}}</font>",
"layout": "row-center",
"x": 950,
"y": 320,
"wires": []
},
{
"id": "33210b7f.0feed4",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 8,
"width": 3,
"height": 1,
"name": "Day1",
"label": "",
"format": "<font color=#3393FF>{{msg.payload.daily[1].day}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.daily[1].weather[0].icon}}\"></i>&nbsp;{{msg.payload.daily[1].hilo}}</font>",
"layout": "row-center",
"x": 950,
"y": 580,
"wires": []
},
{
"id": "188dfee1.7c3e11",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 9,
"width": 3,
"height": 1,
"name": "Hour2",
"label": "",
"format": "<font color=#0EB8C0>{{msg.payload.hourly[2].time}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.hourly[2].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.hourly[2].temperature}}</font>",
"layout": "row-center",
"x": 950,
"y": 360,
"wires": []
},
{
"id": "cecfedca.f74b1",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 11,
"width": 3,
"height": 1,
"name": "Hour3",
"label": "",
"format": "<font color=#0EB8C0>{{msg.payload.hourly[3].time}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.hourly[3].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.hourly[3].temperature}}</font>",
"layout": "row-center",
"x": 950,
"y": 400,
"wires": []
},
{
"id": "c843a03c.3b6c6",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 13,
"width": 3,
"height": 1,
"name": "Hour4",
"label": "",
"format": "<font color=#0EB8C0>{{msg.payload.hourly[4].time}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.hourly[4].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.hourly[4].temperature}}</font>",
"layout": "row-center",
"x": 950,
"y": 440,
"wires": []
},
{
"id": "da3b0fb9.94476",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 15,
"width": 3,
"height": 1,
"name": "Hour5",
"label": "",
"format": "<font color=#0EB8C0>{{msg.payload.hourly[5].time}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.hourly[5].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.hourly[5].temperature}}</font>",
"layout": "row-center",
"x": 950,
"y": 480,
"wires": []
},
{
"id": "c781fdac.2b344",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 17,
"width": 3,
"height": 1,
"name": "Hour6",
"label": "",
"format": "<font color=#0EB8C0>{{msg.payload.hourly[6].time}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.hourly[6].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.hourly[6].temperature}}</font>",
"layout": "row-center",
"x": 950,
"y": 520,
"wires": []
},
{
"id": "77ad26d4.b80728",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 10,
"width": 3,
"height": 1,
"name": "Day2",
"label": "",
"format": "<font color=#3393FF>{{msg.payload.daily[2].day}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.daily[2].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.daily[2].hilo}}</font>",
"layout": "row-center",
"x": 950,
"y": 620,
"wires": []
},
{
"id": "6e9c7bbd.f35514",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 12,
"width": 3,
"height": 1,
"name": "Day3",
"label": "",
"format": "<font color=#3393FF>{{msg.payload.daily[3].day}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.daily[3].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.daily[3].hilo}}</font>",
"layout": "row-center",
"x": 950,
"y": 660,
"wires": []
},
{
"id": "6e6f86ef.4f3e78",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 14,
"width": 3,
"height": 1,
"name": "Day4",
"label": "",
"format": "<font color=#3393FF>{{msg.payload.daily[4].day}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.daily[4].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.daily[4].hilo}}</font>",
"layout": "row-center",
"x": 950,
"y": 700,
"wires": []
},
{
"id": "27156e50.a61182",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 16,
"width": 3,
"height": 1,
"name": "Day5",
"label": "",
"format": "<font color=#3393FF>{{msg.payload.daily[5].day}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.daily[5].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.daily[5].hilo}}</font>",
"layout": "row-center",
"x": 950,
"y": 740,
"wires": []
},
{
"id": "2050e0d3.3408a",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 18,
"width": 3,
"height": 1,
"name": "Day6",
"label": "",
"format": "<font color=#3393FF>{{msg.payload.daily[6].day}}&nbsp;&nbsp;<i class=\"wi wi-owm-{{msg.payload.daily[6].weather[0].icon}}\"></i>&nbsp;&nbsp;{{msg.payload.daily[6].hilo}}</font>",
"layout": "row-center",
"x": 950,
"y": 780,
"wires": []
},
{
"id": "a4476346.61958",
"type": "link out",
"z": "f74c934.b1f607",
"name": "Refresh",
"links": [
"fbae9b4f.b0a078"
],
"x": 1095,
"y": 260,
"wires": []
},
{
"id": "d608dfbd.8a2a9",
"type": "comment",
"z": "f74c934.b1f607",
"name": "OpenWeatherMap One Call API for Weather and Forecast",
"info": "",
"x": 230,
"y": 40,
"wires": []
},
{
"id": "5a05012f.ef0f9",
"type": "ui_text",
"z": "f74c934.b1f607",
"group": "699b821c.069cdc",
"order": 2,
"width": 4,
"height": 1,
"name": "Temperature",
"label": "",
"format": "{{msg.payload.current.temp}}",
"layout": "col-center",
"x": 970,
"y": 60,
"wires": []
},
{
"id": "699b821c.069cdc",
"type": "ui_group",
"name": "Portrait Group",
"tab": "b374715c.6fcb6",
"order": 1,
"disp": false,
"width": "6",
"collapse": false
},
{
"id": "b374715c.6fcb6",
"type": "ui_tab",
"name": "Weather Portrait",
"icon": "fa-umbrella",
"order": 2,
"disabled": false,
"hidden": false
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment