[speaker] Add off on capability to media player (#9295)

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Kevin Ahrendt <kevin.ahrendt@openhomefoundation.org>
This commit is contained in:
rwrozelle
2026-03-03 07:59:01 -08:00
committed by GitHub
parent cfde0613bb
commit b6f0bb9b6b
6 changed files with 97 additions and 0 deletions

View File

@@ -15,6 +15,8 @@ from esphome.const import (
CONF_FORMAT,
CONF_ID,
CONF_NUM_CHANNELS,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_PATH,
CONF_RAW_DATA_ID,
CONF_SAMPLE_RATE,
@@ -401,6 +403,9 @@ FINAL_VALIDATE_SCHEMA = cv.All(
async def to_code(config):
if CONF_ON_TURN_OFF in config or CONF_ON_TURN_ON in config:
cg.add_define("USE_SPEAKER_MEDIA_PLAYER_ON_OFF", True)
var = await media_player.new_media_player(config)
await cg.register_component(var, config)

View File

@@ -51,7 +51,11 @@ static const UBaseType_t ANNOUNCEMENT_PIPELINE_TASK_PRIORITY = 1;
static const char *const TAG = "speaker_media_player";
void SpeakerMediaPlayer::setup() {
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
state = media_player::MEDIA_PLAYER_STATE_OFF;
#else
state = media_player::MEDIA_PLAYER_STATE_IDLE;
#endif
this->media_control_command_queue_ = xQueueCreate(MEDIA_CONTROLS_QUEUE_LENGTH, sizeof(MediaCallCommand));
@@ -128,6 +132,12 @@ void SpeakerMediaPlayer::watch_media_commands_() {
bool enqueue = media_command.enqueue.has_value() && media_command.enqueue.value();
if (media_command.url.has_value() || media_command.file.has_value()) {
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
if (this->state == media_player::MEDIA_PLAYER_STATE_OFF) {
this->state = media_player::MEDIA_PLAYER_STATE_ON;
publish_state();
}
#endif
PlaylistItem playlist_item;
if (media_command.url.has_value()) {
playlist_item.url = *media_command.url.value();
@@ -184,6 +194,12 @@ void SpeakerMediaPlayer::watch_media_commands_() {
if (media_command.command.has_value()) {
switch (media_command.command.value()) {
case media_player::MEDIA_PLAYER_COMMAND_PLAY:
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
if (this->state == media_player::MEDIA_PLAYER_STATE_OFF) {
this->state = media_player::MEDIA_PLAYER_STATE_ON;
publish_state();
}
#endif
if ((this->media_pipeline_ != nullptr) && (this->is_paused_)) {
this->media_pipeline_->set_pause_state(false);
}
@@ -195,10 +211,26 @@ void SpeakerMediaPlayer::watch_media_commands_() {
}
this->is_paused_ = true;
break;
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
case media_player::MEDIA_PLAYER_COMMAND_TURN_ON:
if (this->state == media_player::MEDIA_PLAYER_STATE_OFF) {
this->state = media_player::MEDIA_PLAYER_STATE_ON;
this->publish_state();
}
break;
case media_player::MEDIA_PLAYER_COMMAND_TURN_OFF:
this->is_turn_off_ = true;
// Intentional Fall-through
#endif
case media_player::MEDIA_PLAYER_COMMAND_STOP:
// Pipelines do not stop immediately after calling the stop command, so confirm its stopped before unpausing.
// This avoids an audible short segment playing after receiving the stop command in a paused state.
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value()) ||
(this->is_turn_off_ && this->announcement_pipeline_state_ != AudioPipelineState::STOPPED)) {
#else
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
#endif
if (this->announcement_pipeline_ != nullptr) {
this->cancel_timeout("next_ann");
this->announcement_playlist_.clear();
@@ -366,7 +398,13 @@ void SpeakerMediaPlayer::loop() {
}
} else {
if (this->is_paused_) {
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
if (this->state != media_player::MEDIA_PLAYER_STATE_OFF) {
this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
}
#else
this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
#endif
} else if (this->media_pipeline_state_ == AudioPipelineState::PLAYING) {
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
} else if (this->media_pipeline_state_ == AudioPipelineState::STOPPED) {
@@ -399,7 +437,13 @@ void SpeakerMediaPlayer::loop() {
}
}
} else {
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
if (this->state != media_player::MEDIA_PLAYER_STATE_OFF) {
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
}
#else
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
#endif
}
}
}
@@ -409,6 +453,20 @@ void SpeakerMediaPlayer::loop() {
this->publish_state();
ESP_LOGD(TAG, "State changed to %s", media_player::media_player_state_to_string(this->state));
}
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
if (this->is_turn_off_ && (this->state == media_player::MEDIA_PLAYER_STATE_PAUSED ||
this->state == media_player::MEDIA_PLAYER_STATE_IDLE)) {
this->is_turn_off_ = false;
if (this->state == media_player::MEDIA_PLAYER_STATE_PAUSED) {
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
this->publish_state();
ESP_LOGD(TAG, "State changed to %s", media_player::media_player_state_to_string(this->state));
}
this->state = media_player::MEDIA_PLAYER_STATE_OFF;
this->publish_state();
ESP_LOGD(TAG, "State changed to %s", media_player::media_player_state_to_string(this->state));
}
#endif
}
void SpeakerMediaPlayer::play_file(audio::AudioFile *media_file, bool announcement, bool enqueue) {
@@ -481,6 +539,9 @@ media_player::MediaPlayerTraits SpeakerMediaPlayer::get_traits() {
if (!this->single_pipeline_()) {
traits.set_supports_pause(true);
}
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
traits.set_supports_turn_off_on(true);
#endif
if (this->announcement_format_.has_value()) {
traits.get_supported_formats().push_back(this->announcement_format_.value());

View File

@@ -144,6 +144,9 @@ class SpeakerMediaPlayer : public Component,
bool is_paused_{false};
bool is_muted_{false};
#ifdef USE_SPEAKER_MEDIA_PLAYER_ON_OFF
bool is_turn_off_{false};
#endif
uint8_t unpause_media_remaining_{0};
uint8_t unpause_announcement_remaining_{0};

View File

@@ -233,6 +233,7 @@
#define USE_LWIP_FAST_SELECT
#define USE_WAKE_LOOP_THREADSAFE
#define USE_SPEAKER
#define USE_SPEAKER_MEDIA_PLAYER_ON_OFF
#define USE_SPI
#define USE_VOICE_ASSISTANT
#define USE_WEBSERVER

View File

@@ -0,0 +1,18 @@
<<: !include common.yaml
media_player:
- platform: speaker
id: speaker_media_player_id
announcement_pipeline:
speaker: speaker_id
buffer_size: 1000000
volume_increment: 0.02
volume_max: 0.95
volume_min: 0.0
task_stack_in_psram: true
on_turn_on:
then:
- logger.log: "Turn On Media Player"
on_turn_off:
then:
- logger.log: "Turn Off Media Player"

View File

@@ -0,0 +1,9 @@
substitutions:
scl_pin: GPIO16
sda_pin: GPIO17
i2s_bclk_pin: GPIO27
i2s_lrclk_pin: GPIO26
i2s_mclk_pin: GPIO25
i2s_dout_pin: GPIO23
<<: !include common-media_player_off_on.yaml