notify_user.yaml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. # - service: script.notify_users
  2. # data_template:
  3. # title: "It's {{ first_name }}'s birthday!"
  4. # users:
  5. # - notify.owners
  6. # message: >-
  7. # It is {{ full_name }}'s birthday today. {% if age is not none %}
  8. # {{- first_name }} is turning {{ age }} years.
  9. # {% endif %}
  10. # Send them a text and say <b>Happy Birthday!</b> to let them know that you appriciate them! :)
  11. # data_object:
  12. # tag: "birthday-notification-{{ context.id }}-{{ first_name | upper }}"
  13. # group: "birthday-notifications"
  14. # color: "#A83285"
  15. # icon_url: "https://home-assistant.kgv14.dev:8123/local/entities/birthday-cake.png"
  16. # actions:
  17. # - action: "{{ action_sms }}"
  18. # title: "Send SMS"
  19. # - wait_for_trigger:
  20. # platform: event
  21. # event_type: mobile_app_notification_action
  22. # event_data:
  23. # action: "{{action_sms}}"
  24. # timeout:
  25. # hours: 24
  26. # - choose:
  27. # conditions: "{{ wait.trigger is not none and wait.trigger.event.data.action == action_sms }}"
  28. # sequence:
  29. # service: notify.owners
  30. # data:
  31. # message: command_activity
  32. # title: "sms:{{ phone_number }}?body=Gratulere med {{ age|string + 'års ' if age is not none else '' }}dagen {{first_name}}!"
  33. # data:
  34. # tag: "android.intent.action.SENDTO"
  35. # default:
  36. # service: notify.owners
  37. # data:
  38. # message: clear_notification
  39. # data:
  40. # tag: "birthday-notification-{{ context.id }}-{{ first_name | upper }}"
  41. blueprint:
  42. domain: script
  43. name: Notify user
  44. description: >-
  45. Notify a user
  46. input:
  47. notify_device:
  48. name: "Devices to notify"
  49. description: >-
  50. The name of the notify service, e.g service.mobile_app_x
  51. selector:
  52. object:
  53. index:
  54. name: Device index
  55. description: >-
  56. If called several times within same context, increase this for each.
  57. default:
  58. seconds: 0
  59. selector:
  60. number:
  61. min: 1
  62. max: 1000
  63. step: 1
  64. timeout:
  65. name: "Timeout"
  66. description: >-
  67. Timeout before notifications dissapear.
  68. selector:
  69. duration:
  70. # enable_days: true
  71. data:
  72. name: "Message data"
  73. description: "Equal to data field in the notify service"
  74. selector:
  75. object:
  76. # Scripts to run based on `data->actions` set in notify-service. E.g:
  77. # ```
  78. # data:
  79. # actions:
  80. # - action: ACTION_ANCHOR
  81. # title: This test
  82. # ```
  83. # with matching:
  84. # ```
  85. # action_scripts:
  86. # ACTION_ANCHOR: script...
  87. # ```
  88. # The format for `ACTION_ANCHOR` can be just a string, referencing a script,
  89. # or an object with the following format:
  90. # ```
  91. # ACTION_ANCHOR:
  92. # script: script..
  93. # variables:
  94. # var1: ....
  95. # ```
  96. action_scripts:
  97. name: Action scripts
  98. description: >-
  99. Read `action_scripts` comments within blueprint.
  100. selector:
  101. object:
  102. default: {}
  103. tts:
  104. name: "TTS"
  105. description: "Not used yet"
  106. selector:
  107. boolean:
  108. default: "{{ false }}"
  109. mode: parallel
  110. variables:
  111. input_notify_device: !input notify_device
  112. index: !input index
  113. ctx: "{{ this.context.id + '_' + input_notify_device ~ '_' ~ index }}" #
  114. input_timeout: !input timeout
  115. update_timeout: >-
  116. {% set seconds = 0 %}
  117. {% if input_timeout is iterable %}
  118. {% if 'seconds' in input_timeout %}
  119. {% set seconds = seconds + input_timeout.seconds|int %}
  120. {% endif %}
  121. {% if 'minutes' in input_timeout %}
  122. {% set seconds = seconds + (input_timeout.minutes|int * 60 * 60) %}
  123. {% endif %}
  124. {% if 'hours' in input_timeout %}
  125. {% set seconds = seconds + ((input_timeout.hours|int) * 60 * 60) %}
  126. {% endif %}
  127. {% if 'days' in input_timeout %}
  128. {% set seconds = seconds + ((input_timeout.days|int) * 60 * 60 * 24) %}
  129. {% endif %}
  130. {% elif input_timeout is not iterable %}
  131. {% set seconds = input_timeout|int %}
  132. {% endif %}
  133. {{ 0 if seconds == 0 else (now().timestamp() + seconds) }}
  134. input_data: !input data
  135. update_data: >-
  136. {% set data_data = (input_data.data.items() | list) if 'data' in input_data else [] %}
  137. {% if 'data' in input_data %}
  138. {% if 'actions' in input_data.data %}
  139. {% set action = namespace(entities=[]) %}
  140. {% for a in input_data.data.actions %}
  141. {% set update = {"action": ctx ~ '_' ~ a.action}.items() | list %}
  142. {% set current = a.items() | list | rejectattr(
  143. '0', 'eq', update | map(attribute='0') | list
  144. ) | list %}
  145. {% set action.entities = action.entities + [
  146. dict.from_keys(current + update)
  147. ] %}
  148. {% endfor %}
  149. {% set actions = { "actions": action.entities }.items() | list %}
  150. {% set data_data = dict.from_keys(data_data | rejectattr(
  151. '0', 'eq', actions | map(attribute='0') | list
  152. ) | list + actions).items() | list
  153. %}
  154. {% endif %}
  155. {% if 'group' not in input_data.data %}
  156. {% set add = { "group": "default-group" }.items() | list %}
  157. {% set data_data = dict.from_keys(data_data | rejectattr(
  158. '0', 'eq', add | map(attribute='0') | list
  159. ) | list + add).items() | list
  160. %}
  161. {% endif %}
  162. {% if (
  163. (
  164. 'alert_once' in input_data.data or
  165. 'actions' in input_data.data or
  166. 'persistent' in input_data.data
  167. ) and 'tag' not in input_data.data
  168. ) or (
  169. timeout != 0 and 'tag' not in input_data.data
  170. )
  171. %}
  172. {% set add = { "tag": "tag_" + ctx }.items() | list %}
  173. {% set data_data = dict.from_keys(data_data | rejectattr(
  174. '0', 'eq', add | map(attribute='0') | list
  175. ) | list + add).items() | list
  176. %}
  177. {% endif %}
  178. {% else %}
  179. {% if timeout != 0 %}
  180. {% set add = { "tag": "tag_" + ctx }.items() | list %}
  181. {% set data_data = dict.from_keys(data_data | rejectattr(
  182. '0', 'eq', add | map(attribute='0') | list
  183. ) | list + add).items() | list
  184. %}
  185. {% endif %}
  186. {% endif %}
  187. {% if data_data|length != 0 %}
  188. {% set data_data = {"data": dict.from_keys(data_data)}.items() | list %}
  189. {{ dict.from_keys((input_data.items() | list | rejectattr(
  190. '0', 'eq', data_data | map(attribute='0') | list
  191. ) | list) + data_data) }}
  192. {% else %}
  193. {{ input_data }}
  194. {% endif %}
  195. action_scripts: !input action_scripts
  196. action_handlers: >-
  197. {% set actions = namespace(handlers=[]) %}
  198. {% for ask in action_scripts.keys() %}
  199. {% set askc = ctx ~ '_' ~ ask %}
  200. {% for action in update_data.data.actions if askc == action.action %}
  201. {% set actions.handlers = actions.handlers + [(
  202. askc, action_scripts[ask]
  203. )] %}
  204. {% endfor %}
  205. {% endfor %}
  206. {{ dict.from_keys(actions.handlers) }}
  207. sequence:
  208. # - service: system_log.write
  209. # data:
  210. # level: warning
  211. # message: >-
  212. # Action handlers: {{ action_handlers }}
  213. # Timeout: {{ update_timeout }} seconds
  214. # Data: {{ update_data }}
  215. - alias: "Parallize event listeners and notification"
  216. parallel:
  217. - alias: "Listen for event if actions are given"
  218. if: "{{ action_handlers|length > 0 }}"
  219. then:
  220. - alias: "Loop for events until criterias are met"
  221. repeat:
  222. while: "{{ (update_timeout - now().timestamp() > 0) or update_timeout == 0 }}"
  223. sequence:
  224. - if: "{{ update_timeout == 0 }}"
  225. then:
  226. alias: "Wait for app event, without timeout"
  227. wait_for_trigger:
  228. - platform: event
  229. event_type:
  230. - mobile_app_notification_action
  231. - mobile_app_notification_cleared
  232. else:
  233. alias: "Wait for app event, with timeout"
  234. wait_for_trigger:
  235. - platform: event
  236. event_type:
  237. - mobile_app_notification_action
  238. - mobile_app_notification_cleared
  239. timeout: >-
  240. {{ update_timeout - now().timestamp() }}
  241. - if: "{{ wait.trigger is none }}"
  242. then:
  243. alias: "Reached timeout, stopping."
  244. stop: "Reached timeout, stopping."
  245. - variables:
  246. in_ctx: >-
  247. {% set in_ctx = namespace(bool=false) %}
  248. {% for key in wait.trigger.event.data.keys() if key.startswith('action') and key.endswith('key') %}
  249. {% if wait.trigger.event.data[key].startswith(ctx) %}
  250. {% set in_ctx.bool = true %}
  251. {% endif %}
  252. {% endfor %}
  253. {{ in_ctx.bool }}
  254. - alias: "Check if context matches or restart from top"
  255. condition: "{{ in_ctx }}"
  256. - if: >-
  257. {{
  258. wait.trigger.event.event_type.endswith('_cleared') or
  259. wait.trigger.event.data.action not in action_handlers.keys()
  260. }}
  261. then:
  262. - alias: "Cleared event within user context"
  263. event: custom_mobile_app_notification_action
  264. event_data:
  265. context: "{{ ctx }}"
  266. action: "cleared"
  267. - stop: "User cleared notification or choose an invalid option, stop listening for events."
  268. - event: custom_mobile_app_notification_action
  269. event_data:
  270. context: "{{ ctx }}"
  271. action: "{{ wait.trigger.event.data.action }}"
  272. - stop: "User acted within context, stop listening for further events."
  273. - alias: "Listen for forwarded events"
  274. if: "{{ action_handlers|length > 0 or update_timeout != 0 }}"
  275. then:
  276. - if: "{{ update_timeout == 0 }}"
  277. then:
  278. alias: "Wait for context event, without timeout"
  279. wait_for_trigger:
  280. - platform: event
  281. event_type: custom_mobile_app_notification_action
  282. event_data:
  283. context: "{{ ctx }}"
  284. else:
  285. alias: "Wait for context event, with timeout"
  286. wait_for_trigger:
  287. - platform: event
  288. event_type: custom_mobile_app_notification_action
  289. event_data:
  290. context: "{{ ctx }}"
  291. timeout: >-
  292. {{ update_timeout - now().timestamp() }}
  293. - alias: "Check if timed out and remove notification"
  294. if: "{{ wait.trigger == none }}"
  295. then:
  296. alias: "Clear notification, timeout reached."
  297. service: "{{ input_notify_device }}"
  298. data:
  299. message: "clear_notification"
  300. data:
  301. tag: "{{ data.data.tag }}"
  302. else:
  303. - variables:
  304. action: >-
  305. {% set action = wait.trigger.event.data.action %}
  306. {{ action_handlers[action] if action in action_handlers else false }}
  307. - alias: "Action is known or stop executing"
  308. condition: "{{ action is not false }}"
  309. - if: "{{ action is string or 'variables' not in action }}"
  310. then:
  311. service: script.turn_on
  312. target:
  313. entity_id: "{{ script }}"
  314. else:
  315. service: script.turn_on
  316. target:
  317. entity_id: "{{ action.script }}"
  318. data:
  319. variables: "{{ action.variables }}"
  320. - alias: Send message to device
  321. service: "{{ input_notify_device }}"
  322. data: >-
  323. {{ update_data }}
  324. # @ignore: Incorrect type. Expected "object"