[US] Weather from api.weather.gov
Node-RED

[US] Weather from api.weather.gov

Display weather from US National Weather Service
A flow by somebody

Download flow

Flow Details

  • Upload the icons to your Awtrix.

There 2 configuration nodes:

  • First, set your desired latitude and longitude in second configuration node, run it once, you will see output from console panel telling you the WFO and gridX, gridY you need.
  • Second given the above information, change the first configuration node to match your desired settings.
  • You may ignore authorization_header if no credentials are needed.
[ { "id": "7efaf915ca5b7337", "type": "tab", "label": "Awtrix weather by api.weather.gov", "disabled": false, "info": "", "env": [] }, { "id": "97e239f3b23160ca", "type": "group", "z": "7efaf915ca5b7337", "name": "Awtrix weather by api.weather.gov", "style": { "label": true }, "nodes": [ "514be84e6537b208", "a89bf6bc9b4ee6cd", "43c8034f31d87da1", "c3731eefae02dcb4", "ef31a5c5cd4dbcfc", "82f57ae3116cc75d", "0654e5b8038248e8", "d5cf1dbca661bcb3", "515b12b72d5e63dc", "8c1e5672f66cc334", "52931d15a219febb", "eb9f1cfe4f036184", "7071c05bfb9d9ac7", "97fb33089ff4fd73", "ece47f29798a9a1f", "eae9209dd318913e", "9e4bcd38a1a73d39", "95f76795e13faa31", "6824085062f5947f", "2ea88619e5304433", "a849b46631023e92", "b7d85c4da0f239fb" ], "x": 214, "y": 119, "w": 2092, "h": 482 }, { "id": "514be84e6537b208", "type": "http request", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "make weather request", "method": "GET", "ret": "obj", "paytoqs": "ignore", "url": "", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": true, "headers": [], "x": 1000, "y": 200, "wires": [ [ "43c8034f31d87da1", "515b12b72d5e63dc" ] ] }, { "id": "a89bf6bc9b4ee6cd", "type": "inject", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "check", "props": [ { "p": "payload" } ], "repeat": "900", "crontab": "", "once": true, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 330, "y": 200, "wires": [ [ "d5cf1dbca661bcb3" ] ] }, { "id": "43c8034f31d87da1", "type": "debug", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "debug weather response", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1250, "y": 380, "wires": [] }, { "id": "c3731eefae02dcb4", "type": "change", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "generate weather request", "rules": [ { "t": "set", "p": "url", "pt": "msg", "to": "https://api.weather.gov/gridpoints/{wfo}/{x,y}/forecast/hourly", "tot": "str" }, { "t": "change", "p": "url", "pt": "msg", "from": "{wfo}", "fromt": "str", "to": "wfo", "tot": "flow" }, { "t": "change", "p": "url", "pt": "msg", "from": "{x,y}", "fromt": "str", "to": "x,y", "tot": "flow" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 750, "y": 200, "wires": [ [ "514be84e6537b208" ] ] }, { "id": "ef31a5c5cd4dbcfc", "type": "catch", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "", "scope": null, "uncaught": false, "x": 580, "y": 560, "wires": [ [ "82f57ae3116cc75d" ] ] }, { "id": "82f57ae3116cc75d", "type": "debug", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "debug errors", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 790, "y": 560, "wires": [] }, { "id": "0654e5b8038248e8", "type": "comment", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "Change configuration in this node", "info": "", "x": 540, "y": 160, "wires": [] }, { "id": "d5cf1dbca661bcb3", "type": "change", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "configuration", "rules": [ { "t": "set", "p": "wfo", "pt": "flow", "to": "OKX", "tot": "str" }, { "t": "set", "p": "x,y", "pt": "flow", "to": "36,36", "tot": "str" }, { "t": "set", "p": "use_fahrenheit", "pt": "flow", "to": "true", "tot": "bool" }, { "t": "set", "p": "awtrix_ip", "pt": "flow", "to": "192.168.1.101", "tot": "str" }, { "t": "set", "p": "awtrix_app_name", "pt": "flow", "to": "weather", "tot": "str" }, { "t": "set", "p": "authorization_header", "pt": "flow", "to": "Basic ZXhhbXBsZTpwYXNzd29yZA==", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 510, "y": 200, "wires": [ [ "c3731eefae02dcb4" ] ] }, { "id": "515b12b72d5e63dc", "type": "function", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "frames generator", "func": "const LIFETIME = '3600';\nconst PUSH_ICON = '0';\nconst REPEAT = '2';\nconst DURATION = '';\n// Look ahead 30 mins\nconst TIME_OFFSET_IN_MS = 30 * 60 * 1000;\nconst USE_FAHRENHEIT = flow.get('use_fahrenheit');\n\n// Match keyword case insensitive from top to bottom\n// Put \"cloudy\" before \"mostly cloudy\" will cause latter one never matches\nconst KEYWORD_TO_ICON_DICT = {\n 'storm': 'stormy',\n 'foggy': 'foggy',\n 'snow': 'snowy',\n 'rain': 'rainy',\n 'partly cloudy': 'partly_cloudy',\n 'cloudy': 'mostly_cloudy',\n 'clear': 'moon',\n 'sunny': 'sunny',\n '': 'weather_fallback',\n};\n\nconst createFrameWithTextAndIcon = (text, icon) => ({\n text,\n icon,\n lifetime: LIFETIME,\n pushIcon: PUSH_ICON,\n repeat: REPEAT,\n duration: DURATION,\n});\n\nconst mapShortForecastToIcon = (shortForecast) => {\n for (const keyword of Object.keys(KEYWORD_TO_ICON_DICT)) {\n if (shortForecast?.toLowerCase().includes(keyword?.toLowerCase())) {\n return KEYWORD_TO_ICON_DICT[keyword];\n }\n }\n};\n\nconst findPeriodNow = (periods, timeOffsetInMs = TIME_OFFSET_IN_MS) => {\n if (!Array.isArray(periods)) {\n throw new Error('periods should be array');\n }\n const timestampInEpoch = Date.now() + TIME_OFFSET_IN_MS;\n const found = periods.find(period => (new Date(period?.startTime)).valueOf() > timestampInEpoch);\n if (!found) {\n throw new Error(`unable to find period after ${timestampInEpoch}`);\n }\n return found;\n};\n\nconst FToC = (f, optionalDecimalPoints = 1) => Math.round((f - 32) * 5 / 9 * Math.pow(10, optionalDecimalPoints)) / Math.pow(10, optionalDecimalPoints);\n\nconst handleMessage = (message) => {\n const periods = message?.payload?.properties?.periods;\n if (!Array.isArray(periods)) {\n throw new Error('unable to parse msessage');\n }\n const frames = [];\n const currentPeriod = findPeriodNow(periods);\n const temperatureText = USE_FAHRENHEIT ? `${currentPeriod?.temperature}°F` : `${FToC(currentPeriod?.temperature)}°C`;\n frames.push(createFrameWithTextAndIcon(\n temperatureText,\n mapShortForecastToIcon(currentPeriod?.shortForecast),\n ));\n frames.push(createFrameWithTextAndIcon(\n currentPeriod?.shortForecast,\n mapShortForecastToIcon(currentPeriod?.shortForecast),\n ));\n frames.push(createFrameWithTextAndIcon(\n `${currentPeriod?.probabilityOfPrecipitation?.value}%`,\n 'umbrella',\n ));\n frames.push(createFrameWithTextAndIcon(\n // `${currentPeriod?.windSpeed} ${currentPeriod?.windDirection}`,\n currentPeriod?.windSpeed,\n 'wind',\n ));\n frames.push(createFrameWithTextAndIcon(\n `${currentPeriod?.relativeHumidity?.value}%`,\n 'humidity',\n ));\n message.payload.frames = frames;\n return message;\n};\n\nreturn handleMessage(msg);", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1250, "y": 200, "wires": [ [ "8c1e5672f66cc334", "52931d15a219febb" ] ] }, { "id": "8c1e5672f66cc334", "type": "debug", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "debug frames generator", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1530, "y": 380, "wires": [] }, { "id": "52931d15a219febb", "type": "change", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "generate awtrix request", "rules": [ { "t": "set", "p": "method", "pt": "msg", "to": "POST", "tot": "str" }, { "t": "set", "p": "url", "pt": "msg", "to": "http://{awtrix_ip}/api/custom?name={awtrix_app_name}", "tot": "str" }, { "t": "change", "p": "url", "pt": "msg", "from": "{awtrix_ip}", "fromt": "str", "to": "awtrix_ip", "tot": "flow" }, { "t": "change", "p": "url", "pt": "msg", "from": "{awtrix_app_name}", "fromt": "str", "to": "awtrix_app_name", "tot": "flow" }, { "t": "move", "p": "headers", "pt": "msg", "to": "original_headers", "tot": "msg" }, { "t": "move", "p": "payload", "pt": "msg", "to": "original_payload", "tot": "msg" }, { "t": "set", "p": "headers.Authorization", "pt": "msg", "to": "authorization_header", "tot": "flow" }, { "t": "set", "p": "payload", "pt": "msg", "to": "original_payload.frames", "tot": "msg", "dc": true } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 1510, "y": 200, "wires": [ [ "eb9f1cfe4f036184", "7071c05bfb9d9ac7" ] ] }, { "id": "eb9f1cfe4f036184", "type": "debug", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "debug awtrix request", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1820, "y": 380, "wires": [] }, { "id": "7071c05bfb9d9ac7", "type": "http request", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "make awtrix request", "method": "use", "ret": "txt", "paytoqs": "body", "url": "", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": false, "headers": [ { "keyType": "Content-Type", "keyValue": "", "valueType": "application/json", "valueValue": "" } ], "x": 1860, "y": 200, "wires": [ [ "97fb33089ff4fd73" ] ] }, { "id": "97fb33089ff4fd73", "type": "debug", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "debug awtrix response", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 2160, "y": 380, "wires": [] }, { "id": "ece47f29798a9a1f", "type": "inject", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "get WFO and gridX, gridY", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 370, "y": 440, "wires": [ [ "eae9209dd318913e" ] ] }, { "id": "eae9209dd318913e", "type": "change", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "configuration", "rules": [ { "t": "set", "p": "latitude", "pt": "msg", "to": "40.730610", "tot": "str" }, { "t": "set", "p": "longitude", "pt": "msg", "to": "-73.935242", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 590, "y": 440, "wires": [ [ "9e4bcd38a1a73d39" ] ] }, { "id": "9e4bcd38a1a73d39", "type": "change", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "generate grid lookup request", "rules": [ { "t": "set", "p": "url", "pt": "msg", "to": "https://api.weather.gov/points/{latitude},{longitude}", "tot": "str" }, { "t": "change", "p": "url", "pt": "msg", "from": "{latitude}", "fromt": "str", "to": "latitude", "tot": "msg" }, { "t": "change", "p": "url", "pt": "msg", "from": "{longitude}", "fromt": "str", "to": "longitude", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 840, "y": 440, "wires": [ [ "95f76795e13faa31" ] ] }, { "id": "95f76795e13faa31", "type": "http request", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "make grid lookup request", "method": "GET", "ret": "obj", "paytoqs": "ignore", "url": "", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": true, "headers": [], "x": 1110, "y": 440, "wires": [ [ "6824085062f5947f", "2ea88619e5304433" ] ] }, { "id": "6824085062f5947f", "type": "debug", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "debug grid lookup response", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1380, "y": 540, "wires": [] }, { "id": "2ea88619e5304433", "type": "template", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "", "field": "payload", "fieldType": "msg", "format": "handlebars", "syntax": "mustache", "template": "NWS Weather Forecast Office (WFO):\n{{payload.properties.gridId}}\nLocation gridX:\n{{payload.properties.gridX}}\nLocation gridY:\n{{payload.properties.gridY}}", "output": "str", "x": 1380, "y": 440, "wires": [ [ "a849b46631023e92" ] ] }, { "id": "a849b46631023e92", "type": "debug", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "printout WFO and gridX, gridY", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 1630, "y": 440, "wires": [] }, { "id": "b7d85c4da0f239fb", "type": "comment", "z": "7efaf915ca5b7337", "g": "97e239f3b23160ca", "name": "Change configuration in this node to get WFO and gridX, gridY needed for configuration above", "info": "", "x": 640, "y": 380, "wires": [] } ]
-- Flow first published on February 15, 2024.