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 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 "Set location, appid, units" 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.

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

image

[{"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":260,"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":60,"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":140,"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":180,"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":220,"wires":[]},{"id":"d284ed2f.c15c8","type":"comment","z":"5f633a28.607134","name":"OpenWeatherMap One Call API for Weather and Forecast","info":"","x":270,"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":320,"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//        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  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  \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,"x":760,"y":320,"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":100,"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":480,"y":160,"wires":[["67075190.fc6f3","7adde26c.514aec"]]},{"id":"c72d96f0.204148","type":"inject","z":"5f633a28.607134","name":"Trigger","topic":"","payload":"true","payloadType":"bool","repeat":"600","crontab":"","once":true,"onceDelay":0.1,"x":120,"y":100,"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)\n{\n  units=\"imperial\";\n}\n\nfunction timeConvert(UNIX_timestamp){\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,"x":720,"y":160,"wires":[["d8c5527f.7739","ec4eb2d0.974c","9c052db7.3c434","40f44662.3ecde8","5b97953.01fdd6c"],["180a101a.9cd43"]]},{"id":"79276db2.a2ce14","type":"change","z":"5f633a28.607134","name":"Set location, appid, units","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":"{your app id}","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":[["681b2b94.9f7a44"]]},{"id":"16181411.5a365c","type":"ui_ui_control","z":"5f633a28.607134","name":"Update tab","events":"all","x":110,"y":200,"wires":[["3f3d5ab9.82b3a6"]]},{"id":"c5efdf55.db32f","type":"link out","z":"5f633a28.607134","name":"Refresh","links":["a6b24cea.e52a7"],"x":1115,"y":260,"wires":[]},{"id":"a6b24cea.e52a7","type":"link in","z":"5f633a28.607134","name":"","links":["c5efdf55.db32f"],"x":155,"y":140,"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":280,"y":200,"wires":[["79276db2.a2ce14"]]},{"id":"70fb931.dfc146c","type":"ui_group","z":"","name":"MainGroup","tab":"4bb34f67.69a87","order":1,"disp":false,"width":10,"collapse":false},{"id":"4bb34f67.69a87","type":"ui_tab","z":"","name":"Weather","icon":"fa-thermometer-half","order":3,"disabled":false,"hidden":false}]

Collection Info

prev

Flow Info

Created 10 months, 2 weeks ago
Updated 5 months, 2 weeks ago
Rating: not yet rated

Owner

Node Types

Core
  • change (x1)
  • comment (x1)
  • function (x2)
  • http request (x1)
  • inject (x1)
  • link in (x1)
  • link out (x1)
  • switch (x1)
  • tab (x1)
Other

Tags

  • weather
  • dashboard
  • ui
  • openweathermap
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option