And second the weather widget itself
[{"id":"df06a118d9fd4c82","type":"group","z":"6babbb5b5eab71f6","style":{"stroke":"#0722fc","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#0722fc"},"nodes":["fc1e310ae95a96ca","9dd112296bda8803","9f27b8b89a58b286","45bfdbee8c3d10e3","0e68b11bb1fd4c17","c28b9b3670a06822","fb304d29da5c02d2"],"x":24,"y":199,"w":562,"h":217},{"id":"fc1e310ae95a96ca","type":"function","z":"6babbb5b5eab71f6","g":"df06a118d9fd4c82","name":"Massage data","func":"// ============================================================================\n// Convert timestamp → local day + time (HH:mm)\n// ============================================================================\nfunction getLocalTime(dt, timezone, timezone_offset) {\n const date = new Date(dt * 1000);\n\n let localeString;\n if (timezone) {\n localeString = date.toLocaleString(\"en-US\", { timeZone: timezone });\n } else {\n const hrs = timezone_offset / 3600;\n const sign = hrs >= 0 ? \"+\" : \"-\";\n localeString = date.toLocaleString(\"en-US\", {\n timeZone: \"Etc/GMT\" + sign + Math.abs(hrs)\n });\n }\n\n const local = new Date(localeString);\n\n const days = [\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"];\n return {\n day: days[local.getDay()],\n time:\n (local.getHours().toString().padStart(2, \"0\")) + \":\" +\n (local.getMinutes().toString().padStart(2, \"0\"))\n };\n}\n// =========================\n// Temp to background colour\n// =========================\nfunction backgroundcolor(temp) {\nlet color\nif (temp < -15) color = \"oklch(85% 0.05 290)\" // 1. Pale Indigo/Purple \nelse if (temp < -10) color = \"oklch(87% 0.07 310)\" // 2. Pale Violet \nelse if (temp < -5) color = \"oklch(89% 0.09 265)\" // 3. Pale Deep Blue \nelse if (temp < 0) color = \"oklch(91% 0.10 250)\" // 4. Pale Blue\nelse if (temp < 5) color = \"oklch(93% 0.10 210)\" // 5. Very Light Blue/Cyan \nelse if (temp < 10) color = \"oklch(93% 0.08 150)\" // 6. Very Light Green\nelse if (temp < 15) color = \"oklch(95% 0.10 110)\" // 7. Extremely Light Lime \nelse if (temp < 20) color = \"oklch(96% 0.10 95)\" // 8. Almost White Yellow\nelse if (temp < 25) color = \"oklch(93% 0.12 60)\" // 9. Pale Orange\nelse if (temp < 30) color = \"oklch(91% 0.15 15)\" // 10. Pale Red/Salmon\nelse if (temp < 35) color = \"oklch(87% 0.17 5)\" // 11. Deeper Pale Red\nelse color = \"oklch(85% 0.22 10)\" // 12. Light Magenta-Red\nreturn color\n}\n// ============================================================================\n// Convert degrees → compass\n// ============================================================================\nfunction degreesToCompass(deg) {\n const directions = [\"N\", \"NNE\", \"NE\", \"ENE\", \"E\", \"ESE\", \"SE\", \"SSE\",\n \"S\", \"SSW\", \"SW\", \"WSW\", \"W\", \"WNW\", \"NW\", \"NNW\", \"N\"];\n return directions[Math.round(((deg % 360) / 22.5))];\n}\n\nconst tz = msg.payload.timezone;\nconst tzo = msg.payload.timezone_offset;\n\n// force a local copy of msg.payload to avoid poisoning the cached object\nmsg.payload = JSON.parse(JSON.stringify(msg.payload)) // deep copy\n\nmsg.payload.forecasttime = getLocalTime(\n msg.payload.current.dt,\n msg.payload.timezone,\n msg.payload.timezone_offset\n).time;\n\nconst location = (new URL(msg.payload.url)).searchParams.get('location')\nmsg.payload.location = location\n\n// Current wind formatting\nmsg.payload.current.wind_point = degreesToCompass(msg.payload.current.wind_deg);\nmsg.payload.current.wind_knots = msg.payload.current.wind_speed * 1.944;\nmsg.payload.current.wind_mph = msg.payload.current.wind_speed * 2.237;\n\nmsg.payload.temp = Math.round(msg.payload.current.temp) + \"°C\"\nmsg.payload.humidity = msg.payload.current.humidity + \"%\"\nmsg.payload.pressure = msg.payload.current.pressure + \"hPa\"\nmsg.payload.wind = Math.round(msg.payload.current.wind_mph ) + \"mph \" + degreesToCompass(msg.payload.current.wind_deg)\n\nmsg.payload.color = backgroundcolor(msg.payload.current.temp)\nmsg.payload.location = msg.payload.location.substring(0, 30)\n\nmsg.payload.comments = \"\";\nif (msg.payload.alerts) {\n for (const alert of msg.payload.alerts) {\n if (alert.tags?.length > 0) {\n const s = getLocalTime(alert.start, msg.payload.timezone, msg.payload.timezone_offset);\n const e = getLocalTime(alert.end, msg.payload.timezone, msg.payload.timezone_offset);\n msg.payload.comments =\n `${alert.event} ${s.day} ${s.time} - ${e.day} ${e.time}`;\n break;\n }\n }\n}\nmsg.payload.comments = msg.payload.comments.substring(0, 60)\n\n// ============================================================================\n// HOURLY\n// ============================================================================\nmsg.payload.hourly = (msg.payload.hourly || []).map(h => {\n\n const lt = getLocalTime(h.dt, tz, tzo);\n\n // Build 'note' \n let note = null;\n if (h.wind_speed >= 13.8) {\n note = h.wind_speed + \" m/s\"\n note = \"Wind\"\n note = h.wind_speed > 42.7 ? \"Hurricane\" : h.wind_speed > 28.4 ? \"Storm\" : \"Gale\"\n h.color = \"oklch(0.4 0.01 330)\"\n h.color = \"red\"\n } else if (h.rain && h.rain[\"1h\"] >= 0.5) {\n note = h.rain[\"1h\"] + \" mm\"\n note = \"Rain\"\n h.color = \"blue\" \n } else if (h.snow && h.snow[\"1h\"] >= 0.5) {\n note = h.snow[\"1h\"] +\" mm\"\n note = \"Snow\"\n h.color = \"white\"\n }\n return {\n dt: h.dt,\n temp: h.temp,\n// icon: h?.weather?.[0]?.icon || \"\", // Doesn't work, don't know why\n// id: h?.weather?.[0]?.id || \"\",\n weather: h.weather,\n color: h.color,\n hhmm: lt.time,\n note: note\n };\n});\n\n// ============================================================================\n// DAILY\n// ============================================================================\nmsg.payload.daily = (msg.payload.daily || []).map(d => {\n\n const lt = getLocalTime(d.dt, tz, tzo);\n let note = null\n\n if (d.wind_speed >= 13.8) {\n note = d.wind_speed;\n note = \"Wind\"\n note = d.wind_speed > 42.7 ? \"Hurricane\" : d.wind_speed > 28.4 ? \"Storm\" : \"Gale\"\n d.color = \"red\"\n } else if (d.rain && d.rain >= 2) {\n note = d.rain\n note = \"Rain\"\n d.color = \"blue\"\n } else if (d.snow && d.snow >= 2) {\n note = d.snow\n note = \"Snow\"\n d.color = \"white\"\n }\n return {\n dt: d.dt,\n temp: d.temp,\n weather: d.weather,\n color: d.color,\n ddd: lt.day,\n max: Math.round(d.temp.max),\n min: Math.round(d.temp.min),\n rain: d?.rain || 0,\n snow: d?.snow || 0,\n wind: d?.wind_speed || 0,\n note: note\n };\n});\n\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"url","module":"url"}],"x":130,"y":240,"wires":[["9dd112296bda8803","45bfdbee8c3d10e3","9f27b8b89a58b286","0e68b11bb1fd4c17"]]},{"id":"9dd112296bda8803","type":"ui-template","z":"6babbb5b5eab71f6","g":"df06a118d9fd4c82","group":"737e097eb125e416","page":"","ui":"","name":"OWM Icons 8x9","order":2,"width":"0","height":"0","head":"","format":"<template>\n <div class=\"dashboard-container\">\n <div class=\"content-wrap\" :style=\"{backgroundColor: msg.payload.color}\">\n <!-- === LOCATION ROW === -->\n <div class=\"location-row\">\n <div class=\"location-left\"> {{ msg.payload.location }}</div>\n <div class=\"location-right\">\n @ {{ msg.payload.forecasttime }}\n </div>\n </div>\n <!-- === CURRENT CONDITIONS ROW === -->\n <div class=\"current\">\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Temp</span> {{ msg.payload.temp }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Wind</span> {{ msg.payload.wind }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Hum</span> {{ msg.payload.humidity }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Pres</span> {{ msg.payload.pressure }}\n </div>\n </div>\n\n <!-- === OPTIONAL COMMENTS ROW === -->\n <div class=\"comments-row\" v-if=\"msg.payload.comments\">\n {{ msg.payload.comments }}\n </div>\n\n <!-- HOURLY -->\n <div class=\"hourly-row\">\n <div class=\"icon-wrapper\" v-for=\"(h, index) in msg.payload.hourly.slice(0, 12)\" :key=\"'h' + index\">\n <!-- Time above icon -->\n <div class=\"icon-time\">{{ h.hhmm }}</div>\n <!-- Icon (shadow wrapper) -->\n <div class=\"icon-filter-wrap\">\n <div class=\"weather-icon\" :style=\"{\n '--icon': `url('/icons2/${h.weather[0].icon}.svg')`,\n color: h.color || '#222'\n }\">\n </div>\n </div>\n <!-- Temp below icon -->\n <div class=\"icon-temp\">{{ Math.round(h.temp) }}°</div>\n <!-- Note below temp -->\n <div class=\"icon-note\" v-if=\"h.note\">\n {{ h.note }}\n </div>\n </div>\n </div>\n\n <!-- DAILY -->\n <div class=\"daily-row\">\n <div class=\"icon-wrapper\" v-for=\"(d, index) in msg.payload.daily.slice(0, 8)\" :key=\"'d' + index\">\n <!-- Day name -->\n <div class=\"icon-day\">{{ d.ddd }}</div>\n <!-- Icon -->\n <div class=\"icon-filter-wrap\">\n <div class=\"weather-icon\" :style=\"{\n '--icon': `url('/icons2/${d.weather[0].icon}.svg')`,\n color: d.color || '#222'\n }\">\n </div>\n </div>\n <!-- Temperature high/low -->\n <div class=\"icon-temp\">\n {{ Math.round(d.temp.max) }}/{{ Math.round(d.temp.min) }}°\n </div>\n <!-- Notes -->\n <div class=\"icon-note\" v-if=\"d.note\">\n {{ d.note }}\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":355,"y":240,"wires":[[]]},{"id":"9f27b8b89a58b286","type":"ui-template","z":"6babbb5b5eab71f6","g":"df06a118d9fd4c82","group":"737e097eb125e416","page":"","ui":"","name":"OWM Icons 5x6","order":6,"width":"5","height":"6","head":"","format":"<template>\n <div class=\"dashboard-container\">\n <div class=\"content-wrap\" :style=\"{backgroundColor: msg.payload.color}\">\n <!-- === LOCATION ROW === -->\n <div class=\"location-row\">\n <div class=\"location-left\"> {{ msg.payload.location }}</div>\n <div class=\"location-right\">\n @ {{ msg.payload.forecasttime }}\n </div>\n </div>\n <!-- === CURRENT CONDITIONS ROW === -->\n <div class=\"current\">\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Temp</span> {{ msg.payload.temp }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Wind</span> {{ msg.payload.wind }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Hum</span> {{ msg.payload.humidity }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Pres</span> {{ msg.payload.pressure }}\n </div>\n </div>\n\n <!-- === OPTIONAL COMMENTS ROW === -->\n <div class=\"comments-row\" v-if=\"msg.payload.comments\">\n {{ msg.payload.comments }}\n </div>\n\n <!-- HOURLY -->\n <div class=\"hourly-row\">\n <div class=\"icon-wrapper\" v-for=\"(h, index) in msg.payload.hourly.slice(0, 12)\" :key=\"'h' + index\">\n <!-- Time above icon -->\n <div class=\"icon-time\">{{ h.hhmm }}</div>\n <!-- Icon (shadow wrapper) -->\n <div class=\"icon-filter-wrap\">\n <div class=\"weather-icon\" :style=\"{\n '--icon': `url('/icons2/${h.weather[0].icon}.svg')`,\n color: h.color || '#222'\n }\">\n </div>\n </div>\n <!-- Temp below icon -->\n <div class=\"icon-temp\">{{ Math.round(h.temp) }}°</div>\n <!-- Note below temp -->\n <div class=\"icon-note\" v-if=\"h.note\">\n {{ h.note }}\n </div>\n </div>\n </div>\n\n <!-- DAILY -->\n <div class=\"daily-row\">\n <div class=\"icon-wrapper\" v-for=\"(d, index) in msg.payload.daily.slice(0, 8)\" :key=\"'d' + index\">\n <!-- Day name -->\n <div class=\"icon-day\">{{ d.ddd }}</div>\n <!-- Icon -->\n <div class=\"icon-filter-wrap\">\n <div class=\"weather-icon\" :style=\"{\n '--icon': `url('/icons2/${d.weather[0].icon}.svg')`,\n color: d.color || '#222'\n }\">\n </div>\n </div>\n <!-- Temperature high/low -->\n <div class=\"icon-temp\">\n {{ Math.round(d.temp.max) }}/{{ Math.round(d.temp.min) }}°\n </div>\n <!-- Notes -->\n <div class=\"icon-note\" v-if=\"d.note\">\n {{ d.note }}\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":355,"y":375,"wires":[[]]},{"id":"45bfdbee8c3d10e3","type":"ui-template","z":"6babbb5b5eab71f6","g":"df06a118d9fd4c82","group":"737e097eb125e416","page":"","ui":"","name":"OWM Icons 6x7","order":5,"width":"6","height":"7","head":"","format":"<template>\n <div class=\"dashboard-container\">\n <div class=\"content-wrap\" :style=\"{backgroundColor: msg.payload.color}\">\n <!-- === LOCATION ROW === -->\n <div class=\"location-row\">\n <div class=\"location-left\"> {{ msg.payload.location }}</div>\n <div class=\"location-right\">\n @ {{ msg.payload.forecasttime }}\n </div>\n </div>\n <!-- === CURRENT CONDITIONS ROW === -->\n <div class=\"current\">\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Temp</span> {{ msg.payload.temp }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Wind</span> {{ msg.payload.wind }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Hum</span> {{ msg.payload.humidity }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Pres</span> {{ msg.payload.pressure }}\n </div>\n </div>\n\n <!-- === OPTIONAL COMMENTS ROW === -->\n <div class=\"comments-row\" v-if=\"msg.payload.comments\">\n {{ msg.payload.comments }}\n </div>\n\n <!-- HOURLY -->\n <div class=\"hourly-row\">\n <div class=\"icon-wrapper\" v-for=\"(h, index) in msg.payload.hourly.slice(0, 12)\" :key=\"'h' + index\">\n <!-- Time above icon -->\n <div class=\"icon-time\">{{ h.hhmm }}</div>\n <!-- Icon (shadow wrapper) -->\n <div class=\"icon-filter-wrap\">\n <div class=\"weather-icon\" :style=\"{\n '--icon': `url('/icons2/${h.weather[0].icon}.svg')`,\n color: h.color || '#222'\n }\">\n </div>\n </div>\n <!-- Temp below icon -->\n <div class=\"icon-temp\">{{ Math.round(h.temp) }}°</div>\n <!-- Note below temp -->\n <div class=\"icon-note\" v-if=\"h.note\">\n {{ h.note }}\n </div>\n </div>\n </div>\n\n <!-- DAILY -->\n <div class=\"daily-row\">\n <div class=\"icon-wrapper\" v-for=\"(d, index) in msg.payload.daily.slice(0, 8)\" :key=\"'d' + index\">\n <!-- Day name -->\n <div class=\"icon-day\">{{ d.ddd }}</div>\n <!-- Icon -->\n <div class=\"icon-filter-wrap\">\n <div class=\"weather-icon\" :style=\"{\n '--icon': `url('/icons2/${d.weather[0].icon}.svg')`,\n color: d.color || '#222'\n }\">\n </div>\n </div>\n <!-- Temperature high/low -->\n <div class=\"icon-temp\">\n {{ Math.round(d.temp.max) }}/{{ Math.round(d.temp.min) }}°\n </div>\n <!-- Notes -->\n <div class=\"icon-note\" v-if=\"d.note\">\n {{ d.note }}\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":355,"y":330,"wires":[[]]},{"id":"0e68b11bb1fd4c17","type":"ui-template","z":"6babbb5b5eab71f6","g":"df06a118d9fd4c82","group":"737e097eb125e416","page":"","ui":"","name":"OWM Icons 7x8","order":4,"width":"7","height":"8","head":"","format":"<template>\n <div class=\"dashboard-container\">\n <div class=\"content-wrap\" :style=\"{backgroundColor: msg.payload.color}\">\n <!-- === LOCATION ROW === -->\n <div class=\"location-row\">\n <div class=\"location-left\"> {{ msg.payload.location }}</div>\n <div class=\"location-right\">\n @ {{ msg.payload.forecasttime }}\n </div>\n </div>\n <!-- === CURRENT CONDITIONS ROW === -->\n <div class=\"current\">\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Temp</span> {{ msg.payload.temp }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Wind</span> {{ msg.payload.wind }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Hum</span> {{ msg.payload.humidity }}\n </div>\n <div class=\"currentitem\">\n <span class=\"currentlabel\">Pres</span> {{ msg.payload.pressure }}\n </div>\n </div>\n\n <!-- === OPTIONAL COMMENTS ROW === -->\n <div class=\"comments-row\" v-if=\"msg.payload.comments\">\n {{ msg.payload.comments }}\n </div>\n\n <!-- HOURLY -->\n <div class=\"hourly-row\">\n <div class=\"icon-wrapper\" v-for=\"(h, index) in msg.payload.hourly.slice(0, 12)\" :key=\"'h' + index\">\n <!-- Time above icon -->\n <div class=\"icon-time\">{{ h.hhmm }}</div>\n <!-- Icon (shadow wrapper) -->\n <div class=\"icon-filter-wrap\">\n <div class=\"weather-icon\" :style=\"{\n '--icon': `url('/icons2/${h.weather[0].icon}.svg')`,\n color: h.color || '#222'\n }\">\n </div>\n </div>\n <!-- Temp below icon -->\n <div class=\"icon-temp\">{{ Math.round(h.temp) }}°</div>\n <!-- Note below temp -->\n <div class=\"icon-note\" v-if=\"h.note\">\n {{ h.note }}\n </div>\n </div>\n </div>\n\n <!-- DAILY -->\n <div class=\"daily-row\">\n <div class=\"icon-wrapper\" v-for=\"(d, index) in msg.payload.daily.slice(0, 8)\" :key=\"'d' + index\">\n <!-- Day name -->\n <div class=\"icon-day\">{{ d.ddd }}</div>\n <!-- Icon -->\n <div class=\"icon-filter-wrap\">\n <div class=\"weather-icon\" :style=\"{\n '--icon': `url('/icons2/${d.weather[0].icon}.svg')`,\n color: d.color || '#222'\n }\">\n </div>\n </div>\n <!-- Temperature high/low -->\n <div class=\"icon-temp\">\n {{ Math.round(d.temp.max) }}/{{ Math.round(d.temp.min) }}°\n </div>\n <!-- Notes -->\n <div class=\"icon-note\" v-if=\"d.note\">\n {{ d.note }}\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":355,"y":285,"wires":[[]]},{"id":"c28b9b3670a06822","type":"ui-template","z":"6babbb5b5eab71f6","g":"df06a118d9fd4c82","group":"","page":"c01a9a1536e052af","ui":"","name":"CSS","order":0,"width":0,"height":0,"head":"","format":"/* Outer container because the template height is fixed but contents may not fill it at small sizes */\n.dashboard-container {\n width: 100%;\n height: auto;\n padding: 0; \n border: none; \n background: none; \n}\n\n/* Inner container sizes to content */\n.content-wrap {\n width: 100%;\n container-type: inline-size; /* Enables container queries font sizes */\n display: flex;\n flex-direction: column;\n padding: 5px;\n border: 4px solid black;\n border-radius: 12px;\n box-shadow: 4px 4px 28px rgba(0,0,0,0.3) inset;\n box-sizing: border-box;\n}\n\n.location-row, .comments-row, .current, .currentitem, .currentlabel,\n.icon-time, .icon-day, .icon-temp, .icon-note {\n filter: none !important; /* filter used on icons looks awful on text */\n text-shadow:\n 1px 1px 2px rgba(0,0,0,0.55),\n 0px 0px 4px rgba(0,0,0,0.40),\n 0px 0px 6px rgba(0,0,0,0.25);\n}\n\n/* === Location Row === */\n.location-row {\n display: flex;\n justify-content: space-between;\n align-items: baseline;\n width: 97%;\n margin: 0 auto;\n font-family: \"Times New Roman\", serif;\n line-height: 1.1;\n}\n\n.location-left {\n font-size: clamp(14px, 6cqi, 50px);\n white-space: nowrap;\n}\n\n.location-right {\n font-size: clamp(10px, 4cqi, 40px);\n white-space: nowrap;\n}\n\n/* === Comments row === */\n.comments-row {\n display: flex;\n justify-content: flex-end;\n margin-right: 2%;\n white-space: nowrap;\n color: orangered;\n font-size: clamp(10px, 3.5cqi, 20px); /* minimum, preferred, max size */\n}\n\n/* === Current conditions row === */\n.current {\n display: flex;\n justify-content: space-between;\n align-items: baseline;\n width: 100%;\n padding-left: 1rem;\n padding-right: 1rem;\n font-size: clamp(10px, 3.8cqi, 24px); \n}\n\n/* Each value unit (Temp, Wind, Hum, Pres) */\n.currentitem {\n display: flex;\n gap: 0.25em;\n align-items: baseline;\n}\n\n.currentlabel {\n font-size: 0.7em; /* (70% of row font size) */\n opacity: 0.8;\n}\n@container (max-width: 500px) { /* Hide labels when container width is below 500px */\n .currentlabel {\n display: none;\n }\n}\n\n/* === Hourly and Daily rows === */\n.hourly-row, .daily-row {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: flex-start;\n gap: 12px;\n width: 100%;\n box-sizing: border-box;\n}\n\n.hourly-row {\n width: 95%;\n margin: 0 auto;\n margin-top: 2cqh;\n}\n.daily-row {\n width: 85%;\n margin: 0 auto;\n margin-top: 2cqh;\n}\n\n/* Hourly: 12 icons */\n.hourly-row .icon-wrapper {\n flex: 0 0 calc((100% - 11 * 12px) / 12); /* flexible for 12 icons and 11 gaps based on widget size */\n}\n\n/* Daily: 8 icons */\n.daily-row .icon-wrapper {\n flex: 0 0 calc((100% - 7 * 12px) / 8);\n}\n\n/* === Icon Wrapper === */\n.icon-wrapper {\n display: flex;\n flex-direction: column;\n align-items: center;\n min-width: 0;\n text-align: center;\n gap: 2px;\n}\n\n/* === Icon wrapper for drop shadow === */\n.icon-filter-wrap {\n width: 100%;\n max-width: 60px;\n aspect-ratio: 1 / 1;\n display: flex;\n justify-content: center;\n align-items: center;\n filter:\n drop-shadow(0 0.5px 0.8px rgba(0,0,0,0.55))\n drop-shadow(0 2px 3px rgba(0,0,0,0.28))\n drop-shadow(0 4px 6px rgba(0,0,0,0.18));\n}\n\n/* === Mask to colorise weather icon === */\n.weather-icon {\n width: 100%;\n aspect-ratio: 1 / 1;\n max-width: 60px;\n background-color: currentColor;\n mask-image: var(--icon);\n -webkit-mask-image: var(--icon);\n mask-repeat: no-repeat;\n -webkit-mask-repeat: no-repeat;\n mask-size: contain;\n -webkit-mask-size: contain;\n mask-position: center;\n -webkit-mask-position: center;\n}\n\n/* === Various text items === */\n.icon-time, .icon-temp, .icon-note, .icon-day {\n font-size: clamp(9px, 2.8cqi, 20px);\n margin: 0;\n line-height: 1.2;\n}\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"page:style","className":"","x":510,"y":240,"wires":[[]]},{"id":"fb304d29da5c02d2","type":"comment","z":"6babbb5b5eab71f6","g":"df06a118d9fd4c82","name":"Notes","info":"OWM call specifies metric units, but I show wind speed as mph\nIf you change to other units the template will still say °C but it won't convert\n\nAPI limits\nThis uses OpenWeatherMap OneCall 3.0 which requires a key. It expects to find the key in a global context variable.\nIt's free up to 1000 calls a day but there's a fee if you exceed that.\nSo it caches results for up to 15 minutes and limits the number of calls to 250 a day. \nIf you don't access the API from anywere else you can change that.\n\n\nLocation\nTo get a forecast for a specific location inject an object eg\n{\n \"location\": \"Flying Fish Cove\",\n \"latitude\": -10.426302,\n \"longitude\": 105.67324\n}\nJust a timestamp will use the defaults in function \"Cached or API call\"\n\nBackground colour\nThe widget background varies according to the location's current temperature.\nColours and change temps are set in function \"Massage data\"\n\nIcons\nD\n\nExceptional weather\nStrong winds, heavy rainfall and snow are indicated by a text note under the icon and icon colour:\nwind > red (because coastal storm warning flags in the US are red)\nRain > blue \nSnow > white \nThe trigger levels are arbitrary and only one indication can be shown per time period.\nEasy to include additional extreme conditions - ice, hail, UV index etc.\n\nWidget size\nThe template width will vary according to the browser window, whether the left menu bar is displayed etc.\nConsequently the contents have to scale to fit, hence cqi font sizes etc. \nTemplate height is fixed as a multiple of the page theme row height, so sometimes there may be\nwhite space below the display.\n\nOn my laptop, in Firefox the widget seems to work at sizes 8x9, 7x8, 6x7 and 5x6 when the theme row height is 48px.\nFor theme row height 32px, to avoid a vertical scrollbar at some widget sizes, add one more row eg 7x9\nIf it occupise the full width of the ui-group, use auto.","x":100,"y":300,"wires":[]},{"id":"737e097eb125e416","type":"ui-group","name":"icons","page":"c01a9a1536e052af","width":"8","height":1,"order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"c01a9a1536e052af","type":"ui-page","name":"andon","ui":"e9c974f7c1d080d1","path":"/andon","icon":"home","layout":"grid","theme":"0d92c765bfad87e6","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":"e9c974f7c1d080d1","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"page","navigationStyle":"default","titleBarStyle":"default","showReconnectNotification":true,"notificationDisplayTime":1,"showDisconnectNotification":true,"allowInstall":true},{"id":"0d92c765bfad87e6","type":"ui-theme","name":"Basic Blue Theme","colors":{"surface":"#4d58ff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"2px","density":"default"}},{"id":"a232df0f556f92d4","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.29.0"}}]
My browser developer console shows an error in the way the background colour is passed into the template:
errorCaptured TypeError: can't access property "color", msg.payload is undefined
<div class="content-wrap" :style="{backgroundColor: msg.payload.color}">
I would be grateful if someone could fix this - and explain why chatgpt suggested backgroundColor, when the css selector should be background-color!