diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto
index b8073abc19..774ca7ed9b 100644
--- a/esphome/components/api/api.proto
+++ b/esphome/components/api/api.proto
@@ -1147,6 +1147,9 @@ message MediaPlayerCommandRequest {
 
   bool has_media_url = 6;
   string media_url = 7;
+
+  bool has_announcement = 8;
+  bool announcement = 9;
 }
 
 // ==================== BLUETOOTH ====================
diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp
index b31212bbdb..2804dba31f 100644
--- a/esphome/components/api/api_connection.cpp
+++ b/esphome/components/api/api_connection.cpp
@@ -1002,7 +1002,11 @@ bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_pla
 
   MediaPlayerStateResponse resp{};
   resp.key = media_player->get_object_id_hash();
-  resp.state = static_cast<enums::MediaPlayerState>(media_player->state);
+
+  media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
+                                                    ? media_player::MEDIA_PLAYER_STATE_PLAYING
+                                                    : media_player->state;
+  resp.state = static_cast<enums::MediaPlayerState>(report_state);
   resp.volume = media_player->volume;
   resp.muted = media_player->is_muted();
   return this->send_media_player_state_response(resp);
@@ -1038,6 +1042,9 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
   if (msg.has_media_url) {
     call.set_media_url(msg.media_url);
   }
+  if (msg.has_announcement) {
+    call.set_announcement(msg.announcement);
+  }
   call.perform();
 }
 #endif
diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp
index 6ec1870d72..a48087e348 100644
--- a/esphome/components/api/api_pb2.cpp
+++ b/esphome/components/api/api_pb2.cpp
@@ -5253,6 +5253,14 @@ bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt val
       this->has_media_url = value.as_bool();
       return true;
     }
+    case 8: {
+      this->has_announcement = value.as_bool();
+      return true;
+    }
+    case 9: {
+      this->announcement = value.as_bool();
+      return true;
+    }
     default:
       return false;
   }
@@ -5289,6 +5297,8 @@ void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const {
   buffer.encode_float(5, this->volume);
   buffer.encode_bool(6, this->has_media_url);
   buffer.encode_string(7, this->media_url);
+  buffer.encode_bool(8, this->has_announcement);
+  buffer.encode_bool(9, this->announcement);
 }
 #ifdef HAS_PROTO_MESSAGE_DUMP
 void MediaPlayerCommandRequest::dump_to(std::string &out) const {
@@ -5323,6 +5333,14 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
   out.append("  media_url: ");
   out.append("'").append(this->media_url).append("'");
   out.append("\n");
+
+  out.append("  has_announcement: ");
+  out.append(YESNO(this->has_announcement));
+  out.append("\n");
+
+  out.append("  announcement: ");
+  out.append(YESNO(this->announcement));
+  out.append("\n");
   out.append("}");
 }
 #endif
diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h
index 14fd95df37..807b150d82 100644
--- a/esphome/components/api/api_pb2.h
+++ b/esphome/components/api/api_pb2.h
@@ -1298,6 +1298,8 @@ class MediaPlayerCommandRequest : public ProtoMessage {
   float volume{0.0f};
   bool has_media_url{false};
   std::string media_url{};
+  bool has_announcement{false};
+  bool announcement{false};
   void encode(ProtoWriteBuffer buffer) const override;
 #ifdef HAS_PROTO_MESSAGE_DUMP
   void dump_to(std::string &out) const override;
diff --git a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp
index 6e07983920..1890e27bdf 100644
--- a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp
+++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp
@@ -10,6 +10,11 @@ namespace i2s_audio {
 static const char *const TAG = "audio";
 
 void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
+  media_player::MediaPlayerState play_state = media_player::MEDIA_PLAYER_STATE_PLAYING;
+  if (call.get_announcement().has_value()) {
+    play_state = call.get_announcement().value() ? media_player::MEDIA_PLAYER_STATE_ANNOUNCING
+                                                 : media_player::MEDIA_PLAYER_STATE_PLAYING;
+  }
   if (call.get_media_url().has_value()) {
     this->current_url_ = call.get_media_url();
     if (this->i2s_state_ != I2S_STATE_STOPPED && this->audio_ != nullptr) {
@@ -17,7 +22,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
         this->audio_->stopSong();
       }
       this->audio_->connecttohost(this->current_url_.value().c_str());
-      this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
+      this->state = play_state;
     } else {
       this->start();
     }
@@ -35,7 +40,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
       case media_player::MEDIA_PLAYER_COMMAND_PLAY:
         if (!this->audio_->isRunning())
           this->audio_->pauseResume();
-        this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
+        this->state = play_state;
         break;
       case media_player::MEDIA_PLAYER_COMMAND_PAUSE:
         if (this->audio_->isRunning())
@@ -126,7 +131,9 @@ void I2SAudioMediaPlayer::loop() {
 
 void I2SAudioMediaPlayer::play_() {
   this->audio_->loop();
-  if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) {
+  if ((this->state == media_player::MEDIA_PLAYER_STATE_PLAYING ||
+       this->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING) &&
+      !this->audio_->isRunning()) {
     this->stop();
   }
 }
@@ -164,6 +171,10 @@ void I2SAudioMediaPlayer::start_() {
   if (this->current_url_.has_value()) {
     this->audio_->connecttohost(this->current_url_.value().c_str());
     this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
+    if (this->is_announcement_.has_value()) {
+      this->state = this->is_announcement_.value() ? media_player::MEDIA_PLAYER_STATE_ANNOUNCING
+                                                   : media_player::MEDIA_PLAYER_STATE_PLAYING;
+    }
     this->publish_state();
   }
 }
diff --git a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.h b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.h
index 092e6de8e8..d7d9b1f74a 100644
--- a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.h
+++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.h
@@ -78,6 +78,7 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer,
   HighFrequencyLoopRequester high_freq_;
 
   optional<std::string> current_url_{};
+  optional<bool> is_announcement_{};
 };
 
 }  // namespace i2s_audio
diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py
index 5db78150bb..320014e355 100644
--- a/esphome/components/media_player/__init__.py
+++ b/esphome/components/media_player/__init__.py
@@ -51,12 +51,16 @@ VolumeSetAction = media_player_ns.class_(
 
 CONF_ON_PLAY = "on_play"
 CONF_ON_PAUSE = "on_pause"
+CONF_ON_ANNOUNCEMENT = "on_announcement"
 CONF_MEDIA_URL = "media_url"
 
 StateTrigger = media_player_ns.class_("StateTrigger", automation.Trigger.template())
 IdleTrigger = media_player_ns.class_("IdleTrigger", automation.Trigger.template())
 PlayTrigger = media_player_ns.class_("PlayTrigger", automation.Trigger.template())
 PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.template())
+AnnoucementTrigger = media_player_ns.class_(
+    "AnnouncementTrigger", automation.Trigger.template()
+)
 IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition)
 IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition)
 
@@ -75,6 +79,9 @@ async def setup_media_player_core_(var, config):
     for conf in config.get(CONF_ON_PAUSE, []):
         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
         await automation.build_automation(trigger, [], conf)
+    for conf in config.get(CONF_ON_ANNOUNCEMENT, []):
+        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
+        await automation.build_automation(trigger, [], conf)
 
 
 async def register_media_player(var, config):
@@ -106,6 +113,11 @@ MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger),
             }
         ),
+        cv.Optional(CONF_ON_ANNOUNCEMENT): automation.validate_automation(
+            {
+                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(AnnoucementTrigger),
+            }
+        ),
     }
 )
 
diff --git a/esphome/components/media_player/automation.h b/esphome/components/media_player/automation.h
index 261e93775c..fc3ce7a764 100644
--- a/esphome/components/media_player/automation.h
+++ b/esphome/components/media_player/automation.h
@@ -52,6 +52,7 @@ class StateTrigger : public Trigger<> {
 MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(IdleTrigger, IDLE)
 MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PlayTrigger, PLAYING)
 MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PauseTrigger, PAUSED)
+MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(AnnouncementTrigger, ANNOUNCING)
 
 template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
  public:
diff --git a/esphome/components/media_player/media_player.cpp b/esphome/components/media_player/media_player.cpp
index 81cb6ca751..586345ac9f 100644
--- a/esphome/components/media_player/media_player.cpp
+++ b/esphome/components/media_player/media_player.cpp
@@ -15,6 +15,8 @@ const char *media_player_state_to_string(MediaPlayerState state) {
       return "PLAYING";
     case MEDIA_PLAYER_STATE_PAUSED:
       return "PAUSED";
+    case MEDIA_PLAYER_STATE_ANNOUNCING:
+      return "ANNOUNCING";
     case MEDIA_PLAYER_STATE_NONE:
     default:
       return "UNKNOWN";
@@ -68,6 +70,9 @@ void MediaPlayerCall::perform() {
   if (this->volume_.has_value()) {
     ESP_LOGD(TAG, "  Volume: %.2f", this->volume_.value());
   }
+  if (this->announcement_.has_value()) {
+    ESP_LOGD(TAG, " Announcement: %s", this->announcement_.value() ? "yes" : "no");
+  }
   this->parent_->control(*this);
 }
 
@@ -108,6 +113,11 @@ MediaPlayerCall &MediaPlayerCall::set_volume(float volume) {
   return *this;
 }
 
+MediaPlayerCall &MediaPlayerCall::set_announcement(bool announce) {
+  this->announcement_ = announce;
+  return *this;
+}
+
 void MediaPlayer::add_on_state_callback(std::function<void()> &&callback) {
   this->state_callback_.add(std::move(callback));
 }
diff --git a/esphome/components/media_player/media_player.h b/esphome/components/media_player/media_player.h
index 88114d5337..77746e1808 100644
--- a/esphome/components/media_player/media_player.h
+++ b/esphome/components/media_player/media_player.h
@@ -10,7 +10,8 @@ enum MediaPlayerState : uint8_t {
   MEDIA_PLAYER_STATE_NONE = 0,
   MEDIA_PLAYER_STATE_IDLE = 1,
   MEDIA_PLAYER_STATE_PLAYING = 2,
-  MEDIA_PLAYER_STATE_PAUSED = 3
+  MEDIA_PLAYER_STATE_PAUSED = 3,
+  MEDIA_PLAYER_STATE_ANNOUNCING = 4
 };
 const char *media_player_state_to_string(MediaPlayerState state);
 
@@ -51,12 +52,14 @@ class MediaPlayerCall {
   MediaPlayerCall &set_media_url(const std::string &url);
 
   MediaPlayerCall &set_volume(float volume);
+  MediaPlayerCall &set_announcement(bool announce);
 
   void perform();
 
   const optional<MediaPlayerCommand> &get_command() const { return command_; }
   const optional<std::string> &get_media_url() const { return media_url_; }
   const optional<float> &get_volume() const { return volume_; }
+  const optional<bool> &get_announcement() const { return announcement_; }
 
  protected:
   void validate_();
@@ -64,6 +67,7 @@ class MediaPlayerCall {
   optional<MediaPlayerCommand> command_;
   optional<std::string> media_url_;
   optional<float> volume_;
+  optional<bool> announcement_;
 };
 
 class MediaPlayer : public EntityBase {
diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp
index 3bd41e1fcf..109e52f8eb 100644
--- a/esphome/components/voice_assistant/voice_assistant.cpp
+++ b/esphome/components/voice_assistant/voice_assistant.cpp
@@ -318,7 +318,7 @@ void VoiceAssistant::loop() {
 #endif
 #ifdef USE_MEDIA_PLAYER
       if (this->media_player_ != nullptr) {
-        playing = (this->media_player_->state == media_player::MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING);
+        playing = (this->media_player_->state == media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING);
       }
 #endif
       if (playing) {
@@ -640,7 +640,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
       this->defer([this, url]() {
 #ifdef USE_MEDIA_PLAYER
         if (this->media_player_ != nullptr) {
-          this->media_player_->make_call().set_media_url(url).perform();
+          this->media_player_->make_call().set_media_url(url).set_announcement(true).perform();
         }
 #endif
         this->tts_end_trigger_->trigger(url);