diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 7d6e79a8237..26c2e2ee002 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -1427,18 +1427,33 @@ class VolumeTrait(_Trait): async def _execute_volume_relative(self, data, params): # This could also support up/down commands using relativeSteps relative = params["volumeRelativeLevel"] - current = self.state.attributes.get(media_player.ATTR_MEDIA_VOLUME_LEVEL) - await self.hass.services.async_call( - media_player.DOMAIN, - media_player.SERVICE_VOLUME_SET, - { - ATTR_ENTITY_ID: self.state.entity_id, - media_player.ATTR_MEDIA_VOLUME_LEVEL: current + relative / 100, - }, - blocking=True, - context=data.context, - ) + # if we have access to current volume level, do a single 'set' call + if media_player.ATTR_MEDIA_VOLUME_LEVEL in self.state.attributes: + current = self.state.attributes.get(media_player.ATTR_MEDIA_VOLUME_LEVEL) + + await self.hass.services.async_call( + media_player.DOMAIN, + media_player.SERVICE_VOLUME_SET, + { + ATTR_ENTITY_ID: self.state.entity_id, + media_player.ATTR_MEDIA_VOLUME_LEVEL: current + relative / 100, + }, + blocking=True, + context=data.context, + ) + # otherwise do multiple 'up' or 'down' calls + else: + for _ in range(abs(relative)): + await self.hass.services.async_call( + media_player.DOMAIN, + media_player.SERVICE_VOLUME_UP + if relative > 0 + else media_player.SERVICE_VOLUME_DOWN, + {ATTR_ENTITY_ID: self.state.entity_id}, + blocking=True, + context=data.context, + ) async def execute(self, command, data, params, challenge): """Execute a brightness command.""" diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index a5c527dacfe..d58281b0e11 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -1599,6 +1599,35 @@ async def test_volume_media_player_relative(hass): } +async def test_volume_media_player_relative_no_vol_lvl(hass): + """Test volume trait support for media player domain.""" + trt = trait.VolumeTrait( + hass, State("media_player.bla", media_player.STATE_PLAYING, {}), BASIC_CONFIG + ) + + assert trt.sync_attributes() == {} + + assert trt.query_attributes() == {} + + up_calls = async_mock_service( + hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_UP + ) + + await trt.execute( + trait.COMMAND_VOLUME_RELATIVE, BASIC_DATA, {"volumeRelativeLevel": 2}, {} + ) + assert len(up_calls) == 2 + + down_calls = async_mock_service( + hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_DOWN + ) + + await trt.execute( + trait.COMMAND_VOLUME_RELATIVE, BASIC_DATA, {"volumeRelativeLevel": -2}, {} + ) + assert len(down_calls) == 2 + + async def test_temperature_setting_sensor(hass): """Test TemperatureSetting trait support for temperature sensor.""" assert (