From 45ea515d80348cd3cf31b73385f59961421894e3 Mon Sep 17 00:00:00 2001 From: Tony Stork Date: Thu, 20 Mar 2025 00:33:04 -0400 Subject: [PATCH] Make area weather alerts reusable, add a few areas --- flows.json | 635 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 446 insertions(+), 189 deletions(-) diff --git a/flows.json b/flows.json index 6a71e90..21ec0c0 100644 --- a/flows.json +++ b/flows.json @@ -57,6 +57,38 @@ "color": "#DDAA99", "icon": "font-awesome/fa-cloud" }, + { + "id": "0e70b91ae6570376", + "type": "subflow", + "name": "Area Weather Alerts", + "info": "", + "category": "", + "in": [ + { + "x": 60, + "y": 140, + "wires": [ + { + "id": "8ac36baa32177bd6" + } + ] + } + ], + "out": [], + "env": [], + "meta": {}, + "color": "#DDAA99", + "status": { + "x": 580, + "y": 320, + "wires": [ + { + "id": "404170ec56d4c845", + "port": 5 + } + ] + } + }, { "id": "df425a30fbcfb2e9", "type": "group", @@ -90,21 +122,68 @@ "label": true }, "nodes": [ - "8ac36baa32177bd6", "aa989a69868fc5fe", - "846ac653b993bbec", - "fc63778f17491208", - "404170ec56d4c845", - "ab2e9b884521285a", - "9151c0cd48bb3c45", "67561d51aaa61dfa", - "6e785299ac298ccd", - "4d8ca3ed3f302d97" + "bc210da75548aa27" ], "x": 14, "y": 239, - "w": 1032, - "h": 322 + "w": 552, + "h": 142 + }, + { + "id": "56542cfc9d0c7a24", + "type": "group", + "z": "ed4ea3c2bc13d1ec", + "name": "KY Lake Alerts", + "style": { + "label": true + }, + "nodes": [ + "bce3a3a1210ab253", + "e8efcf839f012317", + "5a0e961755fd6b45" + ], + "x": 14, + "y": 399, + "w": 552, + "h": 142 + }, + { + "id": "0cd2e4517585a8c3", + "type": "group", + "z": "ed4ea3c2bc13d1ec", + "name": "Ashland County Alerts", + "style": { + "label": true + }, + "nodes": [ + "e0c88351cce4178c", + "ee268b5bef276f45", + "028431d8cab86f80" + ], + "x": 14, + "y": 559, + "w": 552, + "h": 142 + }, + { + "id": "b11deb643761dfcf", + "type": "group", + "z": "ed4ea3c2bc13d1ec", + "name": "Bayfield County Alerts", + "style": { + "label": true + }, + "nodes": [ + "d2c7d572608b7d19", + "26a089d3cbbb5ef1", + "eed501c9777eecb5" + ], + "x": 14, + "y": 719, + "w": 552, + "h": 142 }, { "id": "200f284a1171f0a8", @@ -175,6 +254,161 @@ [] ] }, + { + "id": "8ac36baa32177bd6", + "type": "http request", + "z": "0e70b91ae6570376", + "name": "NWS Alerts API", + "method": "GET", + "ret": "obj", + "paytoqs": "ignore", + "url": "", + "tls": "", + "persist": false, + "proxy": "", + "insecureHTTPParser": false, + "authType": "", + "senderr": false, + "headers": [], + "x": 200, + "y": 140, + "wires": [ + [ + "404170ec56d4c845" + ] + ] + }, + { + "id": "404170ec56d4c845", + "type": "function", + "z": "0e70b91ae6570376", + "name": "Filter", + "func": "const severeWarningEvents = [\"Severe Thunderstorm Warning\",\"Destructive Severe Thunderstorm Warning\",\"Considerable Destructive Severe Thunderstorm Warning\"]\nconst tornadoWarningEvents = [\"Tornado Warning\",\"Radar Indicated Tornado Warning\",\"Confirmed Tornado Warning\",\"Tornado Emergency\"]\nconst tornadoWatchEvents = [\"Tornado Watch\"]\nconst severeThunderstormWatchEvents = [\"Severe Thunderstorm Watch\"]\nconst area = msg.area\nconst mqttTopic = \"weather/alerts/\" + area\nconst tstormTopic = mqttTopic + \"/severe_thunderstorm_warning\"\nconst tornadoWarnTopic = mqttTopic + \"/tornado_warning\"\nconst tornadoWatchTopic = mqttTopic + \"/tornado_watch\"\nconst severeThunderstormWatchTopic = mqttTopic + \"/severe_thunderstorm_watch\"\n\nlet alerts = msg.payload.features\nlet tornado_possible = false\nlet considerable_destructive = false\nlet confirmed = false\n\n// Filter for Severe Thunderstorm Warnings\n\nlet ts = alerts.filter(function(alert) {\n if (alert.properties &&\n severeWarningEvents.includes(alert.properties.event)) {\n return true\n }\n})\n\n// Filter for Severe Thunderstorm Warnings with tornado detection\n// that have a tornado possible parameter\n\nlet tstp = alerts.filter(function(alert) {\n if (alert.properties &&\n severeWarningEvents.includes(alert.properties.event) &&\n alert.properties.parameters &&\n alert.properties.parameters.tornadoDetection &&\n alert.properties.parameters.tornadoDetection.length > 0) {\n let tornadoPossible = alert.properties.parameters.tornadoDetection[0]\n return tornadoPossible === \"POSSIBLE\"\n }\n})\n\nif (tstp.length > 0) {\n tornado_possible = true\n}\n\n// Filter for considerable destructive severe thunderstorm warnings\n\nlet cdst = alerts.filter(function(alert) {\n if (alert.properties &&\n alert.properties.event === \"Considerable Destructive Severe Thunderstorm Warning\") {\n return true\n }\n})\n\nif (cdst.length > 0) {\n considerable_destructive = true\n}\n\n// Filter for Tornado Warnings\n\nlet tornado = alerts.filter(function(alert) {\n if (alert.properties &&\n tornadoWarningEvents.includes(alert.properties.event)) {\n return true\n }\n})\n\nlet confirmed_tornado = alerts.filter(function(alert) {\n if (alert.properties &&\n alert.properties.event === \"Confirmed Tornado Warning\") {\n return true\n }\n})\n\nif (confirmed_tornado.length > 0) {\n confirmed = true\n}\n\n// Filter for Tornado Watches\n\nlet tornado_watch = alerts.filter(function(alert) {\n if (alert.properties &&\n tornadoWatchEvents.includes(alert.properties.event)) {\n return true\n }\n})\n\n// Filter for Severe Thunderstorm Watches\n\nlet severe_thunderstorm_watch = alerts.filter(function(alert) {\n if (alert.properties &&\n severeThunderstormWatchEvents.includes(alert.properties.event)) {\n return true\n }\n})\n\n// If there are any alerts, return them\n\nlet allAlerts = {\n \"payload\": msg.payload.features\n}\n\nlet tstormMsg = {\n \"payload\": {\n \"alerts\": ts,\n \"count\": ts.length,\n \"tornado_possible\": tornado_possible,\n \"considerable_destructive\": considerable_destructive\n },\n \"topic\": tstormTopic\n}\n\nlet tornadoWarnMsg = {\n \"payload\": {\n \"alerts\": tornado,\n \"count\": tornado.length,\n \"confirmed\": confirmed\n },\n \"topic\": tornadoWarnTopic\n}\n\nlet tornadoWatchMsg = {\n \"payload\": {\n \"alerts\": tornado_watch,\n \"count\": tornado_watch.length\n },\n \"topic\": tornadoWatchTopic\n}\n\nlet severeThunderstormWatchMsg = {\n \"payload\": {\n \"alerts\": severe_thunderstorm_watch,\n \"count\": severe_thunderstorm_watch.length\n },\n \"topic\": severeThunderstormWatchTopic\n}\n\n// Create status message for node\n\nlet statusMsg = {\n \"status\": {\n \"fill\": \"green\",\n \"shape\": \"dot\",\n \"text\": alerts.length + \" alerts processed at \" + new Date().toLocaleString()\n }\n}\n\n// Send messages to output nodes\n\nnode.send([allAlerts,tstormMsg,tornadoWarnMsg,tornadoWatchMsg,severeThunderstormWatchMsg,statusMsg])", + "outputs": 6, + "timeout": 0, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 390, + "y": 140, + "wires": [ + [ + "846ac653b993bbec" + ], + [ + "9151c0cd48bb3c45" + ], + [ + "ab2e9b884521285a" + ], + [ + "6e785299ac298ccd" + ], + [ + "4d8ca3ed3f302d97" + ], + [] + ], + "outputLabels": [ + "Severe Thunderstorm Warning", + "Tornado Warning", + "", + "", + "", + "" + ] + }, + { + "id": "846ac653b993bbec", + "type": "mqtt out", + "z": "0e70b91ae6570376", + "name": "MQTT - All Alerts", + "topic": "", + "qos": "0", + "retain": "", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "200f284a1171f0a8", + "x": 650, + "y": 20, + "wires": [] + }, + { + "id": "ab2e9b884521285a", + "type": "mqtt out", + "z": "0e70b91ae6570376", + "name": "MQTT - Tornado Warning", + "topic": "", + "qos": "0", + "retain": "", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "200f284a1171f0a8", + "x": 670, + "y": 140, + "wires": [] + }, + { + "id": "9151c0cd48bb3c45", + "type": "mqtt out", + "z": "0e70b91ae6570376", + "name": "MQTT - Severe Thunderstorm Warning", + "topic": "", + "qos": "0", + "retain": "", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "200f284a1171f0a8", + "x": 710, + "y": 80, + "wires": [] + }, + { + "id": "6e785299ac298ccd", + "type": "mqtt out", + "z": "0e70b91ae6570376", + "name": "MQTT - Tornado Watch", + "topic": "", + "qos": "0", + "retain": "", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "200f284a1171f0a8", + "x": 670, + "y": 200, + "wires": [] + }, + { + "id": "4d8ca3ed3f302d97", + "type": "mqtt out", + "z": "0e70b91ae6570376", + "name": "MQTT - Severe Thunderstorm Watch", + "topic": "", + "qos": "0", + "retain": "", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "200f284a1171f0a8", + "x": 710, + "y": 260, + "wires": [] + }, { "id": "7d9f24e0a78ec15c", "type": "http request", @@ -299,32 +533,6 @@ "y": 120, "wires": [] }, - { - "id": "8ac36baa32177bd6", - "type": "http request", - "z": "ed4ea3c2bc13d1ec", - "g": "b0521ee44dc796d1", - "name": "Defiance County", - "method": "GET", - "ret": "obj", - "paytoqs": "ignore", - "url": "https://api.weather.gov/alerts/active/zone/OHC039", - "tls": "", - "persist": false, - "proxy": "", - "insecureHTTPParser": false, - "authType": "", - "senderr": false, - "headers": [], - "x": 300, - "y": 340, - "wires": [ - [ - "fc63778f17491208", - "404170ec56d4c845" - ] - ] - }, { "id": "aa989a69868fc5fe", "type": "inject", @@ -336,7 +544,13 @@ "p": "payload" }, { - "p": "topic", + "p": "url", + "v": "https://api.weather.gov/alerts/active/zone/OHC039", + "vt": "str" + }, + { + "p": "area", + "v": "defiance", "vt": "str" } ], @@ -348,134 +562,13 @@ "payload": "", "payloadType": "date", "x": 130, - "y": 340, - "wires": [ - [ - "8ac36baa32177bd6" - ] - ] - }, - { - "id": "846ac653b993bbec", - "type": "mqtt out", - "z": "ed4ea3c2bc13d1ec", - "g": "b0521ee44dc796d1", - "name": "", - "topic": "weather/alerts/defiance", - "qos": "0", - "retain": "", - "respTopic": "", - "contentType": "", - "userProps": "", - "correl": "", - "expiry": "", - "broker": "200f284a1171f0a8", - "x": 730, - "y": 280, - "wires": [] - }, - { - "id": "fc63778f17491208", - "type": "change", - "z": "ed4ea3c2bc13d1ec", - "g": "b0521ee44dc796d1", - "name": "", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "payload.features", - "tot": "msg" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 500, "y": 280, "wires": [ [ - "846ac653b993bbec" + "bc210da75548aa27" ] ] }, - { - "id": "404170ec56d4c845", - "type": "function", - "z": "ed4ea3c2bc13d1ec", - "g": "b0521ee44dc796d1", - "name": "Filter", - "func": "const severeWarningEvents = [\"Severe Thunderstorm Warning\",\"Destructive Severe Thunderstorm Warning\",\"Considerable Destructive Severe Thunderstorm Warning\"]\nconst tornadoWarningEvents = [\"Tornado Warning\",\"Radar Indicated Tornado Warning\",\"Confirmed Tornado Warning\",\"Tornado Emergency\"]\nconst tornadoWatchEvents = [\"Tornado Watch\"]\nconst severeThunderstormWatchEvents = [\"Severe Thunderstorm Watch\"]\n\nlet alerts = msg.payload.features\nlet tornado_possible = false\nlet considerable_destructive = false\nlet confirmed = false\n\n// Filter for Severe Thunderstorm Warnings\n\nlet ts = alerts.filter(function(alert) {\n if (alert.properties &&\n severeWarningEvents.includes(alert.properties.event)) {\n return true\n }\n})\n\n// Filter for Severe Thunderstorm Warnings with tornado detection\n// that have a tornado possible parameter\n\nlet tstp = alerts.filter(function(alert) {\n if (alert.properties &&\n severeWarningEvents.includes(alert.properties.event) &&\n alert.properties.parameters &&\n alert.properties.parameters.tornadoDetection &&\n alert.properties.parameters.tornadoDetection.length > 0) {\n let tornadoPossible = alert.properties.parameters.tornadoDetection[0]\n return tornadoPossible === \"POSSIBLE\"\n }\n})\n\nif (tstp.length > 0) {\n tornado_possible = true\n}\n\n// Filter for considerable destructive severe thunderstorm warnings\n\nlet cdst = alerts.filter(function(alert) {\n if (alert.properties &&\n alert.properties.event === \"Considerable Destructive Severe Thunderstorm Warning\") {\n return true\n }\n})\n\nif (cdst.length > 0) {\n considerable_destructive = true\n}\n\n// Filter for Tornado Warnings\n\nlet tornado = alerts.filter(function(alert) {\n if (alert.properties &&\n tornadoWarningEvents.includes(alert.properties.event)) {\n return true\n }\n})\n\nlet confirmed_tornado = alerts.filter(function(alert) {\n if (alert.properties &&\n alert.properties.event === \"Confirmed Tornado Warning\") {\n return true\n }\n})\n\nif (confirmed_tornado.length > 0) {\n confirmed = true\n}\n\n// Filter for Tornado Watches\n\nlet tornado_watch = alerts.filter(function(alert) {\n if (alert.properties &&\n tornadoWatchEvents.includes(alert.properties.event)) {\n return true\n }\n})\n\n// Filter for Severe Thunderstorm Watches\n\nlet severe_thunderstorm_watch = alerts.filter(function(alert) {\n if (alert.properties &&\n severeThunderstormWatchEvents.includes(alert.properties.event)) {\n return true\n }\n})\n\n// If there are any alerts, return them\n\nlet tstormMsg = {\n \"payload\": {\n \"alerts\": ts,\n \"count\": ts.length,\n \"tornado_possible\": tornado_possible,\n \"considerable_destructive\": considerable_destructive\n }\n}\n\nlet tornadoWarnMsg = {\n \"payload\": {\n \"alerts\": tornado,\n \"count\": tornado.length,\n \"confirmed\": confirmed\n }\n}\n\nlet tornadoWatchMsg = {\n \"payload\": {\n \"alerts\": tornado_watch,\n \"count\": tornado_watch.length\n }\n}\n\nlet severeThunderstormWatchMsg = {\n \"payload\": {\n \"alerts\": severe_thunderstorm_watch,\n \"count\": severe_thunderstorm_watch.length\n }\n}\n\nnode.send([tstormMsg,tornadoWarnMsg,tornadoWatchMsg,severeThunderstormWatchMsg])\nnode.status({fill:'green',shape:'dot',text:'Alerts Updated'})", - "outputs": 4, - "timeout": 0, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 470, - "y": 340, - "wires": [ - [ - "9151c0cd48bb3c45" - ], - [ - "ab2e9b884521285a" - ], - [ - "6e785299ac298ccd" - ], - [ - "4d8ca3ed3f302d97" - ] - ], - "outputLabels": [ - "Severe Thunderstorm Warning", - "Tornado Warning", - null, - null - ] - }, - { - "id": "ab2e9b884521285a", - "type": "mqtt out", - "z": "ed4ea3c2bc13d1ec", - "g": "b0521ee44dc796d1", - "name": "", - "topic": "weather/alerts/defiance/tornado_warning", - "qos": "0", - "retain": "", - "respTopic": "", - "contentType": "", - "userProps": "", - "correl": "", - "expiry": "", - "broker": "200f284a1171f0a8", - "x": 780, - "y": 400, - "wires": [] - }, - { - "id": "9151c0cd48bb3c45", - "type": "mqtt out", - "z": "ed4ea3c2bc13d1ec", - "g": "b0521ee44dc796d1", - "name": "", - "topic": "weather/alerts/defiance/severe_thunderstorm_warning", - "qos": "0", - "retain": "", - "respTopic": "", - "contentType": "", - "userProps": "", - "correl": "", - "expiry": "", - "broker": "200f284a1171f0a8", - "x": 820, - "y": 340, - "wires": [] - }, { "id": "42b8b21e40f8b824", "type": "link in", @@ -503,10 +596,10 @@ "b98e25d5de4ce89c" ], "x": 185, - "y": 400, + "y": 340, "wires": [ [ - "8ac36baa32177bd6" + "bc210da75548aa27" ] ] }, @@ -565,43 +658,204 @@ "wires": [] }, { - "id": "6e785299ac298ccd", - "type": "mqtt out", + "id": "bc210da75548aa27", + "type": "subflow:0e70b91ae6570376", "z": "ed4ea3c2bc13d1ec", "g": "b0521ee44dc796d1", "name": "", - "topic": "weather/alerts/defiance/tornado_watch", - "qos": "0", - "retain": "", - "respTopic": "", - "contentType": "", - "userProps": "", - "correl": "", - "expiry": "", - "broker": "200f284a1171f0a8", - "x": 770, - "y": 460, + "x": 440, + "y": 280, "wires": [] }, { - "id": "4d8ca3ed3f302d97", - "type": "mqtt out", + "id": "bce3a3a1210ab253", + "type": "inject", "z": "ed4ea3c2bc13d1ec", - "g": "b0521ee44dc796d1", + "g": "56542cfc9d0c7a24", "name": "", - "topic": "weather/alerts/defiance/severe_thunderstorm_watch", - "qos": "0", - "retain": "", - "respTopic": "", - "contentType": "", - "userProps": "", - "correl": "", - "expiry": "", - "broker": "200f284a1171f0a8", - "x": 820, - "y": 520, + "props": [ + { + "p": "payload" + }, + { + "p": "url", + "v": "https://api.weather.gov/alerts/active/zone/KYC157", + "vt": "str" + }, + { + "p": "area", + "v": "ky_lake", + "vt": "str" + } + ], + "repeat": "30", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "", + "payloadType": "date", + "x": 130, + "y": 440, + "wires": [ + [ + "e8efcf839f012317" + ] + ] + }, + { + "id": "e8efcf839f012317", + "type": "subflow:0e70b91ae6570376", + "z": "ed4ea3c2bc13d1ec", + "g": "56542cfc9d0c7a24", + "name": "", + "x": 440, + "y": 440, "wires": [] }, + { + "id": "5a0e961755fd6b45", + "type": "link in", + "z": "ed4ea3c2bc13d1ec", + "g": "56542cfc9d0c7a24", + "name": "Weather - KY Lake Alerts Trigger", + "links": [ + "b98e25d5de4ce89c" + ], + "x": 185, + "y": 500, + "wires": [ + [ + "e8efcf839f012317" + ] + ] + }, + { + "id": "e0c88351cce4178c", + "type": "inject", + "z": "ed4ea3c2bc13d1ec", + "g": "0cd2e4517585a8c3", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "url", + "v": "https://api.weather.gov/alerts/active/zone/WIC003", + "vt": "str" + }, + { + "p": "area", + "v": "ashland_co", + "vt": "str" + } + ], + "repeat": "30", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "", + "payloadType": "date", + "x": 130, + "y": 600, + "wires": [ + [ + "ee268b5bef276f45" + ] + ] + }, + { + "id": "ee268b5bef276f45", + "type": "subflow:0e70b91ae6570376", + "z": "ed4ea3c2bc13d1ec", + "g": "0cd2e4517585a8c3", + "name": "", + "x": 440, + "y": 600, + "wires": [] + }, + { + "id": "028431d8cab86f80", + "type": "link in", + "z": "ed4ea3c2bc13d1ec", + "g": "0cd2e4517585a8c3", + "name": "Weather - KY Lake Alerts Trigger", + "links": [ + "b98e25d5de4ce89c" + ], + "x": 185, + "y": 660, + "wires": [ + [ + "ee268b5bef276f45" + ] + ] + }, + { + "id": "d2c7d572608b7d19", + "type": "inject", + "z": "ed4ea3c2bc13d1ec", + "g": "b11deb643761dfcf", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "url", + "v": "https://api.weather.gov/alerts/active/zone/WIC007", + "vt": "str" + }, + { + "p": "area", + "v": "bayfield_co", + "vt": "str" + } + ], + "repeat": "30", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "", + "payloadType": "date", + "x": 130, + "y": 760, + "wires": [ + [ + "26a089d3cbbb5ef1" + ] + ] + }, + { + "id": "26a089d3cbbb5ef1", + "type": "subflow:0e70b91ae6570376", + "z": "ed4ea3c2bc13d1ec", + "g": "b11deb643761dfcf", + "name": "", + "x": 440, + "y": 760, + "wires": [] + }, + { + "id": "eed501c9777eecb5", + "type": "link in", + "z": "ed4ea3c2bc13d1ec", + "g": "b11deb643761dfcf", + "name": "Weather - KY Lake Alerts Trigger", + "links": [ + "b98e25d5de4ce89c" + ], + "x": 185, + "y": 820, + "wires": [ + [ + "26a089d3cbbb5ef1" + ] + ] + }, { "id": "a1c109993f7dd396", "type": "mqtt in", @@ -815,7 +1069,10 @@ "mode": "link", "links": [ "42b8b21e40f8b824", - "67561d51aaa61dfa" + "5a0e961755fd6b45", + "67561d51aaa61dfa", + "028431d8cab86f80", + "eed501c9777eecb5" ], "x": 335, "y": 300,