Edit esp-bedside-panel.yaml

This commit is contained in:
ESPHome Device Builder
2026-07-20 12:19:25 +12:00
parent ea604b8a30
commit f07cd6d0c4
+156 -67
View File
@@ -6,6 +6,7 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedside-panel.yaml
#:########################################################################################:#
# VERSIONS:
# V1.41 2026-07-20 Fixed raw alarm audio startup and buffering so tones play instead of amplifier clicks
# V1.40 2026-07-20 Added ES8311 speaker output, persistent alarm volume and local speaker test controls
# V1.39 2026-07-19 Removed the Alarm Clock editor title format-truncation warning
# V1.38 2026-07-19 Added per-alarm Snooze controls and a reusable 24-hour time editor
@@ -165,7 +166,7 @@ substitutions:
# Project Naming
project_name: "Guition.JC1060P470C_I_W_Y"
project_version: "v1.40"
project_version: "v1.41"
# Passwords & Secrets
api_key: !secret esp-api_key
@@ -238,7 +239,7 @@ substitutions:
alarm_i2s_mclk_pin: "GPIO13"
alarm_i2s_dout_pin: "GPIO9"
alarm_speaker_enable_pin: "GPIO11"
alarm_audio_sample_rate: "16000"
alarm_audio_sample_rate: "48000"
# Security Camera Settings
# Change this only if homeassistant.local is not reachable from the panel.
@@ -572,7 +573,10 @@ speaker:
sample_rate: "${alarm_audio_sample_rate}"
bits_per_sample: 16bit
channel: mono
buffer_duration: 100ms
i2s_comm_fmt: stand_i2s
mclk_multiple: 256
buffer_duration: 500ms
timeout: never
#:########################################################################################:#
# OUTPUT:
@@ -2361,19 +2365,33 @@ script:
? std::string("Alarm\nON")
: std::string("Alarm");
- id: alarm_audio_beep
# Start the I2S task before submitting the real sound. A first speaker.play
# call made while the speaker is stopped starts the task, but may not accept
# the supplied audio bytes immediately. Priming it with silence prevents the
# audible test clip from being discarded during that startup transition.
- id: prepare_alarm_speaker
mode: restart
then:
- lambda: |-
id(alarm_audio_test_active) = false;
- audio_dac.mute_on:
id: alarm_audio_dac
- script.stop: alarm_audio_alarm_test
- switch.turn_off: alarm_speaker_amplifier
- speaker.stop:
id: alarm_speaker
- switch.turn_on: alarm_speaker_amplifier
- delay: 40ms
- wait_until:
condition:
speaker.is_stopped:
id: alarm_speaker
timeout: 750ms
# This first small write starts the I2S task. It is intentionally silent.
- speaker.play:
id: alarm_speaker
data: [0, 0, 0, 0]
- delay: 150ms
- audio_dac.set_volume:
id: alarm_audio_dac
@@ -2384,22 +2402,38 @@ script:
}
return volume / 100.0f;
- switch.turn_on: alarm_speaker_amplifier
- delay: 20ms
- audio_dac.mute_off:
id: alarm_audio_dac
- delay: 20ms
- id: alarm_audio_beep
mode: restart
then:
- lambda: |-
id(alarm_audio_test_active) = false;
- script.stop: alarm_audio_alarm_test
- script.execute: prepare_alarm_speaker
- script.wait: prepare_alarm_speaker
- lvgl.label.update:
id: alarm_audio_beep_label
text: "Beep\nON"
- speaker.play:
id: alarm_speaker
data: !lambda |-
const uint32_t sample_rate = 16000;
const uint32_t duration_ms = 360;
# Generate signed 16-bit little-endian mono PCM. Feed the ring buffer in
# a loop so the complete sound is queued even when it is larger than the
# speaker buffer's immediately available space.
- lambda: |-
const uint32_t sample_rate = 48000;
const uint32_t duration_ms = 480;
const uint32_t sample_count =
(sample_rate * duration_ms) / 1000;
const float frequency = 880.0f;
const uint32_t fade_samples = sample_rate / 100;
const uint32_t fade_samples = sample_rate / 80;
std::vector<uint8_t> audio;
audio.reserve(sample_count * 2);
@@ -2408,11 +2442,9 @@ script:
float envelope = 1.0f;
if (i < fade_samples) {
envelope =
static_cast<float>(i) / fade_samples;
envelope = static_cast<float>(i) / fade_samples;
} else if (i > sample_count - fade_samples) {
envelope =
static_cast<float>(sample_count - i) /
envelope = static_cast<float>(sample_count - i) /
fade_samples;
}
@@ -2420,29 +2452,63 @@ script:
6.28318530718f * frequency *
static_cast<float>(i) / sample_rate;
const int16_t sample = static_cast<int16_t>(
12000.0f * envelope * sinf(phase)
14000.0f * envelope * sinf(phase)
);
audio.push_back(
static_cast<uint8_t>(sample & 0xFF)
);
audio.push_back(static_cast<uint8_t>(sample & 0xFF));
audio.push_back(
static_cast<uint8_t>((sample >> 8) & 0xFF)
);
}
return audio;
size_t offset = 0;
uint8_t zero_write_count = 0;
- delay: 430ms
while (offset < audio.size()) {
const size_t written = id(alarm_speaker).play(
audio.data() + offset,
audio.size() - offset,
pdMS_TO_TICKS(250)
);
if (written == 0) {
zero_write_count++;
if (zero_write_count >= 8) {
ESP_LOGE(
"alarm_audio",
"Beep playback stalled after %u of %u bytes",
static_cast<unsigned>(offset),
static_cast<unsigned>(audio.size())
);
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
} else {
offset += written;
zero_write_count = 0;
}
}
ESP_LOGD(
"alarm_audio",
"Queued beep audio: %u of %u bytes",
static_cast<unsigned>(offset),
static_cast<unsigned>(audio.size())
);
- speaker.finish:
id: alarm_speaker
- delay: 80ms
- wait_until:
condition:
speaker.is_stopped:
id: alarm_speaker
timeout: 2s
- audio_dac.mute_on:
id: alarm_audio_dac
- delay: 20ms
- switch.turn_off: alarm_speaker_amplifier
- lvgl.label.update:
@@ -2456,28 +2522,12 @@ script:
then:
- script.stop: alarm_audio_beep
- speaker.stop:
id: alarm_speaker
- lambda: |-
id(alarm_audio_test_active) = true;
- script.execute: refresh_alarm_audio_controls
- switch.turn_on: alarm_speaker_amplifier
- delay: 40ms
- audio_dac.set_volume:
id: alarm_audio_dac
volume: !lambda |-
float volume = id(alarm_volume).state;
if (isnan(volume)) {
volume = 35.0f;
}
return volume / 100.0f;
- audio_dac.mute_off:
id: alarm_audio_dac
- script.execute: prepare_alarm_speaker
- script.wait: prepare_alarm_speaker
- while:
condition:
@@ -2485,14 +2535,14 @@ script:
return id(alarm_audio_test_active);
then:
- speaker.play:
id: alarm_speaker
data: !lambda |-
const uint32_t sample_rate = 16000;
const uint32_t duration_ms = 900;
# A two-tone 800 ms alarm cycle. The direct write loop prevents
# truncation when the clip is larger than the free ring-buffer area.
- lambda: |-
const uint32_t sample_rate = 48000;
const uint32_t duration_ms = 800;
const uint32_t sample_count =
(sample_rate * duration_ms) / 1000;
const uint32_t fade_samples = sample_rate / 80;
const uint32_t fade_samples = sample_rate / 100;
std::vector<uint8_t> audio;
audio.reserve(sample_count * 2);
@@ -2500,21 +2550,19 @@ script:
for (uint32_t i = 0; i < sample_count; i++) {
const uint32_t time_ms =
(i * 1000) / sample_rate;
float frequency = 0.0f;
uint32_t burst_start = 0;
uint32_t burst_end = 0;
if (time_ms < 280) {
if (time_ms < 300) {
frequency = 740.0f;
burst_start = 0;
burst_end =
(sample_rate * 280) / 1000;
} else if (time_ms >= 360 && time_ms < 640) {
burst_end = (sample_rate * 300) / 1000;
} else if (time_ms >= 400 && time_ms < 700) {
frequency = 990.0f;
burst_start =
(sample_rate * 360) / 1000;
burst_end =
(sample_rate * 640) / 1000;
burst_start = (sample_rate * 400) / 1000;
burst_end = (sample_rate * 700) / 1000;
}
int16_t sample = 0;
@@ -2523,20 +2571,18 @@ script:
float envelope = 1.0f;
if (i < burst_start + fade_samples) {
envelope = static_cast<float>(
i - burst_start
) / fade_samples;
envelope = static_cast<float>(i - burst_start) /
fade_samples;
} else if (i > burst_end - fade_samples) {
envelope = static_cast<float>(
burst_end - i
) / fade_samples;
envelope = static_cast<float>(burst_end - i) /
fade_samples;
}
const float phase =
6.28318530718f * frequency *
static_cast<float>(i) / sample_rate;
sample = static_cast<int16_t>(
12000.0f * envelope * sinf(phase)
14000.0f * envelope * sinf(phase)
);
}
@@ -2548,9 +2594,52 @@ script:
);
}
return audio;
size_t offset = 0;
uint8_t zero_write_count = 0;
- delay: 950ms
while (
offset < audio.size() &&
id(alarm_audio_test_active)
) {
const size_t written = id(alarm_speaker).play(
audio.data() + offset,
audio.size() - offset,
pdMS_TO_TICKS(250)
);
if (written == 0) {
zero_write_count++;
if (zero_write_count >= 8) {
ESP_LOGE(
"alarm_audio",
"Alarm playback stalled after %u of %u bytes",
static_cast<unsigned>(offset),
static_cast<unsigned>(audio.size())
);
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
} else {
offset += written;
zero_write_count = 0;
}
}
ESP_LOGV(
"alarm_audio",
"Queued alarm cycle: %u of %u bytes",
static_cast<unsigned>(offset),
static_cast<unsigned>(audio.size())
);
- wait_until:
condition:
not:
speaker.is_playing:
id: alarm_speaker
timeout: 2s
- delay: 250ms
- id: stop_alarm_audio
mode: restart