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_PAUSED = 3;
MEDIA_PLAYER_STATE_ANNOUNCING = 4;
MEDIA_PLAYER_STATE_OFF = 5;
MEDIA_PLAYER_STATE_ON = 6;
}
enum MediaPlayerCommand {
MEDIA_PLAYER_COMMAND_PLAY = 0;
@ -1312,6 +1314,8 @@ enum MediaPlayerCommand {
MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9;
MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10;
MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11;
MEDIA_PLAYER_COMMAND_TURN_ON = 12;
MEDIA_PLAYER_COMMAND_TURN_OFF = 13;
}
enum MediaPlayerFormatPurpose {
MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0;

View File

@ -151,6 +151,8 @@ enum MediaPlayerState : uint32_t {
MEDIA_PLAYER_STATE_PLAYING = 2,
MEDIA_PLAYER_STATE_PAUSED = 3,
MEDIA_PLAYER_STATE_ANNOUNCING = 4,
MEDIA_PLAYER_STATE_OFF = 5,
MEDIA_PLAYER_STATE_ON = 6,
};
enum MediaPlayerCommand : uint32_t {
MEDIA_PLAYER_COMMAND_PLAY = 0,
@ -165,6 +167,8 @@ enum MediaPlayerCommand : uint32_t {
MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9,
MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10,
MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11,
MEDIA_PLAYER_COMMAND_TURN_ON = 12,
MEDIA_PLAYER_COMMAND_TURN_OFF = 13,
};
enum MediaPlayerFormatPurpose : uint32_t {
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";
case enums::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:
return "UNKNOWN";
}
@ -415,6 +419,10 @@ template<> const char *proto_enum_to_string<enums::MediaPlayerCommand>(enums::Me
return "MEDIA_PLAYER_COMMAND_REPEAT_OFF";
case enums::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:
return "UNKNOWN";
}

View File

@ -7,6 +7,8 @@ from esphome.const import (
CONF_ID,
CONF_ON_IDLE,
CONF_ON_STATE,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_TRIGGER_ID,
CONF_VOLUME,
)
@ -58,6 +60,12 @@ VolumeDownAction = media_player_ns.class_(
VolumeSetAction = media_player_ns.class_(
"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_ON_PLAY = "on_play"
@ -72,12 +80,16 @@ PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.templat
AnnoucementTrigger = media_player_ns.class_(
"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)
IsPausedCondition = media_player_ns.class_("IsPausedCondition", automation.Condition)
IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition)
IsAnnouncingCondition = media_player_ns.class_(
"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):
@ -97,6 +109,12 @@ async def setup_media_player_core_(var, config):
for conf in config.get(CONF_ON_ANNOUNCEMENT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
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):
@ -140,6 +158,16 @@ _MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
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(
"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):
var = cg.new_Pvariable(action_id, template_arg)
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(
"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):
var = cg.new_Pvariable(action_id, template_arg)
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...>;
template<typename... 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> {
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 PauseTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED>;
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> {
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; }
};
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 esphome

View File

@ -9,6 +9,10 @@ static const char *const TAG = "media_player";
const char *media_player_state_to_string(MediaPlayerState state) {
switch (state) {
case MEDIA_PLAYER_STATE_ON:
return "ON";
case MEDIA_PLAYER_STATE_OFF:
return "OFF";
case MEDIA_PLAYER_STATE_IDLE:
return "IDLE";
case MEDIA_PLAYER_STATE_PLAYING:
@ -18,6 +22,7 @@ const char *media_player_state_to_string(MediaPlayerState state) {
case MEDIA_PLAYER_STATE_ANNOUNCING:
return "ANNOUNCING";
case MEDIA_PLAYER_STATE_NONE:
return "NONE";
default:
return "UNKNOWN";
}
@ -49,6 +54,10 @@ const char *media_player_command_to_string(MediaPlayerCommand command) {
return "REPEAT_OFF";
case MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST:
return "CLEAR_PLAYLIST";
case MEDIA_PLAYER_COMMAND_TURN_ON:
return "TURN_ON";
case MEDIA_PLAYER_COMMAND_TURN_OFF:
return "TURN_OFF";
default:
return "UNKNOWN";
}
@ -110,6 +119,10 @@ MediaPlayerCall &MediaPlayerCall::set_command(const std::string &command) {
this->set_command(MEDIA_PLAYER_COMMAND_UNMUTE);
} else if (str_equals_case_insensitive(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 {
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_PAUSED = 3,
MEDIA_PLAYER_STATE_ANNOUNCING = 4,
MEDIA_PLAYER_STATE_OFF = 5,
MEDIA_PLAYER_STATE_ON = 6,
};
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_OFF = 10,
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);
@ -77,9 +81,11 @@ class MediaPlayerTraits {
MediaPlayerTraits() = default;
void set_supports_pause(bool supports_pause) { this->supports_pause_ = 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_; }
uint32_t get_feature_flags() const {
@ -90,12 +96,16 @@ class MediaPlayerTraits {
if (this->get_supports_pause()) {
flags |= MediaPlayerEntityFeature::PAUSE | MediaPlayerEntityFeature::PLAY;
}
if (this->get_supports_turn_off_on()) {
flags |= MediaPlayerEntityFeature::TURN_OFF | MediaPlayerEntityFeature::TURN_ON;
}
return flags;
}
protected:
bool supports_pause_{false};
std::vector<MediaPlayerSupportedFormat> supported_formats_{};
bool supports_pause_{false};
bool supports_turn_off_on_{false};
};
class MediaPlayerCall {