Skip to content

Commit fa7b9d7

Browse files
committed
fix(hass): start white noise with bedtime audiobook
1 parent 3403aba commit fa7b9d7

2 files changed

Lines changed: 62 additions & 47 deletions

File tree

modules/services/hass/_domains/sleep/default.nix

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ in
341341
"input_boolean.goodnight" = "on";
342342
"select.master_suite_current_mode" = "sleep";
343343
"switch.adaptive_lighting_sleep_mode_living_space" = "on";
344-
"switch.eve_energy_20ebu4101" = "off"; # Whitenoise waits for sleep_done + both bed-presence signals
344+
"switch.eve_energy_20ebu4101" = "off"; # Whitenoise waits for sleep_done + bedroom audiobook
345345
"switch.desk_monitor" = "off";
346346
"switch.desk_pop" = "off";
347347
"cover.smartwings_window_covering" = "closed";
@@ -554,12 +554,12 @@ in
554554
];
555555
}
556556

557-
# White noise starts only after the final Sleep phase and fresh human
558-
# heart-rate readings from both 8Sleep sides. Bed presence alone is
559-
# contaminated by the dog lying on a cool side of the bed.
557+
# White noise starts after the final Sleep phase once either bedroom
558+
# nightstand speaker starts playing. HA currently exposes no audiobook
559+
# metadata for these HomePods, so playing-on-bedroom-speaker is the signal.
560560
{
561-
alias = "White noise after both in bed";
562-
id = "white_noise_after_both_in_bed";
561+
alias = "White noise with bedtime audiobook";
562+
id = "white_noise_with_bedtime_audiobook";
563563
mode = "single";
564564
trigger = [
565565
{
@@ -569,11 +569,13 @@ in
569569
}
570570
{
571571
platform = "state";
572-
entity_id = "sensor.edmund_s_eight_sleep_side_heart_rate";
572+
entity_id = "media_player.bathroom_nightstand";
573+
to = "playing";
573574
}
574575
{
575576
platform = "state";
576-
entity_id = "sensor.monica_s_eight_sleep_side_heart_rate";
577+
entity_id = "media_player.window_nightstand";
578+
to = "playing";
577579
}
578580
];
579581
condition = [
@@ -588,20 +590,19 @@ in
588590
state = "on";
589591
}
590592
{
591-
condition = "template";
592-
value_template = ''
593-
{% set s = states.sensor.edmund_s_eight_sleep_side_heart_rate %}
594-
{% set hr = s.state | float(0) %}
595-
{{ hr >= 35 and hr <= 130 and (now() - s.last_updated).total_seconds() <= 300 }}
596-
'';
597-
}
598-
{
599-
condition = "template";
600-
value_template = ''
601-
{% set s = states.sensor.monica_s_eight_sleep_side_heart_rate %}
602-
{% set hr = s.state | float(0) %}
603-
{{ hr >= 35 and hr <= 130 and (now() - s.last_updated).total_seconds() <= 300 }}
604-
'';
593+
condition = "or";
594+
conditions = [
595+
{
596+
condition = "state";
597+
entity_id = "media_player.bathroom_nightstand";
598+
state = "playing";
599+
}
600+
{
601+
condition = "state";
602+
entity_id = "media_player.window_nightstand";
603+
state = "playing";
604+
}
605+
];
605606
}
606607
{
607608
condition = "state";

modules/services/hass/_tests/eval-automations.nix

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,16 @@ let
145145
(c.condition or null) == "state" && (c.entity_id or null) == entityId && (c.state or null) == state
146146
) conditions;
147147

148+
hasStateConditionDeep =
149+
conditions: entityId: state:
150+
any (
151+
c:
152+
(
153+
(c.condition or null) == "state" && (c.entity_id or null) == entityId && (c.state or null) == state
154+
)
155+
|| (if c ? conditions then hasStateConditionDeep (toList c.conditions) entityId state else false)
156+
) conditions;
157+
148158
toList =
149159
v:
150160
if builtins.isList v then
@@ -175,7 +185,7 @@ let
175185
voiceWebhookSleep = findAutomation "voice_webhook_sleep";
176186
voiceWebhookInBed = findAutomation "voice_webhook_in_bed";
177187
bedtimeNudgeWebhook = findAutomation "bedtime_nudge_webhook";
178-
whiteNoiseAfterBothInBed = findAutomation "white_noise_after_both_in_bed";
188+
whiteNoiseWithBedtimeAudiobook = findAutomation "white_noise_with_bedtime_audiobook";
179189
syncIphoneAlarm8sleep = findAutomation "sync_iphone_alarm_8sleep";
180190
sleepFocusOffEdmund = findAutomation "sleep_focus_off_stop_edmund";
181191
sleepFocusOffMonica = findAutomation "sleep_focus_off_stop_monica";
@@ -262,44 +272,48 @@ let
262272
msg = "automation 'bedtime_nudge_webhook' missing";
263273
}
264274
{
265-
test = whiteNoiseAfterBothInBed != null;
266-
msg = "automation 'white_noise_after_both_in_bed' missing";
275+
test = whiteNoiseWithBedtimeAudiobook != null;
276+
msg = "automation 'white_noise_with_bedtime_audiobook' missing";
267277
}
268278
{
269-
test = hasStateTrigger whiteNoiseAfterBothInBed "input_boolean.sleep_done" "on";
270-
msg = "white_noise_after_both_in_bed must trigger when sleep_done turns on";
279+
test = hasStateTrigger whiteNoiseWithBedtimeAudiobook "input_boolean.sleep_done" "on";
280+
msg = "white_noise_with_bedtime_audiobook must trigger when sleep_done turns on";
271281
}
272282
{
273-
test = hasStateTriggerAny whiteNoiseAfterBothInBed "sensor.edmund_s_eight_sleep_side_heart_rate";
274-
msg = "white_noise_after_both_in_bed must trigger when Edmund heart rate updates";
283+
test = hasStateTrigger whiteNoiseWithBedtimeAudiobook "media_player.bathroom_nightstand" "playing";
284+
msg = "white_noise_with_bedtime_audiobook must trigger when bathroom nightstand starts playing";
275285
}
276286
{
277-
test = hasStateTriggerAny whiteNoiseAfterBothInBed "sensor.monica_s_eight_sleep_side_heart_rate";
278-
msg = "white_noise_after_both_in_bed must trigger when Monica heart rate updates";
287+
test = hasStateTrigger whiteNoiseWithBedtimeAudiobook "media_player.window_nightstand" "playing";
288+
msg = "white_noise_with_bedtime_audiobook must trigger when window nightstand starts playing";
279289
}
280290
{
281291
test =
282292
let
283-
conditions = toList (whiteNoiseAfterBothInBed.condition or [ ]);
284-
templates = map (c: c.value_template or "") conditions;
285-
hasTemplate = needle: any (t: hasInfix needle t) templates;
293+
conditions = toList (whiteNoiseWithBedtimeAudiobook.condition or [ ]);
294+
hasBedroomSpeakerOr = any (
295+
c:
296+
(c.condition or null) == "or"
297+
&& hasStateCondition (toList (c.conditions or [ ])) "media_player.bathroom_nightstand" "playing"
298+
&& hasStateCondition (toList (c.conditions or [ ])) "media_player.window_nightstand" "playing"
299+
) conditions;
286300
in
287301
hasStateCondition conditions "input_boolean.goodnight" "on"
288302
&& hasStateCondition conditions "input_boolean.sleep_done" "on"
289-
&& hasTemplate "sensor.edmund_s_eight_sleep_side_heart_rate"
290-
&& hasTemplate "sensor.monica_s_eight_sleep_side_heart_rate"
291-
&& hasTemplate "last_updated"
292-
&& hasTemplate "hr >= 35"
293-
&& hasTemplate "hr <= 130"
294-
&& !(hasStateCondition conditions "binary_sensor.edmund_bed_presence_reliable" "on")
295-
&& !(hasStateCondition conditions "binary_sensor.monica_bed_presence_reliable" "on");
296-
msg = "white_noise_after_both_in_bed must require goodnight, sleep_done, and fresh human heart-rate signals instead of bed presence";
303+
&& hasBedroomSpeakerOr
304+
&& !(hasStateConditionDeep conditions "binary_sensor.edmund_bed_presence_reliable" "on")
305+
&& !(hasStateConditionDeep conditions "binary_sensor.monica_bed_presence_reliable" "on")
306+
&& !(hasStateTriggerAny whiteNoiseWithBedtimeAudiobook "sensor.edmund_s_eight_sleep_side_heart_rate")
307+
&& !(hasStateTriggerAny whiteNoiseWithBedtimeAudiobook "sensor.monica_s_eight_sleep_side_heart_rate");
308+
msg = "white_noise_with_bedtime_audiobook must require goodnight, sleep_done, and either bedroom speaker playing without bed-presence or HR gates";
297309
}
298310
{
299311
test =
300-
hasActionCall (toList (whiteNoiseAfterBothInBed.action or [ ])) "switch.turn_on"
301-
&& hasTargetEntity (toList (whiteNoiseAfterBothInBed.action or [ ])) "switch.eve_energy_20ebu4101";
302-
msg = "white_noise_after_both_in_bed must turn on the whitenoise machine";
312+
hasActionCall (toList (whiteNoiseWithBedtimeAudiobook.action or [ ])) "switch.turn_on"
313+
&& hasTargetEntity (toList (
314+
whiteNoiseWithBedtimeAudiobook.action or [ ]
315+
)) "switch.eve_energy_20ebu4101";
316+
msg = "white_noise_with_bedtime_audiobook must turn on the whitenoise machine";
303317
}
304318
{
305319
test = syncIphoneAlarm8sleep != null && (syncIphoneAlarm8sleep.initial_state or null) == false;
@@ -494,11 +508,11 @@ let
494508
}
495509
{
496510
test = (goodNightScene.entities."switch.eve_energy_20ebu4101" or null) == "off";
497-
msg = "Good Night scene must leave whitenoise off until bed-presence gate";
511+
msg = "Good Night scene must leave whitenoise off until audiobook speaker gate";
498512
}
499513
{
500514
test = (sleepScene.entities."switch.eve_energy_20ebu4101" or null) == "off";
501-
msg = "Sleep scene must leave whitenoise off until bed-presence gate";
515+
msg = "Sleep scene must leave whitenoise off until audiobook speaker gate";
502516
}
503517
{
504518
test = (sleepScene.entities."switch.desk_monitor" or null) == "off";

0 commit comments

Comments
 (0)