media_player add off on capability (#9294)

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
This commit is contained in:
rwrozelle 2025-07-30 18:02:53 -04:00 committed by GitHub
parent f4f69e827b
commit 79533cb0d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 97 additions and 2 deletions

View File

@ -1298,6 +1298,8 @@ enum MediaPlayerState {
MEDIA_PLAYER_STATE_PLAYING = 2; MEDIA_PLAYER_STATE_PLAYING = 2;
MEDIA_PLAYER_STATE_PAUSED = 3; MEDIA_PLAYER_STATE_PAUSED = 3;
MEDIA_PLAYER_STATE_ANNOUNCING = 4; MEDIA_PLAYER_STATE_ANNOUNCING = 4;
MEDIA_PLAYER_STATE_OFF = 5;
MEDIA_PLAYER_STATE_ON = 6;
} }
enum MediaPlayerCommand { enum MediaPlayerCommand {
MEDIA_PLAYER_COMMAND_PLAY = 0; MEDIA_PLAYER_COMMAND_PLAY = 0;
@ -1312,6 +1314,8 @@ enum MediaPlayerCommand {
MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9; MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9;
MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10; MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10;
MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11; MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11;
MEDIA_PLAYER_COMMAND_TURN_ON = 12;
MEDIA_PLAYER_COMMAND_TURN_OFF = 13;
} }
enum MediaPlayerFormatPurpose { enum MediaPlayerFormatPurpose {
MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0; MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0;

View File

@ -151,6 +151,8 @@ enum MediaPlayerState : uint32_t {
MEDIA_PLAYER_STATE_PLAYING = 2, MEDIA_PLAYER_STATE_PLAYING = 2,
MEDIA_PLAYER_STATE_PAUSED = 3, MEDIA_PLAYER_STATE_PAUSED = 3,
MEDIA_PLAYER_STATE_ANNOUNCING = 4, MEDIA_PLAYER_STATE_ANNOUNCING = 4,
MEDIA_PLAYER_STATE_OFF = 5,
MEDIA_PLAYER_STATE_ON = 6,
}; };
enum MediaPlayerCommand : uint32_t { enum MediaPlayerCommand : uint32_t {
MEDIA_PLAYER_COMMAND_PLAY = 0, MEDIA_PLAYER_COMMAND_PLAY = 0,
@ -165,6 +167,8 @@ enum MediaPlayerCommand : uint32_t {
MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9, MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9,
MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10, MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10,
MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11, MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11,
MEDIA_PLAYER_COMMAND_TURN_ON = 12,
MEDIA_PLAYER_COMMAND_TURN_OFF = 13,
}; };
enum MediaPlayerFormatPurpose : uint32_t { enum MediaPlayerFormatPurpose : uint32_t {
MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0, MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0,

View File

@ -385,6 +385,10 @@ template<> const char *proto_enum_to_string<enums::MediaPlayerState>(enums::Medi
return "MEDIA_PLAYER_STATE_PAUSED"; return "MEDIA_PLAYER_STATE_PAUSED";
case enums::MEDIA_PLAYER_STATE_ANNOUNCING: case enums::MEDIA_PLAYER_STATE_ANNOUNCING:
return "MEDIA_PLAYER_STATE_ANNOUNCING"; return "MEDIA_PLAYER_STATE_ANNOUNCING";
case enums::MEDIA_PLAYER_STATE_OFF:
return "MEDIA_PLAYER_STATE_OFF";
case enums::MEDIA_PLAYER_STATE_ON:
return "MEDIA_PLAYER_STATE_ON";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
@ -415,6 +419,10 @@ template<> const char *proto_enum_to_string<enums::MediaPlayerCommand>(enums::Me
return "MEDIA_PLAYER_COMMAND_REPEAT_OFF"; return "MEDIA_PLAYER_COMMAND_REPEAT_OFF";
case enums::MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST: case enums::MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST:
return "MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST"; return "MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST";
case enums::MEDIA_PLAYER_COMMAND_TURN_ON:
return "MEDIA_PLAYER_COMMAND_TURN_ON";
case enums::MEDIA_PLAYER_COMMAND_TURN_OFF:
return "MEDIA_PLAYER_COMMAND_TURN_OFF";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }

View File

@ -7,6 +7,8 @@ from esphome.const import (
CONF_ID, CONF_ID,
CONF_ON_IDLE, CONF_ON_IDLE,
CONF_ON_STATE, CONF_ON_STATE,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_VOLUME, CONF_VOLUME,
) )
@ -58,6 +60,12 @@ VolumeDownAction = media_player_ns.class_(
VolumeSetAction = media_player_ns.class_( VolumeSetAction = media_player_ns.class_(
"VolumeSetAction", automation.Action, cg.Parented.template(MediaPlayer) "VolumeSetAction", automation.Action, cg.Parented.template(MediaPlayer)
) )
TurnOnAction = media_player_ns.class_(
"TurnOnAction", automation.Action, cg.Parented.template(MediaPlayer)
)
TurnOffAction = media_player_ns.class_(
"TurnOffAction", automation.Action, cg.Parented.template(MediaPlayer)
)
CONF_ANNOUNCEMENT = "announcement" CONF_ANNOUNCEMENT = "announcement"
CONF_ON_PLAY = "on_play" CONF_ON_PLAY = "on_play"
@ -72,12 +80,16 @@ PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.templat
AnnoucementTrigger = media_player_ns.class_( AnnoucementTrigger = media_player_ns.class_(
"AnnouncementTrigger", automation.Trigger.template() "AnnouncementTrigger", automation.Trigger.template()
) )
OnTrigger = media_player_ns.class_("OnTrigger", automation.Trigger.template())
OffTrigger = media_player_ns.class_("OffTrigger", automation.Trigger.template())
IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition) IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition)
IsPausedCondition = media_player_ns.class_("IsPausedCondition", automation.Condition) IsPausedCondition = media_player_ns.class_("IsPausedCondition", automation.Condition)
IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition) IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition)
IsAnnouncingCondition = media_player_ns.class_( IsAnnouncingCondition = media_player_ns.class_(
"IsAnnouncingCondition", automation.Condition "IsAnnouncingCondition", automation.Condition
) )
IsOnCondition = media_player_ns.class_("IsOnCondition", automation.Condition)
IsOffCondition = media_player_ns.class_("IsOffCondition", automation.Condition)
async def setup_media_player_core_(var, config): async def setup_media_player_core_(var, config):
@ -97,6 +109,12 @@ async def setup_media_player_core_(var, config):
for conf in config.get(CONF_ON_ANNOUNCEMENT, []): for conf in config.get(CONF_ON_ANNOUNCEMENT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_TURN_ON, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_TURN_OFF, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
async def register_media_player(var, config): async def register_media_player(var, config):
@ -140,6 +158,16 @@ _MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(AnnoucementTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(AnnoucementTrigger),
} }
), ),
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnTrigger),
}
),
cv.Optional(CONF_ON_TURN_OFF): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OffTrigger),
}
),
} }
) )
@ -218,6 +246,12 @@ async def media_player_play_media_action(config, action_id, template_arg, args):
@automation.register_action( @automation.register_action(
"media_player.volume_down", VolumeDownAction, MEDIA_PLAYER_ACTION_SCHEMA "media_player.volume_down", VolumeDownAction, MEDIA_PLAYER_ACTION_SCHEMA
) )
@automation.register_action(
"media_player.turn_on", TurnOnAction, MEDIA_PLAYER_ACTION_SCHEMA
)
@automation.register_action(
"media_player.turn_off", TurnOffAction, MEDIA_PLAYER_ACTION_SCHEMA
)
async def media_player_action(config, action_id, template_arg, args): async def media_player_action(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID]) await cg.register_parented(var, config[CONF_ID])
@ -238,6 +272,12 @@ async def media_player_action(config, action_id, template_arg, args):
@automation.register_condition( @automation.register_condition(
"media_player.is_announcing", IsAnnouncingCondition, MEDIA_PLAYER_CONDITION_SCHEMA "media_player.is_announcing", IsAnnouncingCondition, MEDIA_PLAYER_CONDITION_SCHEMA
) )
@automation.register_condition(
"media_player.is_on", IsOnCondition, MEDIA_PLAYER_CONDITION_SCHEMA
)
@automation.register_condition(
"media_player.is_off", IsOffCondition, MEDIA_PLAYER_CONDITION_SCHEMA
)
async def media_player_condition(config, action_id, template_arg, args): async def media_player_condition(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID]) await cg.register_parented(var, config[CONF_ID])

View File

@ -28,6 +28,10 @@ template<typename... Ts>
using VolumeUpAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_VOLUME_UP, Ts...>; using VolumeUpAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_VOLUME_UP, Ts...>;
template<typename... Ts> template<typename... Ts>
using VolumeDownAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_VOLUME_DOWN, Ts...>; using VolumeDownAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_VOLUME_DOWN, Ts...>;
template<typename... Ts>
using TurnOnAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_TURN_ON, Ts...>;
template<typename... Ts>
using TurnOffAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_TURN_OFF, Ts...>;
template<typename... Ts> class PlayMediaAction : public Action<Ts...>, public Parented<MediaPlayer> { template<typename... Ts> class PlayMediaAction : public Action<Ts...>, public Parented<MediaPlayer> {
TEMPLATABLE_VALUE(std::string, media_url) TEMPLATABLE_VALUE(std::string, media_url)
@ -66,6 +70,8 @@ using IdleTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE
using PlayTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING>; using PlayTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING>;
using PauseTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED>; using PauseTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED>;
using AnnouncementTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING>; using AnnouncementTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING>;
using OnTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_ON>;
using OffTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_OFF>;
template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> { template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
public: public:
@ -87,5 +93,15 @@ template<typename... Ts> class IsAnnouncingCondition : public Condition<Ts...>,
bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING; } bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING; }
}; };
template<typename... Ts> class IsOnCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
public:
bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ON; }
};
template<typename... Ts> class IsOffCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
public:
bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_OFF; }
};
} // namespace media_player } // namespace media_player
} // namespace esphome } // namespace esphome

View File

@ -9,6 +9,10 @@ static const char *const TAG = "media_player";
const char *media_player_state_to_string(MediaPlayerState state) { const char *media_player_state_to_string(MediaPlayerState state) {
switch (state) { switch (state) {
case MEDIA_PLAYER_STATE_ON:
return "ON";
case MEDIA_PLAYER_STATE_OFF:
return "OFF";
case MEDIA_PLAYER_STATE_IDLE: case MEDIA_PLAYER_STATE_IDLE:
return "IDLE"; return "IDLE";
case MEDIA_PLAYER_STATE_PLAYING: case MEDIA_PLAYER_STATE_PLAYING:
@ -18,6 +22,7 @@ const char *media_player_state_to_string(MediaPlayerState state) {
case MEDIA_PLAYER_STATE_ANNOUNCING: case MEDIA_PLAYER_STATE_ANNOUNCING:
return "ANNOUNCING"; return "ANNOUNCING";
case MEDIA_PLAYER_STATE_NONE: case MEDIA_PLAYER_STATE_NONE:
return "NONE";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
@ -49,6 +54,10 @@ const char *media_player_command_to_string(MediaPlayerCommand command) {
return "REPEAT_OFF"; return "REPEAT_OFF";
case MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST: case MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST:
return "CLEAR_PLAYLIST"; return "CLEAR_PLAYLIST";
case MEDIA_PLAYER_COMMAND_TURN_ON:
return "TURN_ON";
case MEDIA_PLAYER_COMMAND_TURN_OFF:
return "TURN_OFF";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
@ -110,6 +119,10 @@ MediaPlayerCall &MediaPlayerCall::set_command(const std::string &command) {
this->set_command(MEDIA_PLAYER_COMMAND_UNMUTE); this->set_command(MEDIA_PLAYER_COMMAND_UNMUTE);
} else if (str_equals_case_insensitive(command, "TOGGLE")) { } else if (str_equals_case_insensitive(command, "TOGGLE")) {
this->set_command(MEDIA_PLAYER_COMMAND_TOGGLE); this->set_command(MEDIA_PLAYER_COMMAND_TOGGLE);
} else if (str_equals_case_insensitive(command, "TURN_ON")) {
this->set_command(MEDIA_PLAYER_COMMAND_TURN_ON);
} else if (str_equals_case_insensitive(command, "TURN_OFF")) {
this->set_command(MEDIA_PLAYER_COMMAND_TURN_OFF);
} else { } else {
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command.c_str()); ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command.c_str());
} }

View File

@ -38,6 +38,8 @@ enum MediaPlayerState : uint8_t {
MEDIA_PLAYER_STATE_PLAYING = 2, MEDIA_PLAYER_STATE_PLAYING = 2,
MEDIA_PLAYER_STATE_PAUSED = 3, MEDIA_PLAYER_STATE_PAUSED = 3,
MEDIA_PLAYER_STATE_ANNOUNCING = 4, MEDIA_PLAYER_STATE_ANNOUNCING = 4,
MEDIA_PLAYER_STATE_OFF = 5,
MEDIA_PLAYER_STATE_ON = 6,
}; };
const char *media_player_state_to_string(MediaPlayerState state); const char *media_player_state_to_string(MediaPlayerState state);
@ -54,6 +56,8 @@ enum MediaPlayerCommand : uint8_t {
MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9, MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9,
MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10, MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10,
MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11, MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11,
MEDIA_PLAYER_COMMAND_TURN_ON = 12,
MEDIA_PLAYER_COMMAND_TURN_OFF = 13,
}; };
const char *media_player_command_to_string(MediaPlayerCommand command); const char *media_player_command_to_string(MediaPlayerCommand command);
@ -77,9 +81,11 @@ class MediaPlayerTraits {
MediaPlayerTraits() = default; MediaPlayerTraits() = default;
void set_supports_pause(bool supports_pause) { this->supports_pause_ = supports_pause; } void set_supports_pause(bool supports_pause) { this->supports_pause_ = supports_pause; }
bool get_supports_pause() const { return this->supports_pause_; } bool get_supports_pause() const { return this->supports_pause_; }
void set_supports_turn_off_on(bool supports_turn_off_on) { this->supports_turn_off_on_ = supports_turn_off_on; }
bool get_supports_turn_off_on() const { return this->supports_turn_off_on_; }
std::vector<MediaPlayerSupportedFormat> &get_supported_formats() { return this->supported_formats_; } std::vector<MediaPlayerSupportedFormat> &get_supported_formats() { return this->supported_formats_; }
uint32_t get_feature_flags() const { uint32_t get_feature_flags() const {
@ -90,12 +96,16 @@ class MediaPlayerTraits {
if (this->get_supports_pause()) { if (this->get_supports_pause()) {
flags |= MediaPlayerEntityFeature::PAUSE | MediaPlayerEntityFeature::PLAY; flags |= MediaPlayerEntityFeature::PAUSE | MediaPlayerEntityFeature::PLAY;
} }
if (this->get_supports_turn_off_on()) {
flags |= MediaPlayerEntityFeature::TURN_OFF | MediaPlayerEntityFeature::TURN_ON;
}
return flags; return flags;
} }
protected: protected:
bool supports_pause_{false};
std::vector<MediaPlayerSupportedFormat> supported_formats_{}; std::vector<MediaPlayerSupportedFormat> supported_formats_{};
bool supports_pause_{false};
bool supports_turn_off_on_{false};
}; };
class MediaPlayerCall { class MediaPlayerCall {