16 Commits

Author SHA1 Message Date
8dd7771eee Remove deprecated service calls
home_automation/HA-NerdFlows#28
2025-03-21 01:28:23 -04:00
c63adaa68d Make area weather alerts reusable as a subflow 2025-03-20 00:33:31 -04:00
3c092bf519 Rename Defiance file for use as local alerts template 2025-03-20 00:04:59 -04:00
19e0e35bf4 Add watches for Defiance 2025-03-19 18:18:35 -04:00
b9a296d312 Improve filter phrases for warnings 2025-03-19 17:56:47 -04:00
503776de4a Add stats for considerable destructive t-storm, confirmed tornado 2025-03-19 17:11:17 -04:00
e37e4d7718 Clean-up severe thunderstorm and tornado alert presentation 2025-03-18 01:13:27 -04:00
2a5aa26e5c Functions for general alerts and Defiance-specific alerts 2025-03-17 01:35:05 -04:00
96c4918d76 Function for filtering weather alerts 2025-03-16 03:59:03 -04:00
b4fbc2f5e6 Continue improving basement heater logic, as the weather warms up 2025-03-11 13:36:49 -04:00
e53e6ab5f2 Improve logic for when basement heater is needed 2025-03-11 02:00:49 -04:00
4a8a9e10de Add function code from "work tomorrow" sensor flow 2025-03-09 14:22:19 -04:00
b628a0b197 Update .gitignore 2025-03-01 00:53:27 -05:00
0f6b6a8275 Fix timer not being canceled when motion is detected again 2025-02-23 22:54:49 -05:00
e23c78c971 Improve motion lighting in some rooms
Motion lighting should no longer reset lights to adaptive when scene or brightness has been changed manually
2025-02-23 01:07:31 -05:00
87ed0e89c0 Use temperature-based preset for basement heater 2025-02-17 06:19:25 -05:00
16 changed files with 373 additions and 105 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
/.vscode
**.vscode

View File

@ -13,7 +13,7 @@ if (outsideTemp > 32) {
if (nightMode === 'on') {
preset = "frost"
} else {
preset = "eco"
preset = "activity"
}
}

View File

@ -0,0 +1,33 @@
const states = global.get('homeassistant.homeAssistant.states')
const allowed = states['input_boolean.basement_studio_heat_allowed'].state
const outsideTemp = states['weather.iron_nerd_weather_station'].attributes.temperature
const todaysHighTemp = states['sensor.todays_high_temp'].state
const basementTemp = states['sensor.basement_studio_temperature'].state
let power = {}
let reason = {}
if (todaysHighTemp < 55) {
power = 'on'
reason = 'Todays High Temperature'
} else if (outsideTemp < 40) {
power = 'on'
reason = 'Outside Temperature'
} else if (basementTemp < 64) {
power = 'on'
reason = 'Basement Temperature'
} else {
power = 'off'
}
if (allowed === 'on') {
if (power === 'on') {
node.status({fill:'green',shape:'dot',text:'Heater Needed: ' + reason})
node.send(msg,null)
} else {
node.status({fill:'red',shape:'ring',text:'Heater Not Needed'})
node.send(null,msg)
}
} else {
node.status({fill:'red',shape:'ring',text:'Heat Disabled'})
}

View File

@ -53,8 +53,7 @@ node.log("Kallen Bedroom: Decision Logic Complete")
let sendFan = {
"payload": {
"domain": "fan",
"service": setFan,
"action": "fan." + setFan,
"target": {
"entity_id": ["fan.kallen_bedroom_fan"]
},
@ -64,8 +63,7 @@ let sendFan = {
let sendWhiteNoise = {
"payload": {
"domain": "input_boolean",
"service": setWhiteNoise,
"action": "input_boolean." + setWhiteNoise,
"target": {
"entity_id": ["input_boolean.white_noise_kallen_bedroom"]
},
@ -75,8 +73,7 @@ let sendWhiteNoise = {
let sendVolume = {
"payload": {
"domain": "media_player",
"service": "volume_set",
"action": "media_player.volume_set",
"target": {
"entity_id": ["media_player.kallen_bedroom_google_speaker"]
},
@ -88,8 +85,7 @@ let sendVolume = {
let sendLights = {
"payload": {
"domain": "light",
"service": setLights,
"action": "light." + setLights,
"target": {
"entity_id": ["light.kallen_bedroom_lights"]
},
@ -99,8 +95,7 @@ let sendLights = {
let sendWake = {
"payload": {
"domain": "input_boolean",
"service": setWake,
"action": "input_boolean." + setWake,
"target": {
"entity_id": ["input_boolean.kallen_awake"]
},

View File

@ -12,8 +12,7 @@ let setFan = 'turn_off'
let sendFan = {
"payload": {
"domain": "fan",
"service": setFan,
"action": "fan." + setFan,
"target": {
"entity_id": ["fan.master_bedroom_fan"]
},
@ -23,8 +22,7 @@ let sendFan = {
let sendHvac = {
"payload": {
"domain": "climate",
"service": "set_hvac_mode",
"action": "climate.set_hvac_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
},
@ -36,8 +34,7 @@ let sendHvac = {
let sendTemp = {
"payload": {
"domain": "climate",
"service": "set_temperature",
"action": "climate.set_temperature",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
},
@ -49,8 +46,7 @@ let sendTemp = {
let sendEco = {
"payload": {
"domain": "climate",
"service": "set_preset_mode",
"action": "climate.set_preset_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
},
@ -62,8 +58,7 @@ let sendEco = {
let sendAcFan = {
"payload": {
"domain": "climate",
"service": "set_fan_mode",
"action": "climate.set_fan_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
},

View File

@ -183,8 +183,7 @@ node.log("Master Bedroom Climate: Decision Logic Complete")
let sendFan = {
"payload": {
"domain": "fan",
"service": setFan,
"action": "fan." + setFan,
"target": {
"entity_id": ["fan.master_bedroom_fan"]
},
@ -194,8 +193,7 @@ let sendFan = {
let sendCool = {
"payload": {
"domain": "input_boolean",
"service": setCool,
"action": "input_boolean." + setCool,
"target": {
"entity_id": ["input_boolean.master_bedroom_cooling_on"]
},
@ -205,8 +203,7 @@ let sendCool = {
let sendSleep = {
"payload": {
"domain": "input_boolean",
"service": setSleep,
"action": "input_boolean." + setSleep,
"target": {
"entity_id": ["input_boolean.master_bedroom_sleeping"]
},
@ -216,8 +213,7 @@ let sendSleep = {
let sendPeople = {
"payload": {
"domain": "input_boolean",
"service": setPeople,
"action": "input_boolean." + setPeople,
"target": {
"entity_id": ["input_boolean.tony_awake","input_boolean.tina_awake"]
},
@ -227,8 +223,7 @@ let sendPeople = {
let sendDisplay = {
"payload": {
"domain": "switch",
"service": setDisplay,
"action": "switch." + setDisplay,
"target": {
"entity_id": ["switch.master_bedroom_aircon_display"]
},
@ -245,16 +240,14 @@ let notify = {
let sendBriefing = {
"payload": {
"domain": "script",
"service": setBriefing
"action": "script." + setBriefing,
},
"delay": setBriefingDelay
}
let sendHvac = {
"payload": {
"domain": "climate",
"service": "set_hvac_mode",
"action": "climate.set_hvac_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
},
@ -266,8 +259,7 @@ let sendHvac = {
let sendTemp = {
"payload": {
"domain": "climate",
"service": "set_temperature",
"action": "climate.set_temperature",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
},
@ -279,8 +271,7 @@ let sendTemp = {
let sendEco = {
"payload": {
"domain": "climate",
"service": "set_preset_mode",
"action": "climate.set_preset_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
},
@ -292,8 +283,7 @@ let sendEco = {
let sendAcFan = {
"payload": {
"domain": "climate",
"service": "set_fan_mode",
"action": "climate.set_fan_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
},
@ -305,8 +295,7 @@ let sendAcFan = {
let sendEchoDotDND = {
"payload": {
"domain": "switch",
"service": echoDotService,
"action": "switch." + echoDotService,
"target": {
"entity_id": ["switch.basement_echo_dot_do_not_disturb_switch"]
},

View File

@ -7,17 +7,22 @@ const payload = msg.payload
const newDuration = duration * 60
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && lights === 'off') {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -1,5 +1,6 @@
const states = global.get('homeassistant.homeAssistant.states')
const lights = states['light.mud_room_overhead'].state
const selScene = states['input_text.mud_room_selected_scene'].state
const duration = states['input_number.mud_room_lights_off_delay'].state
const lux = parseInt(states['sensor.mud_room_illuminance'].state)
const threshold = parseInt(states['input_number.mud_room_lux_threshold'].state)
@ -7,17 +8,22 @@ const payload = msg.payload
const newDuration = duration * 60
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && (lights === 'off' || selScene === 'Nightlight')) {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -68,8 +68,7 @@ if (payload === 'start') {
// Prepare message payloads
let sendTimerStart = {
"payload": {
"domain": "timer",
"service": "start",
"action": "timer.start",
"target": {
"entity_id": timerEntity
},
@ -81,8 +80,7 @@ let sendTimerStart = {
let sendTimerCancel = {
"payload": {
"domain": "timer",
"service": "cancel",
"action": "timer.cancel",
"target": {
"entity_id": timerEntity
},
@ -92,8 +90,7 @@ let sendTimerCancel = {
let sendBoolFinished = {
"payload": {
"domain": "input_boolean",
"service": servFinished,
"action": "input_boolean." + servFinished,
"target": {
"entity_id": boolFinished
},
@ -103,8 +100,7 @@ let sendBoolFinished = {
let sendDateTimeFinished = {
"payload": {
"domain": "input_datetime",
"service": "set_datetime",
"action": "input_datetime.set_datetime",
"target": {
"entity_id": dateTimeEntity
},

View File

@ -9,17 +9,22 @@ const newDuration = duration * 60
if (sleeping === 'off') {
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && lights === 'off') {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -9,17 +9,22 @@ const newDuration = duration * 60
if (sleeping === 'off') {
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && lights === 'off') {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -12,17 +12,22 @@ const newDuration = duration * 60
if (peopleSleeping === false && nightMode === 'off') {
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && lights === 'off') {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -127,8 +127,7 @@ node.log("Time-based Automations: Decision Logic Complete")
// ---------- Service Calls ----------
let sendLights = {
"payload": {
"domain": "light",
"service": "turn_off",
"action": "light.turn_off",
"target": {
"entity_id": lightsOff
},
@ -138,8 +137,7 @@ let sendLights = {
let sendSleepOff = {
"payload": {
"domain": "switch",
"service": "turn_off",
"action": "switch.turn_off",
"target": {
"entity_id": adaptiveSleep
},
@ -149,8 +147,7 @@ let sendSleepOff = {
let sendBooleanOff = {
"payload": {
"domain": "input_boolean",
"service": "turn_off",
"action": "input_boolean.turn_off",
"target": {
"entity_id": booleanOff
},
@ -160,8 +157,7 @@ let sendBooleanOff = {
let sendAdaptive = {
"payload": {
"domain": "switch",
"service": "turn_" + setAdaptive,
"action": "switch.turn_" + setAdaptive,
"target": {
"entity_id": switchAdaptive
},
@ -171,8 +167,7 @@ let sendAdaptive = {
let sendSceneResetMain = {
"payload": {
"domain": "input_text",
"service": "set_value",
"action": "input_text.set_value",
"target": {
"entity_id": selScenesMain
},
@ -184,8 +179,7 @@ let sendSceneResetMain = {
let sendSceneResetDesk = {
"payload": {
"domain": "input_text",
"service": "set_value",
"action": "input_text.set_value",
"target": {
"entity_id": selScenesTinaDesk
},
@ -197,8 +191,7 @@ let sendSceneResetDesk = {
let sendHoliday = {
"payload": {
"domain": "switch",
"service": "turn_" + setHoliday,
"action": "switch.turn_" + setHoliday,
"target": {
"entity_id": switchHoliday
},
@ -208,8 +201,7 @@ let sendHoliday = {
let sendTimer = {
"payload": {
"domain": "timer",
"service": "start",
"action": "timer.start",
"target": {
"entity_id": timerEntity
},
@ -221,8 +213,7 @@ let sendTimer = {
let sendTimerCancel = {
"payload": {
"domain": "timer",
"service": "cancel",
"action": "timer.cancel",
"target": {
"entity_id": timerEntity
},
@ -232,8 +223,7 @@ let sendTimerCancel = {
let sendNotifyPhone = {
"payload": {
"domain": "script",
"service": "text_notify",
"action": "script.text_notify",
"data": {
"who": "all",
"title": notifyTitle,
@ -246,8 +236,7 @@ let sendNotifyPhone = {
let sendNotifyTV = {
"payload": {
"domain": "script",
"service": "tv_notify",
"action": "script.tv_notify",
"data": {
"who": "all",
"title": notifyTitle,
@ -263,8 +252,7 @@ let sendNotifyTV = {
let sendFirstFloorScene = {
"payload": {
"domain": "script",
"service": "evening_on_first_floor",
"action": "script.evening_on_first_floor",
"data": {
"sunset_lights": 1
}
@ -273,8 +261,7 @@ let sendFirstFloorScene = {
let sendSecondFloorScene = {
"payload": {
"domain": "script",
"service": "evening_on_second_floor",
"action": "script.evening_on_second_floor",
"data": {
"sunset_lights": 1
}
@ -283,8 +270,7 @@ let sendSecondFloorScene = {
let sendDeskScene = {
"payload": {
"domain": "input_select",
"service": "select_option",
"action": "input_select.select_option",
"target": {
"entity_id": ["input_select.tina_desk_scenes"]
},

View File

@ -0,0 +1,26 @@
let tomorrow = msg.tomorrow
let today = msg.today
let number = {}
let work_tomorrow = {}
if (tomorrow > 0) {
work_tomorrow = "true"
if (today == 0) {
number = 0
} else {
number = 1
}
} else {
work_tomorrow = "false"
number = 0
}
msg.work_tomorrow = work_tomorrow
node.status({fill:"green",shape:"dot",text:"Number " + number})
if (number == 0) {
node.send([msg,null])
} else {
node.send([null,msg])
}

74
weather/alerts_filter.js Normal file
View File

@ -0,0 +1,74 @@
const severeWarningEvents = ["Severe Thunderstorm Warning","Destructive Severe Thunderstorm Warning","Considerable Destructive Severe Thunderstorm Warning"]
const tornadoWarningEvents = ["Tornado Warning","Radar Indicated Tornado Warning","Confirmed Tornado Warning","Tornado Emergency"]
let alerts = msg.payload
// Filter for Severe Thunderstorm Warnings
let ts = alerts.filter(function(alert) {
if (alert.raw.properties &&
severeWarningEvents.includes(alert.raw.properties.event)) {
return true
}
})
// Filter for Severe Thunderstorm Warnings with tornado detection
// that have a tornado possible parameter
let tstp = alerts.filter(function(alert) {
if (alert.raw.properties &&
severeWarningEvents.includes(alert.raw.properties.event) &&
alert.raw.properties.parameters &&
alert.raw.properties.parameters.tornadoDetection &&
alert.raw.properties.parameters.tornadoDetection.length > 0) {
let tornadoPossible = alert.raw.properties.parameters.tornadoDetection[0]
return tornadoPossible === "POSSIBLE"
}
})
// Filter for considerable destructive severe thunderstorm warnings
let cdst = alerts.filter(function(alert) {
if (alert.raw.properties &&
alert.raw.properties.event === "Considerable Destructive Severe Thunderstorm Warning") {
return true
}
})
// Filter for Tornado Warnings
let tornado = alerts.filter(function(alert) {
if (alert.raw.properties &&
tornadoWarningEvents.includes(alert.raw.properties.event)) {
return true
}
})
let confirmed_tornado = alerts.filter(function(alert) {
if (alert.raw.properties &&
alert.raw.properties.event === "Confirmed Tornado Warning") {
return true
}
})
// If there are any alerts, return them
let tstormMsg = {
"payload": {
"alerts": ts,
"count": ts.length,
"tornado_possible": tstp.length,
"considerable_destructive": cdst.length
}
}
let tornadoWarnMsg = {
"payload": {
"alerts": tornado,
"count": tornado.length,
"confirmed": confirmed_tornado.length
}
}
node.send([tstormMsg,tornadoWarnMsg])
node.status({fill:'green',shape:'dot',text:'Alerts Updated'})

148
weather/local_alerts.js Normal file
View File

@ -0,0 +1,148 @@
const severeWarningEvents = ["Severe Thunderstorm Warning","Destructive Severe Thunderstorm Warning","Considerable Destructive Severe Thunderstorm Warning"]
const tornadoWarningEvents = ["Tornado Warning","Radar Indicated Tornado Warning","Confirmed Tornado Warning","Tornado Emergency"]
const tornadoWatchEvents = ["Tornado Watch"]
const severeThunderstormWatchEvents = ["Severe Thunderstorm Watch"]
const area = msg.area
const mqttTopic = "weather/alerts/" + area
const tstormTopic = mqttTopic + "/severe_thunderstorm_warning"
const tornadoWarnTopic = mqttTopic + "/tornado_warning"
const tornadoWatchTopic = mqttTopic + "/tornado_watch"
const severeThunderstormWatchTopic = mqttTopic + "/severe_thunderstorm_watch"
let alerts = msg.payload.features
let tornado_possible = false
let considerable_destructive = false
let confirmed = false
// Filter for Severe Thunderstorm Warnings
let ts = alerts.filter(function(alert) {
if (alert.properties &&
severeWarningEvents.includes(alert.properties.event)) {
return true
}
})
// Filter for Severe Thunderstorm Warnings with tornado detection
// that have a tornado possible parameter
let tstp = alerts.filter(function(alert) {
if (alert.properties &&
severeWarningEvents.includes(alert.properties.event) &&
alert.properties.parameters &&
alert.properties.parameters.tornadoDetection &&
alert.properties.parameters.tornadoDetection.length > 0) {
let tornadoPossible = alert.properties.parameters.tornadoDetection[0]
return tornadoPossible === "POSSIBLE"
}
})
if (tstp.length > 0) {
tornado_possible = true
}
// Filter for considerable destructive severe thunderstorm warnings
let cdst = alerts.filter(function(alert) {
if (alert.properties &&
alert.properties.event === "Considerable Destructive Severe Thunderstorm Warning") {
return true
}
})
if (cdst.length > 0) {
considerable_destructive = true
}
// Filter for Tornado Warnings
let tornado = alerts.filter(function(alert) {
if (alert.properties &&
tornadoWarningEvents.includes(alert.properties.event)) {
return true
}
})
let confirmed_tornado = alerts.filter(function(alert) {
if (alert.properties &&
alert.properties.event === "Confirmed Tornado Warning") {
return true
}
})
if (confirmed_tornado.length > 0) {
confirmed = true
}
// Filter for Tornado Watches
let tornado_watch = alerts.filter(function(alert) {
if (alert.properties &&
tornadoWatchEvents.includes(alert.properties.event)) {
return true
}
})
// Filter for Severe Thunderstorm Watches
let severe_thunderstorm_watch = alerts.filter(function(alert) {
if (alert.properties &&
severeThunderstormWatchEvents.includes(alert.properties.event)) {
return true
}
})
// If there are any alerts, return them
let allAlerts = {
"payload": msg.payload.features
}
let tstormMsg = {
"payload": {
"alerts": ts,
"count": ts.length,
"tornado_possible": tornado_possible,
"considerable_destructive": considerable_destructive
},
"topic": tstormTopic
}
let tornadoWarnMsg = {
"payload": {
"alerts": tornado,
"count": tornado.length,
"confirmed": confirmed
},
"topic": tornadoWarnTopic
}
let tornadoWatchMsg = {
"payload": {
"alerts": tornado_watch,
"count": tornado_watch.length
},
"topic": tornadoWatchTopic
}
let severeThunderstormWatchMsg = {
"payload": {
"alerts": severe_thunderstorm_watch,
"count": severe_thunderstorm_watch.length
},
"topic": severeThunderstormWatchTopic
}
// Create status message for node
let statusMsg = {
"status": {
"fill": "green",
"shape": "dot",
"text": alerts.length + " alerts processed at " + new Date().toLocaleString()
}
}
// Send messages to output nodes
node.send([allAlerts,tstormMsg,tornadoWarnMsg,tornadoWatchMsg,severeThunderstormWatchMsg,statusMsg])