From fd2fe422d08d4849b729335c991e10b555125385 Mon Sep 17 00:00:00 2001 From: ESPHome Device Builder Date: Mon, 20 Jul 2026 12:30:38 +1200 Subject: [PATCH] Edit esp-bedside-panel.yaml --- esphome/esp-bedside-panel.yaml | 122 ++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 46 deletions(-) diff --git a/esphome/esp-bedside-panel.yaml b/esphome/esp-bedside-panel.yaml index b6d7709..51b3379 100644 --- a/esphome/esp-bedside-panel.yaml +++ b/esphome/esp-bedside-panel.yaml @@ -6,6 +6,7 @@ # https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedside-panel.yaml #:########################################################################################:# # VERSIONS: +# V1.42 2026-07-20 Changed tests to higher single-tone sounds and added a useful perceptual volume curve # 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 @@ -141,7 +142,10 @@ # - Tapping a time opens a shared circular 24-hour hour/minute roller editor. # - The Snooze duration is a persistent Home Assistant configuration entity. # - Alarm speaker volume is persistent and adjustable from Home Assistant or the display. +# - The ES8311 remains at its 75% / 0 dB reference while local PCM amplitude +# follows a perceptual 24 dB volume curve across the displayed 5-100% range. # - The Alarm Clock page provides local Beep, repeating Alarm and Stop hardware tests. +# - Beep is a short 1800 Hz tone; Alarm repeats one 1600 Hz tone. # - Speaker tests generate audio locally and do not require Home Assistant or media files. #:########################################################################################:# # OFFLINE NOTES: @@ -161,12 +165,12 @@ substitutions: # Device Naming device_name: "esp-bedside-panel" friendly_name: "ESP Bedside Panel" - description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Four-row Alarm Clock interface with local ES8311 speaker testing, persistent alarm volume and a reusable 24-hour roller time editor. (Layout V1.1)" + description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Four-row Alarm Clock interface with higher-pitched single-tone speaker testing, perceptual PCM volume control and a reusable 24-hour roller time editor. (Layout V1.1)" device_area: "Bedroom" # Project Naming project_name: "Guition.JC1060P470C_I_W_Y" - project_version: "v1.41" + project_version: "v1.42" # Passwords & Secrets api_key: !secret esp-api_key @@ -241,6 +245,18 @@ substitutions: alarm_speaker_enable_pin: "GPIO11" alarm_audio_sample_rate: "48000" + # Alarm Test Sound + # The ES8311 uses 75% as its 0 dB reference. Keep the codec at that + # reference and apply the user volume to the generated PCM samples. + alarm_dac_reference_volume: "0.75" + alarm_pcm_max_amplitude: "28000" + alarm_volume_attenuation_db: "24" + alarm_beep_frequency_hz: "1800" + alarm_beep_duration_ms: "250" + alarm_tone_frequency_hz: "1600" + alarm_tone_on_ms: "350" + alarm_tone_off_ms: "250" + # Security Camera Settings # Change this only if homeassistant.local is not reachable from the panel. home_assistant_internal_url: "http://homeassistant.local:8123" @@ -1042,7 +1058,7 @@ number: - audio_dac.set_volume: id: alarm_audio_dac volume: !lambda |- - return x / 100.0f; + return ${alarm_dac_reference_volume}f; - script.execute: refresh_alarm_audio_controls @@ -2319,11 +2335,7 @@ script: - 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; + return ${alarm_dac_reference_volume}f; - if: condition: @@ -2396,11 +2408,7 @@ script: - 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; + return ${alarm_dac_reference_volume}f; - switch.turn_on: alarm_speaker_amplifier - delay: 20ms @@ -2428,12 +2436,30 @@ script: # 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_rate = ${alarm_audio_sample_rate}; + const uint32_t duration_ms = ${alarm_beep_duration_ms}; const uint32_t sample_count = (sample_rate * duration_ms) / 1000; - const float frequency = 880.0f; - const uint32_t fade_samples = sample_rate / 80; + const float frequency = ${alarm_beep_frequency_hz}.0f; + const uint32_t fade_samples = sample_rate / 100; + + float ui_volume = id(alarm_volume).state; + if (isnan(ui_volume)) { + ui_volume = ${alarm_volume_default}.0f; + } + ui_volume = fminf(fmaxf(ui_volume, 5.0f), 100.0f); + + // Equal steps in the displayed percentage produce roughly equal + // perceived loudness changes. At 75%, this gives approximately the + // same PCM level as the proven V1.41 test at the codec's 0 dB point. + const float volume_position = + (ui_volume - 5.0f) / 95.0f; + const float attenuation_db = + -${alarm_volume_attenuation_db}.0f * + (1.0f - volume_position); + const float peak_amplitude = + ${alarm_pcm_max_amplitude}.0f * + powf(10.0f, attenuation_db / 20.0f); std::vector audio; audio.reserve(sample_count * 2); @@ -2452,7 +2478,7 @@ script: 6.28318530718f * frequency * static_cast(i) / sample_rate; const int16_t sample = static_cast( - 14000.0f * envelope * sinf(phase) + peak_amplitude * envelope * sinf(phase) ); audio.push_back(static_cast(sample & 0xFF)); @@ -2535,46 +2561,50 @@ script: return id(alarm_audio_test_active); then: - # A two-tone 800 ms alarm cycle. The direct write loop prevents - # truncation when the clip is larger than the free ring-buffer area. + # A higher-pitched single-tone alarm pulse. 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_rate = ${alarm_audio_sample_rate}; + const uint32_t tone_ms = ${alarm_tone_on_ms}; + const uint32_t silence_ms = ${alarm_tone_off_ms}; + const uint32_t duration_ms = tone_ms + silence_ms; const uint32_t sample_count = (sample_rate * duration_ms) / 1000; + const uint32_t tone_end = + (sample_rate * tone_ms) / 1000; const uint32_t fade_samples = sample_rate / 100; + const float frequency = ${alarm_tone_frequency_hz}.0f; + + float ui_volume = id(alarm_volume).state; + if (isnan(ui_volume)) { + ui_volume = ${alarm_volume_default}.0f; + } + ui_volume = fminf(fmaxf(ui_volume, 5.0f), 100.0f); + + const float volume_position = + (ui_volume - 5.0f) / 95.0f; + const float attenuation_db = + -${alarm_volume_attenuation_db}.0f * + (1.0f - volume_position); + const float peak_amplitude = + ${alarm_pcm_max_amplitude}.0f * + powf(10.0f, attenuation_db / 20.0f); std::vector 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; - - 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) { + if (i < tone_end) { float envelope = 1.0f; - if (i < burst_start + fade_samples) { - envelope = static_cast(i - burst_start) / + if (i < fade_samples) { + envelope = static_cast(i) / fade_samples; - } else if (i > burst_end - fade_samples) { - envelope = static_cast(burst_end - i) / + } else if (i > tone_end - fade_samples) { + envelope = static_cast(tone_end - i) / fade_samples; } @@ -2582,7 +2612,7 @@ script: 6.28318530718f * frequency * static_cast(i) / sample_rate; sample = static_cast( - 14000.0f * envelope * sinf(phase) + peak_amplitude * envelope * sinf(phase) ); } @@ -2639,7 +2669,7 @@ script: id: alarm_speaker timeout: 2s - - delay: 250ms + - delay: 20ms - id: stop_alarm_audio mode: restart