Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save okets/c7baea968aab019a09c9d2c80df29d40 to your computer and use it in GitHub Desktop.
Save okets/c7baea968aab019a09c9d2c80df29d40 to your computer and use it in GitHub Desktop.
iOS Subflow for HomeAssistant Companion notifications
[
{
"id":"914734122ce79fcd",
"type":"subflow",
"name":"iOS Actionable Notification",
"info":"[Documentation](https://github.com/zachowj/node-red-contrib-home-assistant-websocket/discussions/379)\n",
"category":"home_assistant",
"in":[
{
"x":60,
"y":60,
"wires":[{"id":"f8d958779acdc007"}]
}
],
"out":[
{
"x":1440,
"y":160,
"wires":[{"id":"7bb055c340123ca6","port":0}]
},
{
"x":1440,
"y":200,
"wires":[{"id":"7bb055c340123ca6","port":1}]
},
{
"x":1440,
"y":240,
"wires":[{"id":"7bb055c340123ca6","port":2}]
},
{
"x":1440,
"y":280,
"wires":[{"id":"7bb055c340123ca6","port":3}]
}
],
"env":[
{
"name":"group",
"type":"str",
"value":"None",
"ui":{"label":{"en-US":"Group Name"}}
},
{
"name":"tag",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Tag (for replacing messages - optional)"
}
}
},
{
"name":"service",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Notify Service"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"title",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Title"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"subtitle",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Subtitle (optional)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"message",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Message"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"notificationUrl",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Notification URL (optional)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"customSoundPreInstalled",
"type":"str",
"value":"default",
"ui":{
"label":{"en-US":"Pre-installed Custom Sound"},
"type":"select",
"opts":{
"opts":[
{"l":{"en-US":"default"},"v":"default"},
{"l":{"en-US":"No Sound"},"v":"none"},
{
"l":{
"en-US":"US-EN-Alexa-Back-Door-Opened.wav"
},
"v":"US-EN-Alexa-Back-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Back-Door-Unlocked.wav"
},
"v":"US-EN-Alexa-Back-Door-Unlocked.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Basement-Door-Opened.wav"
},
"v":"US-EN-Alexa-Basement-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Basement-Door-Unlocked.wav"
},
"v":"US-EN-Alexa-Basement-Door-Unlocked.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Boyfriend-Is-Arriving.wav"
},
"v":"US-EN-Alexa-Boyfriend-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Daughter-Is-Arriving.wav"
},
"v":"US-EN-Alexa-Daughter-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Front-Door-Opened.wav"
},
"v":"US-EN-Alexa-Front-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Front-Door-Unlocked.wav"
},
"v":"US-EN-Alexa-Front-Door-Unlocked.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Garage-Door-Opened.wav"
},
"v":"US-EN-Alexa-Garage-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Girlfriend-Is-Arriving.wav"
},
"v":"US-EN-Alexa-Girlfriend-Is-Arriving.wav"
},
{
"l":{"en-US":"US-EN-Alexa-Good-Morning.wav"},
"v":"US-EN-Alexa-Good-Morning.wav"
},
{
"l":{"en-US":"US-EN-Alexa-Good-Night.wav"},
"v":"US-EN-Alexa-Good-Night.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Husband-Is-Arriving.wav"
},
"v":"US-EN-Alexa-Husband-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Mail-Has-Arrived.wav"
},
"v":"US-EN-Alexa-Mail-Has-Arrived.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Motion-At-Back-Door.wav"
},
"v":"US-EN-Alexa-Motion-At-Back-Door.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Motion-At-Front-Door.wav"
},
"v":"US-EN-Alexa-Motion-At-Front-Door.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Motion-Detected-Generic.wav"
},
"v":"US-EN-Alexa-Motion-Detected-Generic.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Motion-In-Back-Yard.wav"
},
"v":"US-EN-Alexa-Motion-In-Back-Yard.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Motion-In-Basement.wav"
},
"v":"US-EN-Alexa-Motion-In-Basement.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Motion-In-Front-Yard.wav"
},
"v":"US-EN-Alexa-Motion-In-Front-Yard.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Motion-In-Garage.wav"
},
"v":"US-EN-Alexa-Motion-In-Garage.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Patio-Door-Opened.wav"
},
"v":"US-EN-Alexa-Patio-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Patio-Door-Unlocked.wav"
},
"v":"US-EN-Alexa-Patio-Door-Unlocked.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Smoke-Detected-Generic.wav"
},
"v":"US-EN-Alexa-Smoke-Detected-Generic.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Smoke-Detected-In-Basement.wav"
},
"v":"US-EN-Alexa-Smoke-Detected-In-Basement.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Smoke-Detected-In-Garage.wav"
},
"v":"US-EN-Alexa-Smoke-Detected-In-Garage.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Smoke-Detected-In-Kitchen.wav"
},
"v":"US-EN-Alexa-Smoke-Detected-In-Kitchen.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Son-Is-Arriving.wav"
},
"v":"US-EN-Alexa-Son-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Water-Detected-Generic.wav"
},
"v":"US-EN-Alexa-Water-Detected-Generic.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Water-Detected-In-Basement.wav"
},
"v":"US-EN-Alexa-Water-Detected-In-Basement.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Water-Detected-In-Garage.wav"
},
"v":"US-EN-Alexa-Water-Detected-In-Garage.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Water-Detected-In-Kitchen.wav"
},
"v":"US-EN-Alexa-Water-Detected-In-Kitchen.wav"
},
{
"l":{"en-US":"US-EN-Alexa-Welcome-Home.wav"},
"v":"US-EN-Alexa-Welcome-Home.wav"
},
{
"l":{
"en-US":"US-EN-Alexa-Wife-Is-Arriving.wav"
},
"v":"US-EN-Alexa-Wife-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Back-Door-Motion.wav"
},
"v":"US-EN-Daisy-Back-Door-Motion.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Back-Door-Open.wav"
},
"v":"US-EN-Daisy-Back-Door-Open.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Front-Door-Motion.wav"
},
"v":"US-EN-Daisy-Front-Door-Motion.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Front-Door-Open.wav"
},
"v":"US-EN-Daisy-Front-Door-Open.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Front-Window-Open.wav"
},
"v":"US-EN-Daisy-Front-Window-Open.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Garage-Door-Open.wav"
},
"v":"US-EN-Daisy-Garage-Door-Open.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Guest-Bath-Leak.wav"
},
"v":"US-EN-Daisy-Guest-Bath-Leak.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Kitchen-Sink-Leak.wav"
},
"v":"US-EN-Daisy-Kitchen-Sink-Leak.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Kitchen-Window-Open.wav"
},
"v":"US-EN-Daisy-Kitchen-Window-Open.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Laundry-Room-Leak.wav"
},
"v":"US-EN-Daisy-Laundry-Room-Leak.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Master-Bath-Leak.wav"
},
"v":"US-EN-Daisy-Master-Bath-Leak.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Master-Bedroom-Window-Open.wav"
},
"v":"US-EN-Daisy-Master-Bedroom-Window-Open.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Office-Window-Open.wav"
},
"v":"US-EN-Daisy-Office-Window-Open.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Refrigerator-Leak.wav"
},
"v":"US-EN-Daisy-Refrigerator-Leak.wav"
},
{
"l":{
"en-US":"US-EN-Daisy-Water-Heater-Leak.wav"
},
"v":"US-EN-Daisy-Water-Heater-Leak.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Back-Door-Closed.wav"
},
"v":"US-EN-Morgan-Freeman-Back-Door-Closed.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Back-Door-Locked.wav"
},
"v":"US-EN-Morgan-Freeman-Back-Door-Locked.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Back-Door-Opened.wav"
},
"v":"US-EN-Morgan-Freeman-Back-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Back-Door-Unlocked.wav"
},
"v":"US-EN-Morgan-Freeman-Back-Door-Unlocked.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Basement-Door-Closed.wav"
},
"v":"US-EN-Morgan-Freeman-Basement-Door-Closed.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Basement-Door-Locked.wav"
},
"v":"US-EN-Morgan-Freeman-Basement-Door-Locked.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Basement-Door-Opened.wav"
},
"v":"US-EN-Morgan-Freeman-Basement-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Basement-Door-Unlocked.wav"
},
"v":"US-EN-Morgan-Freeman-Basement-Door-Unlocked.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Boss-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Boss-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Boyfriend-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Boyfriend-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Cleaning-Supplies-Closet-Opened.wav"
},
"v":"US-EN-Morgan-Freeman-Cleaning-Supplies-Closet-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Coworker-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Coworker-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Daughter-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Daughter-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Friend-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Friend-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Front-Door-Closed.wav"
},
"v":"US-EN-Morgan-Freeman-Front-Door-Closed.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Front-Door-Locked.wav"
},
"v":"US-EN-Morgan-Freeman-Front-Door-Locked.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Front-Door-Opened.wav"
},
"v":"US-EN-Morgan-Freeman-Front-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Front-Door-Unlocked.wav"
},
"v":"US-EN-Morgan-Freeman-Front-Door-Unlocked.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Garage-Door-Closed.wav"
},
"v":"US-EN-Morgan-Freeman-Garage-Door-Closed.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Garage-Door-Opened.wav"
},
"v":"US-EN-Morgan-Freeman-Garage-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Girlfriend-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Girlfriend-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Good-Morning.wav"
},
"v":"US-EN-Morgan-Freeman-Good-Morning.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Good-Night.wav"
},
"v":"US-EN-Morgan-Freeman-Good-Night.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Liquor-Cabinet-Opened.wav"
},
"v":"US-EN-Morgan-Freeman-Liquor-Cabinet-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-Detected.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-Detected.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-In-Basement.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-In-Basement.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-In-Bedroom.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-In-Bedroom.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-In-Game-Room.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-In-Game-Room.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-In-Garage.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-In-Garage.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-In-Kitchen.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-In-Kitchen.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-In-Living-Room.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-In-Living-Room.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-In-Theater.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-In-Theater.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Motion-In-Wine-Cellar.wav"
},
"v":"US-EN-Morgan-Freeman-Motion-In-Wine-Cellar.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Patio-Door-Closed.wav"
},
"v":"US-EN-Morgan-Freeman-Patio-Door-Closed.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Patio-Door-Locked.wav"
},
"v":"US-EN-Morgan-Freeman-Patio-Door-Locked.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Patio-Door-Opened.wav"
},
"v":"US-EN-Morgan-Freeman-Patio-Door-Opened.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Patio-Door-Unlocked.wav"
},
"v":"US-EN-Morgan-Freeman-Patio-Door-Unlocked.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Searching-For-Car-Keys.wav"
},
"v":"US-EN-Morgan-Freeman-Searching-For-Car-Keys.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Setting-The-Mood.wav"
},
"v":"US-EN-Morgan-Freeman-Setting-The-Mood.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Smartthings-Detected-A-Flood.wav"
},
"v":"US-EN-Morgan-Freeman-Smartthings-Detected-A-Flood.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Smartthings-Detected-Carbon-Monoxide.wav"
},
"v":"US-EN-Morgan-Freeman-Smartthings-Detected-Carbon-Monoxide.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Smartthings-Detected-Smoke.wav"
},
"v":"US-EN-Morgan-Freeman-Smartthings-Detected-Smoke.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Smoke-Detected-In-Basement.wav"
},
"v":"US-EN-Morgan-Freeman-Smoke-Detected-In-Basement.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Smoke-Detected-In-Garage.wav"
},
"v":"US-EN-Morgan-Freeman-Smoke-Detected-In-Garage.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Smoke-Detected-In-Kitchen.wav"
},
"v":"US-EN-Morgan-Freeman-Smoke-Detected-In-Kitchen.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Someone-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Someone-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Son-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Son-Is-Arriving.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Starting-Movie-Mode.wav"
},
"v":"US-EN-Morgan-Freeman-Starting-Movie-Mode.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Starting-Party-Mode.wav"
},
"v":"US-EN-Morgan-Freeman-Starting-Party-Mode.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Starting-Romance-Mode.wav"
},
"v":"US-EN-Morgan-Freeman-Starting-Romance-Mode.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-All-The-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-All-The-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Air-Conditioner.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Air-Conditioner.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Bar-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Bar-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Chandelier.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Chandelier.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Family-Room-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Family-Room-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Hallway-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Hallway-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Kitchen-Light.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Kitchen-Light.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Light.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Light.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-Mood-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-Mood-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-Off-The-TV.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-Off-The-TV.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Air-Conditioner.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Air-Conditioner.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Bar-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Bar-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Chandelier.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Chandelier.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Family-Room-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Family-Room-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Hallway-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Hallway-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Kitchen-Light.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Kitchen-Light.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Light.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Light.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-Mood-Lights.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-Mood-Lights.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Turning-On-The-TV.wav"
},
"v":"US-EN-Morgan-Freeman-Turning-On-The-TV.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Vacate-The-Premises.wav"
},
"v":"US-EN-Morgan-Freeman-Vacate-The-Premises.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Water-Detected-In-Basement.wav"
},
"v":"US-EN-Morgan-Freeman-Water-Detected-In-Basement.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Water-Detected-In-Garage.wav"
},
"v":"US-EN-Morgan-Freeman-Water-Detected-In-Garage.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Water-Detected-In-Kitchen.wav"
},
"v":"US-EN-Morgan-Freeman-Water-Detected-In-Kitchen.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Welcome-Home.wav"
},
"v":"US-EN-Morgan-Freeman-Welcome-Home.wav"
},
{
"l":{
"en-US":"US-EN-Morgan-Freeman-Wife-Is-Arriving.wav"
},
"v":"US-EN-Morgan-Freeman-Wife-Is-Arriving.wav"
}
]
}
}
},
{
"name":"customSound",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Custom Sound (Optional - will override pre-installed selection)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"interruptionLevel",
"type":"str",
"value":"active",
"ui":{
"label":{"en-US":"Interruption Level"},
"type":"select",
"opts":{
"opts":[
{"l":{"en-US":"Passive"},"v":"passive"},
{
"l":{"en-US":"Active (Default)"},
"v":"active"
},
{
"l":{"en-US":"Time Sensitive"},
"v":"time-sensitive"
},
{"l":{"en-US":"Critical"},"v":"critical"}
]
}
}
},
{
"name":"userInfo",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Populate User Information"},
"type":"checkbox"
}
},
{
"name":"isClearNotificationsOnAction",
"type":"bool",
"value":"false",
"ui":{
"label":{
"en-US":"Clear notifications on Action Received?"
},
"type":"checkbox"
}
},
{
"name":"action1Title",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 1 Title"},
"type":"input",
"opts":{"types":["str"]}
}
},
{
"name":"action1Uri",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 1 URL (optional)"},
"type":"input",
"opts":{"types":["str"]}
}
},
{
"name":"action1ActivationMode",
"type":"str",
"value":"background",
"ui":{
"label":{"en-US":"Action 1 Activation Mode"},
"type":"select",
"opts":{
"opts":[
{
"l":{"en-US":"Background"},
"v":"background"
},
{
"l":{"en-US":"Foreground"},
"v":"foreground"
}
]
}
}
},
{
"name":"action1AuthenticationRequired",
"type":"bool",
"value":"false",
"ui":{
"label":{
"en-US":"Action 1 App Authentication Required?"
},
"type":"checkbox"
}
},
{
"name":"action1Destructive",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Action 1 Destructive?"},
"type":"checkbox"
}
},
{
"name":"action1Behavior",
"type":"str",
"value":"default",
"ui":{
"label":{"en-US":"Action 1 Behavior"},
"type":"select",
"opts":{
"opts":[
{"l":{"en-US":"Default"},"v":"default"},
{
"l":{"en-US":"Text Input"},
"v":"textInput"
}
]
}
}
},
{
"name":"action1TextInputButtonTitle",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Action 1 Text Input Button Title (Required if behavior is Text Input)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action1TextInputPlaceHolder",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Action 1 Text Input Placeholder (Optional - only if behavior is Text Input)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action1Icon",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 1 Icon (Optional)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action2Title",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 2 Title"},
"type":"input",
"opts":{"types":["str"]}
}
},
{
"name":"action2Uri",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 2 URL (optional)"},
"type":"input",
"opts":{"types":["str"]}
}
},
{
"name":"action2ActivationMode",
"type":"str",
"value":"background",
"ui":{
"label":{"en-US":"Action 2 Activation Mode"},
"type":"select",
"opts":{
"opts":[
{
"l":{"en-US":"Background"},
"v":"background"
},
{
"l":{"en-US":"Foreground"},
"v":"foreground"
}
]
}
}
},
{
"name":"action2AuthenticationRequired",
"type":"bool",
"value":"false",
"ui":{
"label":{
"en-US":"Action 2 App Authentication Required?"
},
"type":"checkbox"
}
},
{
"name":"action2Destructive",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Action 2 Destructive?"},
"type":"checkbox"
}
},
{
"name":"action2Behavior",
"type":"str",
"value":"default",
"ui":{
"label":{"en-US":"Action 2 Behavior"},
"type":"select",
"opts":{
"opts":[
{"l":{"en-US":"Default"},"v":"default"},
{
"l":{"en-US":"Text Input"},
"v":"textInput"
}
]
}
}
},
{
"name":"action2TextInputButtonTitle",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Action 2 Text Input Button Title (Required if behavior is Text Input)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action2TextInputPlaceHolder",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Action 2 Text Input Placeholder (Optional - only if behavior is Text Input)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action2Icon",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 2 Icon (Optional)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action3Title",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 3 Title"},
"type":"input",
"opts":{"types":["str"]}
}
},
{
"name":"action3Uri",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 3 URL (optional)"},
"type":"input",
"opts":{"types":["str"]}
}
},
{
"name":"action3ActivationMode",
"type":"str",
"value":"background",
"ui":{
"label":{"en-US":"Action 3 Activation Mode"},
"type":"select",
"opts":{
"opts":[
{
"l":{"en-US":"Background"},
"v":"background"
},
{
"l":{"en-US":"Foreground"},
"v":"foreground"
}
]
}
}
},
{
"name":"action3AuthenticationRequired",
"type":"bool",
"value":"false",
"ui":{
"label":{
"en-US":"Action 3 App Authentication Required?"
},
"type":"checkbox"
}
},
{
"name":"action3Destructive",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Action 3 Destructive?"},
"type":"checkbox"
}
},
{
"name":"action3Behavior",
"type":"str",
"value":"default",
"ui":{
"label":{"en-US":"Action 3 Behavior"},
"type":"select",
"opts":{
"opts":[
{"l":{"en-US":"Default"},"v":"default"},
{
"l":{"en-US":"Text Input"},
"v":"textInput"
}
]
}
}
},
{
"name":"action3TextInputButtonTitle",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Action 3 Text Input Button Title (Required if behavior is Text Input)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action3TextInputPlaceHolder",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Action 3 Text Input Placeholder (Optional - only if behavior is Text Input)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action3Icon",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 3 Icon (Optional)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action4Title",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 4 Title"},
"type":"input",
"opts":{"types":["str"]}
}
},
{
"name":"action4Uri",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 4 URL (optional)"},
"type":"input",
"opts":{"types":["str"]}
}
},
{
"name":"action4ActivationMode",
"type":"str",
"value":"background",
"ui":{
"label":{"en-US":"Action 4 Activation Mode"},
"type":"select",
"opts":{
"opts":[
{
"l":{"en-US":"Background"},
"v":"background"
},
{
"l":{"en-US":"Foreground"},
"v":"foreground"
}
]
}
}
},
{
"name":"action4AuthenticationRequired",
"type":"bool",
"value":"false",
"ui":{
"label":{
"en-US":"Action 4 App Authentication Required?"
},
"type":"checkbox"
}
},
{
"name":"action4Destructive",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Action 4 Destructive?"},
"type":"checkbox"
}
},
{
"name":"action4Behavior",
"type":"str",
"value":"default",
"ui":{
"label":{"en-US":"Action 4 Behavior"},
"type":"select",
"opts":{
"opts":[
{"l":{"en-US":"Default"},"v":"default"},
{
"l":{"en-US":"Text Input"},
"v":"textInput"
}
]
}
}
},
{
"name":"action4TextInputButtonTitle",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Action 4 Text Input Button Title (Required if behavior is Text Input)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action4TextInputPlaceHolder",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Action 4 Text Input Placeholder (Optional - only if behavior is Text Input)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"action4Icon",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Action 4 Icon (Optional)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"latitudeFirst",
"type":"num",
"value":"",
"ui":{
"label":{"en-US":"Latitude for Pin 1"},
"type":"input",
"opts":{"types":["num","env"]}
}
},
{
"name":"longitudeFirst",
"type":"num",
"value":"",
"ui":{
"label":{"en-US":"Longitude for Pin 1"},
"type":"input",
"opts":{"types":["num","env"]}
}
},
{
"name":"latitudeSecond",
"type":"num",
"value":"",
"ui":{
"label":{"en-US":"Latitude for Pin 2"},
"type":"input",
"opts":{"types":["num","env"]}
}
},
{
"name":"longitudeSecond",
"type":"num",
"value":"",
"ui":{
"label":{"en-US":"Longitude for Pin 2"},
"type":"input",
"opts":{"types":["num","env"]}
}
},
{
"name":"showLineBetweenPoints",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Show a line between points?"},
"type":"input",
"opts":{"types":["bool","env"]}
}
},
{
"name":"showCompass",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Show a compass on the map?"},
"type":"input",
"opts":{"types":["bool","env"]}
}
},
{
"name":"showPointsOfInterest",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Show points of interest?"},
"type":"input",
"opts":{"types":["bool","env"]}
}
},
{
"name":"showScale",
"type":"bool",
"value":"false",
"ui":{
"label":{
"en-US":"Show scale information on the map?"
},
"type":"input",
"opts":{"types":["bool","env"]}
}
},
{
"name":"showTraffic",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Show Traffic?"},
"type":"input",
"opts":{"types":["bool","env"]}
}
},
{
"name":"showUserLocation",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Show User Location?"},
"type":"input",
"opts":{"types":["bool","env"]}
}
},
{
"name":"cameraEntity",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Camera Entity"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"imagePath",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Image Path (10MB limit)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"videoPath",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Video Path (50MB limit)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"audioPath",
"type":"str",
"value":"",
"ui":{
"label":{"en-US":"Audio Path (5MB limit)"},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"contentUrl",
"type":"str",
"value":"",
"ui":{
"label":{
"en-US":"Content URL (overrides image/video/audio)"
},
"type":"input",
"opts":{"types":["str","env"]}
}
},
{
"name":"lazyLoading",
"type":"bool",
"value":"false",
"ui":{
"label":{
"en-US":"Load Media in Notification Lazily?"
},
"type":"input",
"opts":{"types":["bool","env"]}
}
},
{
"name":"hideThumbnail",
"type":"bool",
"value":"false",
"ui":{
"label":{"en-US":"Hide thumbnail?"},
"type":"input",
"opts":{"types":["bool","env"]}
}
}
],
"meta":{},
"color":"#D8BFD8",
"outputLabels":[
"Action 1",
"Action 2",
"Action 3",
"Cleared"
],
"icon":"font-awesome/fa-mobile-phone",
"status":{
"x":200,
"y":320,
"wires":[{"id":"f87ecb61310bc411","port":0}]
}
},
{
"id":"0c74efbb9a85bee5",
"type":"function",
"z":"914734122ce79fcd",
"name":"create service call",
"func":"msg._originalPayload = msg.payload;\nlet flow_msg_variables = {\n tag: '',\n service: '',\n message: msg,\n date_created: Date.now()\n}\n\n//flow.set('latestMessage', msg);\n\nvar xTitle = \"\";\nvar xSubtitle = \"\";\nvar xMessage = \"\";\nvar xServices = \"\";\nvar xTag = \"\";\nvar xUrl = \"\";\n// overrides\nif (typeof msg.notificationOverride !== 'undefined' && msg.notificationOverride !== null) {\n if (typeof msg.notificationOverride.title !== 'undefined' && msg.notificationOverride.title !=='' && msg.notificationOverride.title !== null){\n xTitle = msg.notificationOverride.title;\n } else {\n xTitle = env.get('title');\n }\n if (typeof msg.notificationOverride.subtitle !== 'undefined' && msg.notificationOverride.subtitle !=='' && msg.notificationOverride.subtitle !== null){\n xSubtitle = msg.notificationOverride.subtitle;\n } else {\n xSubtitle = env.get('subtitle');\n }\n if (typeof msg.notificationOverride.message !== 'undefined' && msg.notificationOverride.message !=='' && msg.notificationOverride.message !== null){\n xMessage = msg.notificationOverride.message;\n } else {\n xMessage = env.get('message');\n }\n if (typeof msg.notificationOverride.url !== 'undefined' && msg.notificationOverride.url !=='' && msg.notificationOverride.url !== null){\n xUrl = msg.notificationOverride.url;\n } else {\n xUrl = env.get('notificationUrl');\n }\n if (typeof msg.notificationOverride.services !== 'undefined' && msg.notificationOverride.services !=='' && msg.notificationOverride.services !== null){\n xServices = msg.notificationOverride.services;\n } else {\n xServices = env.get('service');\n }\n// flow.set('service', xServices);\n flow_msg_variables.service=xServices;\n if (typeof msg.notificationOverride.tag !== 'undefined' && msg.notificationOverride.tag !=='' && msg.notificationOverride.tag !== null){\n xTag = msg.notificationOverride.tag.replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase();\n } else {\n if (env.get('tag') !== '') {\n xTag = `${env.get('tag').replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase()}`;\n } else {\n // need to still set this to something in case clear_notification is sent.\n xTag = `${env.get('title').replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase()}_${flow.get('random')}`;\n }\n }\n//support multiple tags per flow\n //flow.set('tag',xTag);\n flow_msg_variables.tag=xTag;\n} else {\n // If no override is sent in...\n if (env.get('tag') !== '') {\n xTag = `${env.get('tag').replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase()}`;\n } else {\n // need to still set this to something in case clear_notification is sent.\n xTag = `${env.get('title').replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase()}_${flow.get('random')}`;\n }\n//support multiple tags per flow\n// flow.set('tag',xTag);\n flow_msg_variables.tag=xTag;\n \n xServices = env.get('service');\n// flow.set('service', xServices);\n flow_msg_variables.service=xServices;\n \n xTitle = env.get('title');\n xSubtitle = env.get('subtitle');\n xMessage = env.get('message');\n xUrl = env.get('notificationUrl');\n}\nlet all_flow_messages= flow.get('flow_messages');\nif (!all_flow_messages){\n all_flow_messages=[];\n}\nlet new_flow_messages=[];\nfor(var i=0;i<all_flow_messages.length;i++){//copy (and reset) the messages array\n if ((all_flow_messages[i].tag==flow_msg_variables.tag)&&(all_flow_messages[i].service==flow_msg_variables.service)){\n continue;//don't copy this message\n }else{\n if (Date.now()-all_flow_messages[i].date_created<86400000){//only copy messages less than 24h old\n new_flow_messages.push(all_flow_messages[i]);\n }\n }\n}\nnew_flow_messages.push(flow_msg_variables);\nflow.set('flow_messages',new_flow_messages);\n\nconst services = xServices;//flow.get('service');\nif(!services) {\n node.status({\n text: 'no services defined',\n shape: 'ring',\n fill: 'red'\n });\n return; \n} else if (services == \"NOONE\"){\n node.status({\n text: 'No one to send to',\n shape: 'ring',\n fill: 'yellow'\n });\n return;\n}\n\n// create actions\nconst actions = [];\n[1,2,3,4].forEach(i => {\n const name = `action${i}`\n if (env.get(`${name}Title`) !== ''){\n const action = env.get(`${name}Title`).replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase();\n const title = env.get(`${name}Title`);\n const activationMode = env.get(`${name}ActivationMode`); \n \n const uri = env.get(`${name}Uri`);\n const textInputButtonTitle = env.get(`${name}TextInputButtonTitle`);\n const textInputPlaceholder = env.get(`${name}TextInputPlaceHolder`);\n const authenticationRequired = env.get(`${name}AuthenticationRequired`);\n const destructive = env.get(`${name}Destructive`);\n const behavior = env.get(`${name}Behavior`);\n const icon = env.get(`${name}Icon`);\n \n const actionObject = {};\n actionObject.action = action;\n actionObject.title = title;\n actionObject.activationMode = activationMode;\n \n if (uri != \"\") actionObject.uri = uri;\n if (textInputButtonTitle != \"\") actionObject.textInputButtonTitle = textInputButtonTitle;\n if (textInputPlaceholder != \"\") actionObject.textInputPlaceholder = textInputPlaceholder;\n if (authenticationRequired != \"\") actionObject.authenticationRequired = authenticationRequired;\n if (destructive != \"\") actionObject.destructive = destructive;\n if (behavior != \"\") actionObject.behavior = behavior;\n if (icon != \"\") actionObject.icon = icon;\n \n actions.push(actionObject);\n }\n});\n// create msg object\nmsg.payload = {\n domain: 'notify',\n data: {\n title: xTitle,\n message: xMessage,\n data: {\n push: {},\n tag: xTag\n }\n }\n };\nif (actions.length > 0) {\n msg.payload.data.data.action_data = {tag: xTag};\n msg.payload.data.data.actions = actions;\n}\n// notification url\nif (xUrl !== \"\") {\n msg.payload.data.data.url = {};\n msg.payload.data.data.url = xUrl;\n}\n// subtitle\nif (xSubtitle !== '') {\n msg.payload.data.data.subtitle = xSubtitle;\n}\n// sound information\nmsg.payload.data.data.push.sound = {};\n\nif (env.get('customSound').length > 0){\n msg.payload.data.data.push.sound.name = env.get('customSound');\n} else if (env.get('customSoundPreInstalled').length > 0) {\n if(env.get('customSoundPreInstalled')=='none'){\n msg.payload.data.data.push.sound = 'none';\n }else{\n msg.payload.data.data.push.sound.name = env.get('customSoundPreInstalled');\n }\n} else {\n msg.payload.data.data.push.sound.name = 'default';\n}\n\n//if (env.get('isCriticalNotification')) {\n// msg.payload.data.data.push.sound.critical = 1;\n// msg.payload.data.data.push.sound.volume = 1.0;\n//}\nmsg.payload.data.data.push[\"interruption-level\"] = env.get('interruptionLevel');\n// group thread id\nif (env.get('group') !== '') {\n msg.payload.data.data.group = env.get('group');\n}\n\n// Map Information\nif (env.get('latitudeFirst') !== 0 && env.get('longitudeFirst') !== 0){\n msg.payload.data.data.action_data.latitude = env.get('latitudeFirst');\n msg.payload.data.data.action_data.longitude = env.get('longitudeFirst');\n if (env.get('latitudeSecond') !== 0 && env.get('longitudeSecond') !== 0){\n msg.payload.data.data.action_data.second_latitude = env.get('latitudeSecond');\n msg.payload.data.data.action_data.second_longitude = env.get('longitudeSecond');\n // add in all the extras, which default to false.\n msg.payload.data.data.action_data.shows_line_between_points = env.get('showLineBetweenPoints');\n msg.payload.data.data.action_data.shows_compass = env.get('showCompass');\n msg.payload.data.data.action_data.shows_points_of_interest = env.get('showPointsOfInterest');\n msg.payload.data.data.action_data.shows_scale = env.get('showScale');\n msg.payload.data.data.action_data.shows_traffic = env.get('showTraffic');\n msg.payload.data.data.action_data.shows_user_location = env.get('showUserLocation');\n }\n}\n// camera information\nif (env.get('cameraEntity') !== '') {\n msg.payload.data.data.entity_id = env.get('cameraEntity');\n}\n// media information\nif (env.get('contentUrl') !== '') {\n\n} else {\n if (env.get('imagePath') !== '') {\n msg.payload.data.data.image = env.get('imagePath');\n }\n if (env.get('videoPath') !== '') {\n msg.payload.data.data.video = env.get('videoPath');\n }\n if (env.get('audioPath') !== '') {\n msg.payload.data.data.audio = env.get('audioPath');\n }\n}\nif (env.get('contentUrl') !== '' || env.get('imagePath') !== '' || env.get('videoPath') !== '' || env.get('audioPath') !== '') {\n if (env.get('lazyLoading')) {\n msg.payload.data.data.lazy = env.get('lazyLoading');\n }\n if (env.get('hideThumbnail')) {\n msg.payload.data.data.attachment = {};\n msg.payload.data.data.attachment['hide-thumbnail'] = env.get('hideThumbnail');\n }\n}\nservices.trim().split(/,\\s*/).forEach(service => {\n if(!service) return;\n msg.payload.service = service;\n node.send(msg);\n});\n\nnode.done();",
"outputs":1,
"noerr":0,
"initialize":"flow.set('random',Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5).toUpperCase());\n",
"finalize":"",
"libs":[],
"x":530,
"y":40,
"wires":[["623033ccf4ee0715"]]
},
{
"id":"7bb055c340123ca6",
"type":"switch",
"z":"914734122ce79fcd",
"name":"which action?",
"property":"responseIndex",
"propertyType":"flow",
"rules":[
{"t":"eq","v":"1","vt":"num"},
{"t":"eq","v":"2","vt":"num"},
{"t":"eq","v":"3","vt":"num"},
{"t":"eq","v":"4","vt":"num"}
],
"checkall":"true",
"repair":false,
"outputs":4,
"x":1260,
"y":220,
"wires":[[],[],[],[]]
},
{
"id":"f87ecb61310bc411",
"type":"status",
"z":"914734122ce79fcd",
"name":"",
"scope":[
"0c74efbb9a85bee5",
"6cdec7ddd37347ca",
"d5bbdcb0c9aae966",
"a18477a92277fde6"
],
"x":80,
"y":320,
"wires":[[]]
},
{
"id":"6cdec7ddd37347ca",
"type":"function",
"z":"914734122ce79fcd",
"name":"build message",
"func":"const latestMessage = flow.get('latestMessage');\nconst event = msg.payload.event;\n\nlatestMessage.payload = latestMessage._originalPayload;\nlatestMessage.eventData = msg.payload;\ndelete latestMessage._originalPayload;\n\nif(env.get('userInfo')) {\n const userData = msg.userData.find(u => u.id === msg.payload.context.user_id);\n latestMessage.userData = userData;\n}\n\n\ntry{ //For Some reason it is called twice, second time with empty env.get values\n const index = [1,2,3,4].find(i => env.get(`action${i}Title`).replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase() === event.actionName);\n flow.set(\"responseIndex\",index);\n}catch(e){}\ntry{\n node.status({\n text: `${env.get(`action${index}Title`).replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase()} at: ${getPrettyDate()}`,\n shape: 'dot',\n fill: 'green'\n });\n}catch(e){}\nreturn latestMessage;\n\n\nfunction getPrettyDate() {\n return new Date().toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n hour12: false,\n hour: 'numeric',\n minute: 'numeric',\n });\n}",
"outputs":1,
"noerr":0,
"initialize":"",
"finalize":"",
"libs":[],
"x":1060,
"y":220,
"wires":[["7bb055c340123ca6"]]
},
{
"id":"315c4ad3c3647075",
"type":"switch",
"z":"914734122ce79fcd",
"name":"belongs here?",
"property":"payload.event.action_data.tag",
"propertyType":"msg",
"rules":[{"t":"eq","v":"tag","vt":"flow"}],
"checkall":"true",
"repair":false,
"outputs":1,
"x":380,
"y":260,
"wires":[[]]
},
{
"id":"95c45e1dfd4436e0",
"type":"ha-api",
"z":"914734122ce79fcd",
"name":"get user info",
"server":"",
"version":1,
"debugenabled":false,
"protocol":"websocket",
"method":"get",
"path":"",
"data":"{\"type\": \"config/auth/list\"}",
"dataType":"json",
"responseType":"json",
"outputProperties":[
{
"property":"userData",
"propertyType":"msg",
"value":"",
"valueType":"results"
}
],
"x":850,
"y":260,
"wires":[["6cdec7ddd37347ca"]]
},
{
"id":"52754e832dea9cb6",
"type":"switch",
"z":"914734122ce79fcd",
"name":"fetch user info?",
"property":"userInfo",
"propertyType":"env",
"rules":[{"t":"true"},{"t":"else"}],
"checkall":"true",
"repair":false,
"outputs":2,
"x":840,
"y":220,
"wires":[
["95c45e1dfd4436e0"],
["6cdec7ddd37347ca"]
]
},
{
"id":"66cdad51e119544d",
"type":"server-events",
"z":"914734122ce79fcd",
"name":"ios.notification_action_fired",
"server":"",
"version":1,
"event_type":"ios.notification_action_fired",
"exposeToHomeAssistant":false,
"haConfig":[
{"property":"name","value":""},
{"property":"icon","value":""}
],
"waitForRunning":false,
"outputProperties":[
{
"property":"payload",
"propertyType":"msg",
"value":"",
"valueType":"eventData"
},
{
"property":"topic",
"propertyType":"msg",
"value":"$outputData(\"eventData\").event_type",
"valueType":"jsonata"
},
{
"property":"event_type",
"propertyType":"msg",
"value":"$outputData(\"eventData\").event_type",
"valueType":"jsonata"
}
],
"x":130,
"y":180,
"wires":[["1d74d403fab61ed8"]]
},
{
"id":"d5bbdcb0c9aae966",
"type":"api-call-service",
"z":"914734122ce79fcd",
"name":"Send Notifications",
"server":"",
"version":5,
"debugenabled":false,
"domain":"notify",
"service":"",
"areaId":[],
"deviceId":[],
"entityId":[],
"data":"",
"dataType":"json",
"mergeContext":"callServiceData",
"mustacheAltTags":false,
"outputProperties":[],
"queue":"none",
"x":1130,
"y":40,
"wires":[[]]
},
{
"id":"a18477a92277fde6",
"type":"function",
"z":"914734122ce79fcd",
"name":"create CLEAR service call",
"func":"msg._originalPayload = msg.payload;\n//flow.set('latestMessage', msg);\nvar tag_to_clear = \"\";\nvar services = \"\";\nvar clearAll = false;\nif (typeof msg.notificationOverride !== 'undefined') {\n if (typeof msg.notificationOverride.clear !== 'undefined' && msg.notificationOverride.clear !== null && msg.notificationOverride.clear) {\n clearAll = true;\n }\n if (typeof msg.notificationOverride.services !== 'undefined' && msg.notificationOverride.services !== null && msg.notificationOverride.services){\n services = msg.notificationOverride.services;\n }\n if (typeof msg.notificationOverride.tag !== 'undefined' && msg.notificationOverride.tag !== null && msg.notificationOverride.tag){\n tag_to_clear = msg.notificationOverride.tag.replace(/[^\\w\\s]/gi, '').replace(/\\s+/g,'_').toUpperCase();//formatting tags as sent to the phone before\n }\n}\nif (tag_to_clear==\"\"){//no specific tag sent, clear the last message.\n var all_flow_messages= flow.get('flow_messages');\n if(all_flow_messages) {\n var last_message= all_flow_messages.pop();\n if (last_message){//on nodeRED restart\n tag_to_clear= last_message.tag;\n services = last_message.service;//override service to the one used in last message sent\n flow.set('flow_messages',all_flow_messages);//clear the message from memory\n }\n }\n}\n\nif(services==\"\") {\n node.status({\n text: 'no services defined',\n shape: 'ring',\n fill: 'red'\n });\n return; \n}\nif(tag_to_clear==\"\") {\n node.status({\n text: 'no messages to delete',\n shape: 'ring',\n fill: 'red'\n });\n return; \n}\n// create ios msg object\nmsg.payload = {\n domain: 'notify',\n data: {\n message: \"clear_notification\",\n data: {\n tag: tag_to_clear //flow.get('tag'),\n }\n }\n };\n\nfunction getPrettyDate() {\n return new Date().toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n hour12: false,\n hour: 'numeric',\n minute: 'numeric',\n });\n}\n\nif (clearAll){\n delete msg.notificationOverride;\n}\n \nvar xCountCleared = 0;\nservices.trim().split(/,\\s*/).forEach(service => {\n if(!service) return;\n // only clear on devices that didn't send the event.\n if (clearAll){\n msg.payload.service = service;\n node.send(msg);\n xCountCleared++;\n } else if (!service.includes(msg._originalPayload.event.sourceDeviceID)){\n msg.payload.service = service;\n node.send(msg);\n xCountCleared++;\n }\n});\nif (xCountCleared > 0) {\n node.status({\n text: `${xCountCleared} messages cleared at: ${getPrettyDate()}`,\n shape: 'dot',\n fill: 'blue'\n });\n} else {\n node.status({\n text: `No messages cleared: ${getPrettyDate()}`,\n shape: 'dot',\n fill: 'red'\n });\n}",
"outputs":1,
"noerr":0,
"initialize":"",
"finalize":"",
"libs":[],
"x":550,
"y":80,
"wires":[["b649a0d3bf05a394"]]
},
{
"id":"2f18a199fe3023b9",
"type":"switch",
"z":"914734122ce79fcd",
"name":"Clear Notification on Action?",
"property":"isClearNotificationsOnAction",
"propertyType":"env",
"rules":[{"t":"true"},{"t":"false"}],
"checkall":"true",
"repair":false,
"outputs":2,
"x":420,
"y":220,
"wires":[
["a18477a92277fde6","ca88e6add3be35fc"],
["52754e832dea9cb6"]
]
},
{
"id":"59d66e6372aaca66",
"type":"api-call-service",
"z":"914734122ce79fcd",
"name":"Clear Notifications",
"server":"",
"version":5,
"debugenabled":false,
"domain":"notify",
"service":"",
"areaId":[],
"deviceId":[],
"entityId":[],
"data":"",
"dataType":"json",
"mergeContext":"callServiceData",
"mustacheAltTags":false,
"outputProperties":[],
"queue":"none",
"x":1130,
"y":80,
"wires":[[]]
},
{
"id":"ca88e6add3be35fc",
"type":"delay",
"z":"914734122ce79fcd",
"name":"",
"pauseType":"delay",
"timeout":"10",
"timeoutUnits":"seconds",
"rate":"1",
"nbRateUnits":"1",
"rateUnits":"second",
"randomFirst":"1",
"randomLast":"5",
"randomUnits":"seconds",
"drop":false,
"outputs":1,
"x":660,
"y":220,
"wires":[["52754e832dea9cb6"]]
},
{
"id":"f8d958779acdc007",
"type":"switch",
"z":"914734122ce79fcd",
"name":"clear?",
"property":"notificationOverride.clear",
"propertyType":"msg",
"rules":[
{
"t":"istype",
"v":"undefined",
"vt":"undefined"
},
{"t":"null"},
{"t":"false"},
{"t":"true"}
],
"checkall":"false",
"repair":false,
"outputs":4,
"x":170,
"y":60,
"wires":[
["0c74efbb9a85bee5"],
["0c74efbb9a85bee5"],
["0c74efbb9a85bee5"],
["a18477a92277fde6"]
]
},
{
"id":"b649a0d3bf05a394",
"type":"delay",
"z":"914734122ce79fcd",
"name":"",
"pauseType":"rate",
"timeout":"5",
"timeoutUnits":"seconds",
"rate":"1",
"nbRateUnits":"1",
"rateUnits":"second",
"randomFirst":"1",
"randomLast":"5",
"randomUnits":"seconds",
"drop":false,
"outputs":1,
"x":910,
"y":80,
"wires":[["59d66e6372aaca66"]]
},
{
"id":"623033ccf4ee0715",
"type":"delay",
"z":"914734122ce79fcd",
"name":"",
"pauseType":"rate",
"timeout":"5",
"timeoutUnits":"seconds",
"rate":"1",
"nbRateUnits":"1",
"rateUnits":"second",
"randomFirst":"1",
"randomLast":"5",
"randomUnits":"seconds",
"drop":false,
"allowrate":false,
"outputs":1,
"x":910,
"y":40,
"wires":[["d5bbdcb0c9aae966"]]
},
{
"id":"1d74d403fab61ed8",
"type":"function",
"z":"914734122ce79fcd",
"name":"belongs here?",
"func":"let msg_tag=msg.payload.event.action_data.tag;\nlet all_flow_messages= flow.get('flow_messages');\n//node.warn({'message tag':msg_tag,'flow_messages':all_flow_messages});\nif(all_flow_messages){\n for (var i=0;i<all_flow_messages.length;i++){\n if (all_flow_messages[i].tag.trim()==msg_tag.trim()){\n//node.warn(all_flow_messages[i])\n flow.set('latestMessage',all_flow_messages[i].message);\n return [msg,null];\n }\n }\n}\nreturn [null,msg];\n",
"outputs":2,
"noerr":0,
"initialize":"",
"finalize":"",
"libs":[],
"x":380,
"y":180,
"wires":[["2f18a199fe3023b9"],[]]
}
]
@okets
Copy link
Author

okets commented Mar 21, 2022

Hello All,
This is a fork of the wonderful sub-flow of @sstratoti

I made some significant changes to the original sub-flow as I kept using it in more complex scenarios.
among the changes I made:

  1. Added "No-sound" to the sounds list, use it to send a silent message.
  2. Changed original "isCritical" checkbox to interruption levels dropdown list now you can choose between [Critical/Time Sensitive/Passive/Normal] instead of the old way that only supported [Normal/critical].
  3. Fixed multiple bugs around context storage, clearing messages and figuring out actions. now it can find the correct message when using tags in overrides, clears the correct messages etc.
  • the old code did not find the correct messages for actions if notificationOverride.tag was set.

  • the old code could only clear messages using the last service used, not the service used for the actual notification.
    ios notifications

  • I will keep maintaining this fork as I make more changes or fixes.

@sstratoti
Copy link

I love the interruption levels dropdown list! And thanks for catching that newer method! But in reading up on it, this option is only available in iOS 15+ according to the documentation.
I brought in your code into my node red and ran a test against my iPad still on 14, and it gave a critical alert on my phone (15.3) but a normal one on my iPad (14.6).
I'm going to merge the two options so that those pre-15 can still send a critical notification. If the drop down is set to critical, it'll override the isCriticalNotification boolean and set it to true. It'll also include the older way of setting the volume to 1.0 and critical to 1 if "critical" is selected. Should capture all use cases then. :)

@sstratoti
Copy link

Thank you thank you thank you for the updates with regards to tags and clearing messages. Great Job!!

@sstratoti
Copy link

So I mentioned this in my gist, but I've created a github project for this. I think it'll be easier than us all managing our own gists. I'd welcome any more updates you'd like to submit a PR for!

I've gone through the past few hours and have diff'd our code to see all of your updates. Very well done! I've run some test cases through my node red, and I think we're looking good for your changes getting merged in. I've made a couple updates myself as well that I hadn't updated in the gist quite yet.

New Github Project: https://github.com/sstratoti/actionable-notifications-subflow-for-ios

Also, just a heads up - the subflow ID in your gist is different than the one in mine. So if a user wanted to use yours and import it, they'd have to change all of their existing nodes to use the new id (or if they're not familiar how to do that, they may have to replace all of the older subflows with yours). If they import with the id I've been using, it'll ask if they want to "replace" over top of it. It makes updating easier. That, and you may want to use this tool: https://zachowj.github.io/node-red-contrib-home-assistant-websocket/scrubber/ to scrub out your HA connection data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment