# Weather widget in Dashboard 2

**URL:** <https://discourse.nodered.org/t/weather-widget-in-dashboard-2/95729>\
**Category:** Dashboard\
**Tags:** dashboard-2\
**Created:** [1 March 2025 13:07 UTC](https://discourse.nodered.org/t/weather-widget-in-dashboard-2/95729 "2025-03-01T13:07:04Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![maxuser74](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/maxuser74/32/70109_2.png) [@maxuser74](https://discourse.nodered.org/u/maxuser74)\
**Post date:** [1 March 2025 13:07 UTC](https://discourse.nodered.org/t/weather-widget-in-dashboard-2/95729/1 "2025-03-01T13:07:04Z")

</div>

Hello guys, I've just finished a simple but functional weather forecast card in Dashboard 2 using Open Meteo API, specifically designed to be nice on mobile.

> **[🌦️ Docs | Open-Meteo.com](https://open-meteo.com/en/docs)**
>
> Weather Forecast APIs with weather models from multiple national weather providers, combining the best models for accurate forecasts worldwide. Explore the API documentation to learn more about the available weather models, their origin countries,...

The code is very basic, no message validation or optimization, most of it is hardcoded based on the JSON of the response, so be careful if you modify the Http request, it will likely break something.

 ![IMG_0914](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/0/a/0ad3b379c40a17c1211d8f98cf8f534b6d57051f.jpeg)

City name is hardcoded here in Template and there is no direct connection to latitude and longitude:

```auto
    return {
      city: "Madrid"
    };

```

Days name are in this array, in my case in Italian:

```auto
    const days = ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"];

```

you can easily switch to English or whatever:

```auto
    const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];

```

Latitude and Longitude must be changed in Api Http request body.

> [https://api.open-meteo.com/v1/forecast?latitude=40.416775&longitude=-3.703790&current=temperature\_2m,weather\_code&daily=weather\_code,temperature\_2m\_max,temperature\_2m\_min,precipitation\_probability\_max&timezone=GMT](https://api.open-meteo.com/v1/forecast?latitude=40.416775&longitude=-3.703790&current=temperature_2m,weather_code&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max&timezone=GMT)

Nothing special, code attached here:

```auto
[
    {
        "id": "abe58356fe41212b",
        "type": "http request",
        "z": "e756ecd6788c47e1",
        "name": "Madrid",
        "method": "GET",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "https://api.open-meteo.com/v1/forecast?latitude=40.416775&longitude=-3.703790&current=temperature_2m,weather_code&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max&timezone=GMT",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [],
        "x": 340,
        "y": 660,
        "wires": [
            [
                "e48e9e6375dc7dba"
            ]
        ]
    },
    {
        "id": "e48e9e6375dc7dba",
        "type": "json",
        "z": "e756ecd6788c47e1",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 490,
        "y": 660,
        "wires": [
            [
                "769b552931a52149"
            ]
        ]
    },
    {
        "id": "769b552931a52149",
        "type": "ui-template",
        "z": "e756ecd6788c47e1",
        "group": "925398646582192e",
        "page": "",
        "ui": "",
        "name": "Madrid",
        "order": 1,
        "width": "12",
        "height": "4",
        "head": "",
        "format": "<style>\n\n.hv_centered{\ndisplay: flex;\njustify-content: center;\nalign-items: center;\nheight:100%\n}\n\n.container {\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n grid-template-rows: 0.6fr 1.4fr 0.4fr 0.3fr 1fr 0.5fr;\n gap: 2px;\n grid-template-areas:\n \"city city city city city\"\n \"temp temp temp curr-weather-icon curr-weather-icon\"\n \"day-name-1 day-name-2 day-name-3 day-name-4 day-name-5\"\n \"prec1 prec2 prec3 prec4 prec5\"\n \"weathericon1 weathericon2 weathericon3 weathericon4 weathericon5\"\n \"minmax1 minmax2 minmax3 minmax4 minmax5\";\n}\n\n.city { \n grid-area: city; \n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: 1.5rem;\n color: #5DADE2;\n font-weight: 500;\n\n }\n.temp { grid-area: temp; \n font-size: 2.7rem;\n padding-left: 10px;\n }\n.curr-weather-icon { grid-area: curr-weather-icon; \n font-size: 3rem;\n display: flex;\n justify-content: right;\n padding-right: 30px;\n }\n\n.day_text{\nfont-size: 1.2rem;\n}\n\n.weather_icon{\nfont-size: 2.5rem;\n\n}\n\n.temp_text{\n font-size: 1rem;\n display: flex;\n // justify-content: center;\n align-items: center;\n}\n\n.day-name-1 { grid-area: day-name-1; }\n.day-name-2 { grid-area: day-name-2; }\n.day-name-3 { grid-area: day-name-3; }\n.day-name-4 { grid-area: day-name-4; }\n.day-name-5 { grid-area: day-name-5; }\n.prec1 { grid-area: prec1; }\n.prec2 { grid-area: prec2; }\n.prec3 { grid-area: prec3; }\n.prec4 { grid-area: prec4; }\n.prec5 { grid-area: prec5; }\n.weathericon1 { grid-area: weathericon1}\n.weathericon2 { grid-area: weathericon2}\n.weathericon3 { grid-area: weathericon3}\n.weathericon4 { grid-area: weathericon4}\n.weathericon5 { grid-area: weathericon5}\n.minmax1 { grid-area: minmax1; }\n.minmax2 { grid-area: minmax2; }\n.minmax3 { grid-area: minmax3; }\n.minmax4 { grid-area: minmax4; }\n.minmax5 { grid-area: minmax5; }\n</style>\n\n<template>\n <div class=\"container\">\n <div class=\"city\">{{ city }}</div>\n <div class=\"temp\">{{ curr_temp }}°C</div>\n <div class=\"curr-weather-icon\"> {{curr_weather_code}}</div>\n <div class=\"day-name-1 hv_centered day_text\">{{ today }}</div>\n <div class=\"day-name-2 hv_centered day_text\">{{ today_plus1 }}</div>\n <div class=\"day-name-3 hv_centered day_text\">{{ today_plus2 }}</div>\n <div class=\"day-name-4 hv_centered day_text\">{{ today_plus3 }}</div>\n <div class=\"day-name-5 hv_centered day_text\">{{ today_plus4 }}</div>\n <div class=\"prec1 hv_centered\">{{prec0}}</div>\n <div class=\"prec2 hv_centered\">{{prec1}}</div>\n <div class=\"prec3 hv_centered\">{{prec2}}</div>\n <div class=\"prec4 hv_centered\">{{prec3}}</div>\n <div class=\"prec5 hv_centered\">{{prec4}}</div>\n <div class=\"weathericon1 hv_centered weather_icon\">{{ icon0 }}</div>\n <div class=\"weathericon2 hv_centered weather_icon\">{{ icon1 }}</div>\n <div class=\"weathericon3 hv_centered weather_icon\">{{ icon2 }}</div>\n <div class=\"weathericon4 hv_centered weather_icon\">{{ icon3 }}</div>\n <div class=\"weathericon5 hv_centered weather_icon\">{{ icon4 }}</div>\n <div class=\"minmax1 hv_centered temp_text\">{{minmax0}}</div>\n <div class=\"minmax2 hv_centered temp_text\">{{minmax1}}</div>\n <div class=\"minmax3 hv_centered temp_text\">{{minmax2}}</div>\n <div class=\"minmax4 hv_centered temp_text\">{{minmax3}}</div>\n <div class=\"minmax5 hv_centered temp_text\">{{minmax4}}</div>\n </div>\n</template>\n\n<script>\nexport default {\n data() {\n return {\n city: \"Madrid\",\n curr_temp: 10,\n curr_weather_code: 0,\n today: \"\",\n tomorrow: \"\",\n today_plus2: \"\",\n today_plus3: \"\",\n today_plus4: \"\",\n minmax0: '0/0',\n minmax1: '0/0',\n minmax2: '0/0',\n minmax3: '0/0',\n minmax4: '0/0',\n icon0: 0,\n icon1: 1,\n icon2: 2,\n icon3: 3,\n icon4: 4,\n prec0: 0,\n prec1: 0,\n prec2: 0,\n prec3: 0,\n prec4: 0\n };\n },\n watch: {\n msg: function () {\n // if (newMsg.payload && newMsg.payload.daily && newMsg.payload.daily.time) {\n this.curr_temp = this.msg.payload.current.temperature_2m\n\n this.curr_weather_code = this.getWeatherIcon(this.msg.payload.current.weather_code)\n\n this.today = this.getdayname(this.msg.payload.daily.time[0]);\n this.today_plus1 = this.getdayname(this.msg.payload.daily.time[1]);\n this.today_plus2 = this.getdayname(this.msg.payload.daily.time[2]);\n this.today_plus3 = this.getdayname(this.msg.payload.daily.time[3]);\n this.today_plus4 = this.getdayname(this.msg.payload.daily.time[4]);\n this.minmax0 = Math.round(this.msg.payload.daily.temperature_2m_min[0]) + \n '/' + Math.round(this.msg.payload.daily.temperature_2m_max[0]);\n\n this.prec0 = this.msg.payload.daily.precipitation_probability_max[0] + ' %'\n this.prec1 = this.msg.payload.daily.precipitation_probability_max[1] + ' %'\n this.prec2 = this.msg.payload.daily.precipitation_probability_max[2] + ' %'\n this.prec3 = this.msg.payload.daily.precipitation_probability_max[3] + ' %'\n this.prec4 = this.msg.payload.daily.precipitation_probability_max[4] + ' %'\n\n this.minmax1 = Math.round(this.msg.payload.daily.temperature_2m_min[1]) +\n '/' + Math.round(this.msg.payload.daily.temperature_2m_max[1]);\n\n this.minmax2 = Math.round(this.msg.payload.daily.temperature_2m_min[2]) +\n '/' + Math.round(this.msg.payload.daily.temperature_2m_max[2]);\n\n this.minmax3 = Math.round(this.msg.payload.daily.temperature_2m_min[3]) +\n '/' + Math.round(this.msg.payload.daily.temperature_2m_max[3]);\n\n this.minmax4 = Math.round(this.msg.payload.daily.temperature_2m_min[4]) +\n '/' + Math.round(this.msg.payload.daily.temperature_2m_max[4]);\n\n this.minmax5 = Math.round(this.msg.payload.daily.temperature_2m_min[5]) +\n '/' + Math.round(this.msg.payload.daily.temperature_2m_max[5]);\n\n this.icon0 = this.getWeatherIcon(this.msg.payload.daily.weather_code[0])\n this.icon1 = this.getWeatherIcon(this.msg.payload.daily.weather_code[1])\n this.icon2 = this.getWeatherIcon(this.msg.payload.daily.weather_code[2])\n this.icon3 = this.getWeatherIcon(this.msg.payload.daily.weather_code[3])\n this.icon4 = this.getWeatherIcon(this.msg.payload.daily.weather_code[4])\n\n\n }\n //}\n },\n methods: {\n getdayname: function (time_stamp) {\n let day = new Date(time_stamp);\n const days = [\"Dom\", \"Lun\", \"Mar\", \"Mer\", \"Gio\", \"Ven\", \"Sab\"];\n return days[day.getDay()];\n },\n\n getWeatherIcon: function (code) {\n if (code === 0) {\n return '☀️';\n } else if (code === 1) {\n return '🌤️';\n } else if (code === 2) {\n return '⛅';\n } else if (code === 3) {\n return '☁️';\n } else if ([45, 48].includes(code)) {\n return '🌫️';\n } else if ([51, 53, 55].includes(code)) {\n return '🌦️';\n } else if ([56, 57].includes(code)) {\n return '🌧️';\n } else if ([61, 63, 65].includes(code)) {\n return '🌧️';\n } else if ([66, 67].includes(code)) {\n return '🌨️';\n } else if ([71, 73, 75, 77].includes(code)) {\n return '❄️';\n } else if ([80, 81, 82].includes(code)) {\n return '🌦️';\n } else if ([85, 86].includes(code)) {\n return '❄️';\n } else if (code === 95) {\n return '⛈️';\n } else if ([96, 99].includes(code)) {\n return '⛈️';\n } else {\n return '❓';\n }\n }\n\n },\n mounted() {\n console.log(\"Component mounted\");\n },\n unmounted() {\n console.log(\"Component removed\");\n }\n};\n</script>\n",
        "storeOutMessages": true,
        "passthru": false,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 640,
        "y": 660,
        "wires": [
            []
        ]
    },
    {
        "id": "1dd137a565e1a50c",
        "type": "inject",
        "z": "e756ecd6788c47e1",
        "name": "Test",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "600",
        "crontab": "",
        "once": true,
        "onceDelay": "1",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 110,
        "y": 600,
        "wires": [
            [
                "d799f7fd7c6bca7b",
                "abe58356fe41212b"
            ]
        ]
    },
    {
        "id": "925398646582192e",
        "type": "ui-group",
        "name": "Madrid",
        "page": "bf101ed8a33290dc",
        "width": 6,
        "height": 1,
        "order": 1,
        "showTitle": false,
        "className": "",
        "visible": "true",
        "disabled": "false",
        "groupType": "default"
    },
    {
        "id": "bf101ed8a33290dc",
        "type": "ui-page",
        "name": "Page Name",
        "ui": "43e57a187b2ebc1f",
        "path": "/page4",
        "icon": "home",
        "layout": "grid",
        "theme": "f019925b84983e76",
        "breakpoints": [
            {
                "name": "Default",
                "px": "0",
                "cols": "3"
            },
            {
                "name": "Tablet",
                "px": "576",
                "cols": "6"
            },
            {
                "name": "Small Desktop",
                "px": "768",
                "cols": "9"
            },
            {
                "name": "Desktop",
                "px": "1024",
                "cols": "12"
            }
        ],
        "order": 1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "43e57a187b2ebc1f",
        "type": "ui-base",
        "name": "Max Passeri Dashboard",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "showPageTitle": true,
        "navigationStyle": "temporary",
        "titleBarStyle": "fixed",
        "showReconnectNotification": true,
        "notificationDisplayTime": "5",
        "showDisconnectNotification": true
    },
    {
        "id": "f019925b84983e76",
        "type": "ui-theme",
        "name": "Dark",
        "colors": {
            "surface": "#303030",
            "primary": "#0094ce",
            "bgPage": "#303030",
            "groupBg": "#303030",
            "groupOutline": "#545454"
        },
        "sizes": {
            "density": "default",
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "10px",
            "widgetGap": "12px"
        }
    }
]

```

Hope useful !

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [1 March 2025 14:25 UTC](https://discourse.nodered.org/t/weather-widget-in-dashboard-2/95729/2 "2025-03-01T14:25:24Z")

</div>

Well done. Easy enough to understand and modify if needed.  
It just misses solution(s) for error cases. For sure such will hapen time to time.

---

<div class="post-metadata">

**Author:** ![xx\_Nexus\_xx](https://avatars.discourse-cdn.com/v4/letter/x/54ee81/32.png) [@xx\_Nexus\_xx](https://discourse.nodered.org/u/xx_Nexus_xx)\
**Post date:** [3 March 2025 03:05 UTC](https://discourse.nodered.org/t/weather-widget-in-dashboard-2/95729/3 "2025-03-03T03:05:03Z")

</div>

👍 I'm using Open-Meteo as well for a while

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/1/c/1ccdc39e5ec4adb610d2f5c56efc27ca69b0afa0.png)

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [3 March 2025 07:57 UTC](https://discourse.nodered.org/t/weather-widget-in-dashboard-2/95729/4 "2025-03-03T07:57:54Z")

</div>

I'm also sharing a picture of my weather card, mainly as a source of inspiration. It's also based on open-meteo data. chartjs is used to create the temperature curve. CSS is pretty heavily overridden to ensure a consistent user experience on both large and small screens. Since this is a custom job and made for a specific use, I won't share the solution as a whole, but I can share advice on how to achieve something similar. But the prerequisite for this is a clear vision.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/c/0/c0e32235ace3b6b707ab38636772314317f8eefb.jpeg)

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [2 April 2025 07:58 UTC](https://discourse.nodered.org/t/weather-widget-in-dashboard-2/95729/5 "2025-04-02T07:58:25Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
