Edit esp-bedside-panel.yaml
This commit is contained in:
+212
-123
@@ -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,65 +2402,113 @@ 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;
|
||||
const uint32_t sample_count =
|
||||
(sample_rate * duration_ms) / 1000;
|
||||
const float frequency = 880.0f;
|
||||
const uint32_t fade_samples = sample_rate / 100;
|
||||
# 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 / 80;
|
||||
|
||||
std::vector<uint8_t> audio;
|
||||
audio.reserve(sample_count * 2);
|
||||
std::vector<uint8_t> audio;
|
||||
audio.reserve(sample_count * 2);
|
||||
|
||||
for (uint32_t i = 0; i < sample_count; i++) {
|
||||
float envelope = 1.0f;
|
||||
for (uint32_t i = 0; i < sample_count; i++) {
|
||||
float envelope = 1.0f;
|
||||
|
||||
if (i < fade_samples) {
|
||||
envelope =
|
||||
static_cast<float>(i) / fade_samples;
|
||||
} else if (i > sample_count - fade_samples) {
|
||||
envelope =
|
||||
static_cast<float>(sample_count - i) /
|
||||
fade_samples;
|
||||
}
|
||||
|
||||
const float phase =
|
||||
6.28318530718f * frequency *
|
||||
static_cast<float>(i) / sample_rate;
|
||||
const int16_t sample = static_cast<int16_t>(
|
||||
12000.0f * envelope * sinf(phase)
|
||||
);
|
||||
|
||||
audio.push_back(
|
||||
static_cast<uint8_t>(sample & 0xFF)
|
||||
);
|
||||
audio.push_back(
|
||||
static_cast<uint8_t>((sample >> 8) & 0xFF)
|
||||
);
|
||||
if (i < fade_samples) {
|
||||
envelope = static_cast<float>(i) / fade_samples;
|
||||
} else if (i > sample_count - fade_samples) {
|
||||
envelope = static_cast<float>(sample_count - i) /
|
||||
fade_samples;
|
||||
}
|
||||
|
||||
return audio;
|
||||
const float phase =
|
||||
6.28318530718f * frequency *
|
||||
static_cast<float>(i) / sample_rate;
|
||||
const int16_t sample = static_cast<int16_t>(
|
||||
14000.0f * envelope * sinf(phase)
|
||||
);
|
||||
|
||||
- delay: 430ms
|
||||
audio.push_back(static_cast<uint8_t>(sample & 0xFF));
|
||||
audio.push_back(
|
||||
static_cast<uint8_t>((sample >> 8) & 0xFF)
|
||||
);
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
uint8_t zero_write_count = 0;
|
||||
|
||||
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,72 +2535,111 @@ 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;
|
||||
const uint32_t sample_count =
|
||||
(sample_rate * duration_ms) / 1000;
|
||||
const uint32_t fade_samples = sample_rate / 80;
|
||||
# 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 / 100;
|
||||
|
||||
std::vector<uint8_t> audio;
|
||||
audio.reserve(sample_count * 2);
|
||||
std::vector<uint8_t> audio;
|
||||
audio.reserve(sample_count * 2);
|
||||
|
||||
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;
|
||||
for (uint32_t i = 0; i < sample_count; i++) {
|
||||
const uint32_t time_ms =
|
||||
(i * 1000) / sample_rate;
|
||||
|
||||
if (time_ms < 280) {
|
||||
frequency = 740.0f;
|
||||
burst_start = 0;
|
||||
burst_end =
|
||||
(sample_rate * 280) / 1000;
|
||||
} else if (time_ms >= 360 && time_ms < 640) {
|
||||
frequency = 990.0f;
|
||||
burst_start =
|
||||
(sample_rate * 360) / 1000;
|
||||
burst_end =
|
||||
(sample_rate * 640) / 1000;
|
||||
float frequency = 0.0f;
|
||||
uint32_t burst_start = 0;
|
||||
uint32_t burst_end = 0;
|
||||
|
||||
if (time_ms < 300) {
|
||||
frequency = 740.0f;
|
||||
burst_start = 0;
|
||||
burst_end = (sample_rate * 300) / 1000;
|
||||
} else if (time_ms >= 400 && time_ms < 700) {
|
||||
frequency = 990.0f;
|
||||
burst_start = (sample_rate * 400) / 1000;
|
||||
burst_end = (sample_rate * 700) / 1000;
|
||||
}
|
||||
|
||||
int16_t sample = 0;
|
||||
|
||||
if (frequency > 0.0f) {
|
||||
float envelope = 1.0f;
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
int16_t sample = 0;
|
||||
|
||||
if (frequency > 0.0f) {
|
||||
float envelope = 1.0f;
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
const float phase =
|
||||
6.28318530718f * frequency *
|
||||
static_cast<float>(i) / sample_rate;
|
||||
sample = static_cast<int16_t>(
|
||||
12000.0f * envelope * sinf(phase)
|
||||
);
|
||||
}
|
||||
|
||||
audio.push_back(
|
||||
static_cast<uint8_t>(sample & 0xFF)
|
||||
);
|
||||
audio.push_back(
|
||||
static_cast<uint8_t>((sample >> 8) & 0xFF)
|
||||
const float phase =
|
||||
6.28318530718f * frequency *
|
||||
static_cast<float>(i) / sample_rate;
|
||||
sample = static_cast<int16_t>(
|
||||
14000.0f * envelope * sinf(phase)
|
||||
);
|
||||
}
|
||||
|
||||
return audio;
|
||||
audio.push_back(
|
||||
static_cast<uint8_t>(sample & 0xFF)
|
||||
);
|
||||
audio.push_back(
|
||||
static_cast<uint8_t>((sample >> 8) & 0xFF)
|
||||
);
|
||||
}
|
||||
|
||||
- delay: 950ms
|
||||
size_t offset = 0;
|
||||
uint8_t zero_write_count = 0;
|
||||
|
||||
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
|
||||
@@ -11700,4 +11789,4 @@ lvgl:
|
||||
text: ""
|
||||
text_font: font_small
|
||||
text_color: 0x887799
|
||||
clickable: false
|
||||
clickable: false
|
||||
|
||||
Reference in New Issue
Block a user