Skip to content

Instantly share code, notes, and snippets.

@psbaltar
Last active February 24, 2024 21:25
Show Gist options
  • Save psbaltar/a954ef9c8d280c0b816ad875827ac993 to your computer and use it in GitHub Desktop.
Save psbaltar/a954ef9c8d280c0b816ad875827ac993 to your computer and use it in GitHub Desktop.
Home Assistant blueprint for handling Sleep as Android webhooks events
blueprint:
name: Sleep as Android webhook handler
description: >
Home Assistant blueprint for handling Sleep as Android webhooks events
In Sleep as Android app, set Webhooks URL to:
https://<home-assistant-host>/api/webhook/<webhook-id>
Sleep as Android events: https://docs.sleep.urbandroid.org/services/automation.html#events
Blueprint based on above documentation as of 2022-03-11
Some events include additional data ('value1' and 'value2'). This blueprint makes
them available as template variables 'value1' and 'value2'. 'event' is also
available as a variable if needed.
Note:
Some Sleep as Android events can be triggered several hundred times per night. Specific
events can be disabled in the app. Otherwise, filtering this automation from
the logs may be warranted (https://www.home-assistant.io/integrations/recorder/#configure-filter)
source_url: https://gist.github.com/psbaltar/a954ef9c8d280c0b816ad875827ac993
domain: automation
input:
webhook:
name: Webhook ID
description: "Webhook ID"
localonly:
name: Local Only
description: "Only accessible from the local network"
default: false
selector:
boolean:
person:
name: Person (optional)
description: "Person to track (optional if Zone Check not enabled)"
default: []
selector:
entity:
domain: person
zone:
name: Zone (optional)
description: "Check if person is in this zone (optional if Zone Check not enabled)"
default: []
selector:
entity:
domain: zone
zone_check:
name: Zone Check
description: "Enable to run automation only if Person is in Zone"
default: false
selector:
boolean:
sleep_tracking_started:
name: sleep_tracking_started
description: ""
default: []
selector:
action: {}
sleep_tracking_stopped:
name: sleep_tracking_stopped
description: ""
default: []
selector:
action: {}
sleep_tracking_paused:
name: sleep_tracking_paused
description: ""
default: []
selector:
action: {}
sleep_tracking_resumed:
name: sleep_tracking_resumed
description: ""
default: []
selector:
action: {}
alarm_snooze_clicked:
name: alarm_snooze_clicked
description: >
You have snoozed a ringing alarm.
We are sending the following values:
- value1: UNIX timestamp of the alarm start time, example: "1582719660934"
- value2: alarm label, example: "label" (Any tabs and newline characters in the label will be removed before sending)
default: []
selector:
action: {}
alarm_snooze_canceled:
name: alarm_snooze_canceled
description: >
You have a canceled an alarm that is currently snoozed.
We are sending the following values:
- value1: UNIX timestamp of the alarm start time, example: "1582719660934"
- value2: alarm label, example: "label" (Any tabs and newline characters in the label will be removed before sending)
default: []
selector:
action: {}
time_to_bed_alarm_alert:
name: time_to_bed_alarm_alert
description: >
Fires when you get a bedtime notification.
We are sending the following values:
- value1: UNIX timestamp of the alarm start time (the alarm which triggered the bedtime notification, based on your ideal daily sleep income), example: "1582719660934"
default: []
selector:
action: {}
alarm_alert_start:
name: alarm_alert_start
description: >
Fires when alarm starts.
We are sending the following values:
- value1: UNIX timestamp of the alarm start time, example: "1582719660934"
- value2: alarm label, example: "label" (Any tabs and newline characters in the label will be removed before sending)
default: []
selector:
action: {}
alarm_alert_dismiss:
name: alarm_alert_dismiss
description: >
Fires when you dismiss alarm (after you solve CAPTCHA, if it’s set).
We are sending the following values:
- value1: UNIX timestamp of the alarm start time, example: "1582719660934"
- value2: alarm label, example: "label" (Any tabs and newline characters in the label will be removed before sending)
default: []
selector:
action: {}
alarm_skip_next:
name: alarm_skip_next
description: >
Fires when you tap dismiss an alarm from notification before it actually rings.
We are sending the following values:
- value1: UNIX timestamp of the alarm start time, example: "1582719660934"
- value2: alarm label, example: "label" (Any tabs and newline characters in the label will be removed before sending)
default: []
selector:
action: {}
before_alarm:
name: show_skip_next_alarm
description: >
Note: As of 2023-05-10, the documentation is incorrect. The app sends 'before_alarm' instead of 'show_skip_next_alarm'. This blueprint handles it correctly, but leaves the displayed name to be consistent with the documentation
Fires exactly 1 hour before the next alarm is triggered.
value1: UNIX timestamp of the alarm start time, example: "1582719660934"
default: []
selector:
action: {}
rem:
name: rem
description: "Fires when we estimate the start of REM phase."
default: []
selector:
action: {}
smart_period:
name: smart_period
description: "Fires at the start of the smart period."
default: []
selector:
action: {}
before_smart_period:
name: before_smart_period
description: >
Fires 45 minutes before the start of smart period.
We are sending the following value:
- value: alarm label, example: "label" (Any tabs and newline characters in the label will be removed before sending)
default: []
selector:
action: {}
lullaby_start:
name: lullaby_start
description: "Fires when lullaby starts playing."
default: []
selector:
action: {}
lullaby_stop:
name: lullaby_stop
description: "Fires when lullaby is stopped (either manually or automatically)."
default: []
selector:
action: {}
lullaby_volume_down:
name: lullaby_volume_down
description: "Fires when we detect you fell asleep and starting lowering the volume of lullabies."
default: []
selector:
action: {}
deep_sleep:
name: deep_sleep
description: "Fires when we detect you going into deep sleep phase. Warning: This may result in lots of events during the night and may not exactly fit the resulting sleep graph as we can only detect phases reliably from whole-night data."
default: []
selector:
action: {}
light_sleep:
name: light_sleep
description: "Fires when we detect you going into light sleep phase. Warning: This may result in lots of events during the night and may not exactly fit the resulting sleep graph as we can only detect phases reliably from whole-night data."
default: []
selector:
action: {}
awake:
name: awake
description: "Fires when we detect you woke up."
default: []
selector:
action: {}
not_awake:
name: not_awake
description: "Fires when we detect you fell asleep."
default: []
selector:
action: {}
apnea_alarm:
name: apnea_alarm
description: "Fires when we detect a significant dip in your oxygen levels."
default: []
selector:
action: {}
antisnoring:
name: antisnoring
description: "Fires when antisnoring is triggered."
default: []
selector:
action: {}
sound_event_snore:
name: sound_event_snore
description: "Fires when we detect snoring."
default: []
selector:
action: {}
sound_event_talk:
name: sound_event_talk
description: "Fires when we detect talking."
default: []
selector:
action: {}
sound_event_cough:
name: sound_event_cough
description: "Fires when we detect coughing."
default: []
selector:
action: {}
sound_event_baby:
name: sound_event_baby
description: "Fires when we detect baby cry."
default: []
selector:
action: {}
sound_event_laugh:
name: sound_event_laugh
description: "Fires when we detect laughter."
default: []
selector:
action: {}
mode: parallel
variables:
var_zone_check: !input zone_check
trigger:
- platform: webhook
webhook_id: !input webhook
allowed_methods:
- POST
local_only: !input localonly
condition:
condition: or
conditions:
- "{{ not var_zone_check }}"
- condition: and
conditions:
- "{{ var_zone_check }}"
- condition: zone
entity_id: !input person
zone: !input zone
action:
- variables:
event: "{{ trigger.json.event }}"
value1: "{{ trigger.json.value1 if trigger.json.value1 is defined }}"
value2: "{{ trigger.json.value2 if trigger.json.value2 is defined }}"
- choose:
- conditions: "{{ event == 'sleep_tracking_started' }}"
sequence: !input sleep_tracking_started
- conditions: "{{ event == 'sleep_tracking_stopped' }}"
sequence: !input sleep_tracking_stopped
- conditions: "{{ event == 'sleep_tracking_paused' }}"
sequence: !input sleep_tracking_paused
- conditions: "{{ event == 'sleep_tracking_resumed' }}"
sequence: !input sleep_tracking_resumed
- conditions: "{{ event == 'alarm_snooze_clicked' }}"
sequence: !input alarm_snooze_clicked
- conditions: "{{ event == 'alarm_snooze_canceled' }}"
sequence: !input alarm_snooze_canceled
- conditions: "{{ event == 'time_to_bed_alarm_alert' }}"
sequence: !input time_to_bed_alarm_alert
- conditions: "{{ event == 'alarm_alert_start' }}"
sequence: !input alarm_alert_start
- conditions: "{{ event == 'alarm_alert_dismiss' }}"
sequence: !input alarm_alert_dismiss
- conditions: "{{ event == 'alarm_skip_next' }}"
sequence: !input alarm_skip_next
- conditions: "{{ event == 'before_alarm' }}" # documenation incorrctly calls this show_skip_next_alarm
sequence: !input before_alarm
- conditions: "{{ event == 'rem' }}"
sequence: !input rem
- conditions: "{{ event == 'smart_period' }}"
sequence: !input smart_period
- conditions: "{{ event == 'before_smart_period' }}"
sequence: !input before_smart_period
- conditions: "{{ event == 'lullaby_start' }}"
sequence: !input lullaby_start
- conditions: "{{ event == 'lullaby_stop' }}"
sequence: !input lullaby_stop
- conditions: "{{ event == 'lullaby_volume_down' }}"
sequence: !input lullaby_volume_down
- conditions: "{{ event == 'deep_sleep' }}"
sequence: !input deep_sleep
- conditions: "{{ event == 'light_sleep' }}"
sequence: !input light_sleep
- conditions: "{{ event == 'awake' }}"
sequence: !input awake
- conditions: "{{ event == 'not_awake' }}"
sequence: !input not_awake
- conditions: "{{ event == 'apnea_alarm' }}"
sequence: !input apnea_alarm
- conditions: "{{ event == 'antisnoring' }}"
sequence: !input antisnoring
- conditions: "{{ event == 'sound_event_snore' }}"
sequence: !input sound_event_snore
- conditions: "{{ event == 'sound_event_talk' }}"
sequence: !input sound_event_talk
- conditions: "{{ event == 'sound_event_cough' }}"
sequence: !input sound_event_cough
- conditions: "{{ event == 'sound_event_baby' }}"
sequence: !input sound_event_baby
- conditions: "{{ event == 'sound_event_laugh' }}"
sequence: !input sound_event_laugh
default:
- service: persistent_notification.create
data:
title: "Sleep As Android blueprint exception"
message: "Caught unhandled event: {{ event }}"
notification_id: 'sleep_default_action_notification'
- service: system_log.write
data:
level: warning
message: "Sleep As Android blueprint caught unhandled event: {{ event }}"
@psbaltar
Copy link
Author

psbaltar commented May 11, 2023

Hi, I get the following notification from repairs on the current version of Home Assistant. Is this something that needs to be implemented in this blueprint because I don't see the mentioned input when editing the automation?

Thanks for letting me know. I've updated the blueprint to comply with the new webhook trigger scheme. There's now an option for "Local Only" right below the webhook ID. I also corrected the before_alarm event based on reports here: https://community.home-assistant.io/t/sleep-as-android-webhooks-handler/401601

Based on my understanding from the current HA documentation, webhook automations will default to local only. I assume that warning means that this new default will take effect in 2023.7.0.

@FunctionalHacker
Copy link

Thanks! I can confirm that updating the blueprint to the latest version gets rid if the warning.

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