From 72c7d4dff4958df930bffa4ed181097f4f389032 Mon Sep 17 00:00:00 2001 From: Philipp Grafe Date: Tue, 10 Feb 2026 18:58:55 +0100 Subject: [PATCH] media_player: handle STOP command correctly (fixes #128) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Home Assistant sends MEDIA_PLAYER_COMMAND_STOP when the queue is cleared. The MediaPlayer entity did not handle this command, causing playback to continue without media metadata and leaving the UI in an inconsistent state. This change adds explicit handling for the STOP command: - stop playback via the music player - reset the media player state to IDLE This aligns the behavior with the original player and Home Assistant’s expected media player semantics. --- linux_voice_assistant/entity.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/linux_voice_assistant/entity.py b/linux_voice_assistant/entity.py index a91af07..339581c 100644 --- a/linux_voice_assistant/entity.py +++ b/linux_voice_assistant/entity.py @@ -1,6 +1,7 @@ from abc import abstractmethod from collections.abc import Iterable from typing import Callable, List, Optional, Union +import logging # pylint: disable=no-name-in-module from aioesphomeapi.api_pb2 import ( # type: ignore[attr-defined] @@ -68,6 +69,7 @@ class MediaPlayerEntity(ESPHomeEntity): self.previous_volume = 1.0 self.music_player = music_player self.announce_player = announce_player + self._log = logging.getLogger(f"{self.__class__.__name__}[{self.key}]") def play( self, @@ -111,19 +113,37 @@ class MediaPlayerEntity(ESPHomeEntity): yield self._update_state(MediaPlayerState.PLAYING) def handle_message(self, msg: message.Message) -> Iterable[message.Message]: + self._log.debug("handle_message called with msg: %s", msg) + if isinstance(msg, MediaPlayerCommandRequest) and (msg.key == self.key): + self._log.debug("MediaPlayerCommandRequest matched for this key") + if msg.has_media_url: + self._log.debug("Message has media URL: %s", msg.media_url) announcement = msg.has_announcement and msg.announcement yield from self.play(msg.media_url, announcement=announcement) + elif msg.has_command: + self._log.debug("Message has command: %s", msg.command) command = MediaPlayerCommand(msg.command) + if msg.command == MediaPlayerCommand.PAUSE: + self._log.debug("Executing PAUSE") self.music_player.pause() yield self._update_state(MediaPlayerState.PAUSED) + elif msg.command == MediaPlayerCommand.PLAY: + self._log.debug("Executing PLAY / RESUME") self.music_player.resume() yield self._update_state(MediaPlayerState.PLAYING) + + elif command == MediaPlayerCommand.STOP: + self._log.debug("Executing STOP") + self.music_player.stop() + yield self._update_state(MediaPlayerState.IDLE) + elif command == MediaPlayerCommand.MUTE: + self._log.debug("Executing MUTE") if not self.muted: self.previous_volume = self.volume self.volume = 0 @@ -131,20 +151,26 @@ class MediaPlayerEntity(ESPHomeEntity): self.announce_player.set_volume(0) self.muted = True yield self._update_state(self.state) + elif command == MediaPlayerCommand.UNMUTE: + self._log.debug("Executing UNMUTE") if self.muted: self.volume = self.previous_volume self.music_player.set_volume(int(self.volume * 100)) self.announce_player.set_volume(int(self.volume * 100)) self.muted = False - yield self._update_state(self.state) + yield self._update_state(self.state) + elif msg.has_volume: + self._log.debug("Message has volume: %.2f", msg.volume) volume = int(msg.volume * 100) self.music_player.set_volume(volume) self.announce_player.set_volume(volume) self.volume = msg.volume yield self._update_state(self.state) + elif isinstance(msg, ListEntitiesRequest): + self._log.debug("ListEntitiesRequest received") yield ListEntitiesMediaPlayerResponse( object_id=self.object_id, key=self.key, @@ -153,7 +179,10 @@ class MediaPlayerEntity(ESPHomeEntity): feature_flags=SUPPORTED_MEDIA_PLAYER_FEATURES, ) elif isinstance(msg, SubscribeHomeAssistantStatesRequest): + self._log.debug("SubscribeHomeAssistantStatesRequest received") yield self._get_state_message() + else: + self._log.warning("Unknown message type received: %s", type(msg)) def _update_state(self, new_state: MediaPlayerState) -> MediaPlayerStateResponse: self.state = new_state