diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 0d5ba0a656c0..d29b17d13de5 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -247,29 +247,30 @@ class ADBDevice(AndroidTVEntity, MediaPlayerEntity): await self.aftv.stop_app(self._app_name_to_id.get(source_, source_)) @adb_decorator() - async def adb_command(self, command: str) -> None: + async def adb_command(self, command: str) -> dict[str, str] | None: """Send an ADB command to an Android / Fire TV device.""" if key := KEYS.get(command): await self.aftv.adb_shell(f"input keyevent {key}") - return + return None if command == "GET_PROPERTIES": - self._attr_extra_state_attributes[ATTR_ADB_RESPONSE] = str( - await self.aftv.get_properties_dict() - ) + adb_response = str(await self.aftv.get_properties_dict()) + self._attr_extra_state_attributes[ATTR_ADB_RESPONSE] = adb_response self.async_write_ha_state() - return + return {ATTR_ADB_RESPONSE: adb_response} try: response = await self.aftv.adb_shell(command) except UnicodeDecodeError: - return + return None if isinstance(response, str) and response.strip(): - self._attr_extra_state_attributes[ATTR_ADB_RESPONSE] = response.strip() + adb_response = response.strip() + self._attr_extra_state_attributes[ATTR_ADB_RESPONSE] = adb_response self.async_write_ha_state() + return {ATTR_ADB_RESPONSE: adb_response} - return + return None @adb_decorator() async def learn_sendevent(self) -> None: diff --git a/homeassistant/components/androidtv/services.py b/homeassistant/components/androidtv/services.py index 2d565628f71b..1788235f8491 100644 --- a/homeassistant/components/androidtv/services.py +++ b/homeassistant/components/androidtv/services.py @@ -4,7 +4,7 @@ import voluptuous as vol from homeassistant.components.media_player import DOMAIN as MEDIA_PLAYER_DOMAIN from homeassistant.const import ATTR_COMMAND -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant, SupportsResponse, callback from homeassistant.helpers import config_validation as cv, service from .const import DOMAIN @@ -26,6 +26,7 @@ def async_setup_services(hass: HomeAssistant) -> None: entity_domain=MEDIA_PLAYER_DOMAIN, schema={vol.Required(ATTR_COMMAND): cv.string}, func="adb_command", + supports_response=SupportsResponse.OPTIONAL, ) service.async_register_platform_entity_service( hass, diff --git a/tests/components/androidtv/test_media_player.py b/tests/components/androidtv/test_media_player.py index a460e236deee..e03d209bf3b9 100644 --- a/tests/components/androidtv/test_media_player.py +++ b/tests/components/androidtv/test_media_player.py @@ -509,17 +509,51 @@ async def test_adb_command(hass: HomeAssistant) -> None: with patch( "androidtv.basetv.basetv_async.BaseTVAsync.adb_shell", return_value=response ) as patch_shell: - await hass.services.async_call( + service_response = await hass.services.async_call( DOMAIN, "adb_command", {ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: command}, blocking=True, + return_response=True, ) patch_shell.assert_called_with(command) state = hass.states.get(entity_id) assert state is not None assert state.attributes["adb_response"] == response + assert service_response == {entity_id: {"adb_response": response}} + + +async def test_adb_command_empty_response(hass: HomeAssistant) -> None: + """Test sending a command via the `androidtv.adb_command` service that returns an empty response.""" + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROID_DEFAULT) + config_entry.add_to_hass(hass) + command = "test command" + response = " " + + with ( + patchers.patch_connect(True)[patch_key], + patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key], + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + with patch( + "androidtv.basetv.basetv_async.BaseTVAsync.adb_shell", return_value=response + ) as patch_shell: + service_response = await hass.services.async_call( + DOMAIN, + "adb_command", + {ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: command}, + blocking=True, + return_response=True, + ) + + patch_shell.assert_called_with(command) + state = hass.states.get(entity_id) + assert state is not None + assert state.attributes["adb_response"] is None + assert service_response == {entity_id: None} async def test_adb_command_unicode_decode_error(hass: HomeAssistant) -> None: @@ -540,16 +574,18 @@ async def test_adb_command_unicode_decode_error(hass: HomeAssistant) -> None: "androidtv.basetv.basetv_async.BaseTVAsync.adb_shell", side_effect=UnicodeDecodeError("utf-8", response, 0, len(response), "TEST"), ): - await hass.services.async_call( + service_response = await hass.services.async_call( DOMAIN, "adb_command", {ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: command}, blocking=True, + return_response=True, ) state = hass.states.get(entity_id) assert state is not None assert state.attributes["adb_response"] is None + assert service_response == {entity_id: None} async def test_adb_command_key(hass: HomeAssistant) -> None: @@ -569,17 +605,19 @@ async def test_adb_command_key(hass: HomeAssistant) -> None: with patch( "androidtv.basetv.basetv_async.BaseTVAsync.adb_shell", return_value=response ) as patch_shell: - await hass.services.async_call( + service_response = await hass.services.async_call( DOMAIN, "adb_command", {ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: command}, blocking=True, + return_response=True, ) patch_shell.assert_called_with(f"input keyevent {KEYS[command]}") state = hass.states.get(entity_id) assert state is not None assert state.attributes["adb_response"] is None + assert service_response == {entity_id: None} async def test_adb_command_get_properties(hass: HomeAssistant) -> None: @@ -600,17 +638,19 @@ async def test_adb_command_get_properties(hass: HomeAssistant) -> None: "androidtv.androidtv.androidtv_async.AndroidTVAsync.get_properties_dict", return_value=response, ) as patch_get_props: - await hass.services.async_call( + service_response = await hass.services.async_call( DOMAIN, "adb_command", {ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: command}, blocking=True, + return_response=True, ) patch_get_props.assert_called() state = hass.states.get(entity_id) assert state is not None assert state.attributes["adb_response"] == str(response) + assert service_response == {entity_id: {"adb_response": str(response)}} async def test_learn_sendevent(hass: HomeAssistant) -> None: