Volume trait for google assistant (#23237)

* Add action.devices.traits.Volume

* Drop media player from brightness trait

* Factor out commands into separate functions

* Drop support for explicit mute
This commit is contained in:
Joakim Plate
2019-04-24 18:08:41 +02:00
committed by Paulus Schoutsen
parent 2863ac1068
commit e11e6e1b04
4 changed files with 145 additions and 51 deletions

View File

@@ -60,6 +60,7 @@ TRAIT_LOCKUNLOCK = PREFIX_TRAITS + 'LockUnlock'
TRAIT_FANSPEED = PREFIX_TRAITS + 'FanSpeed'
TRAIT_MODES = PREFIX_TRAITS + 'Modes'
TRAIT_OPENCLOSE = PREFIX_TRAITS + 'OpenClose'
TRAIT_VOLUME = PREFIX_TRAITS + 'Volume'
PREFIX_COMMANDS = 'action.devices.commands.'
COMMAND_ONOFF = PREFIX_COMMANDS + 'OnOff'
@@ -79,6 +80,8 @@ COMMAND_LOCKUNLOCK = PREFIX_COMMANDS + 'LockUnlock'
COMMAND_FANSPEED = PREFIX_COMMANDS + 'SetFanSpeed'
COMMAND_MODES = PREFIX_COMMANDS + 'SetModes'
COMMAND_OPENCLOSE = PREFIX_COMMANDS + 'OpenClose'
COMMAND_SET_VOLUME = PREFIX_COMMANDS + 'setVolume'
COMMAND_VOLUME_RELATIVE = PREFIX_COMMANDS + 'volumeRelative'
TRAITS = []
@@ -141,8 +144,6 @@ class BrightnessTrait(_Trait):
"""Test if state is supported."""
if domain == light.DOMAIN:
return features & light.SUPPORT_BRIGHTNESS
if domain == media_player.DOMAIN:
return features & media_player.SUPPORT_VOLUME_SET
return False
@@ -160,13 +161,6 @@ class BrightnessTrait(_Trait):
if brightness is not None:
response['brightness'] = int(100 * (brightness / 255))
elif domain == media_player.DOMAIN:
level = self.state.attributes.get(
media_player.ATTR_MEDIA_VOLUME_LEVEL)
if level is not None:
# Convert 0.0-1.0 to 0-255
response['brightness'] = int(level * 100)
return response
async def execute(self, command, data, params, challenge):
@@ -179,13 +173,6 @@ class BrightnessTrait(_Trait):
ATTR_ENTITY_ID: self.state.entity_id,
light.ATTR_BRIGHTNESS_PCT: params['brightness']
}, blocking=True, context=data.context)
elif domain == media_player.DOMAIN:
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:
params['brightness'] / 100
}, blocking=True, context=data.context)
@register_trait
@@ -1132,6 +1119,81 @@ class OpenCloseTrait(_Trait):
'Setting a position is not supported')
@register_trait
class VolumeTrait(_Trait):
"""Trait to control brightness of a device.
https://developers.google.com/actions/smarthome/traits/volume
"""
name = TRAIT_VOLUME
commands = [
COMMAND_SET_VOLUME,
COMMAND_VOLUME_RELATIVE,
]
@staticmethod
def supported(domain, features, device_class):
"""Test if state is supported."""
if domain == media_player.DOMAIN:
return features & media_player.SUPPORT_VOLUME_SET
return False
def sync_attributes(self):
"""Return brightness attributes for a sync request."""
return {}
def query_attributes(self):
"""Return brightness query attributes."""
response = {}
level = self.state.attributes.get(
media_player.ATTR_MEDIA_VOLUME_LEVEL)
muted = self.state.attributes.get(
media_player.ATTR_MEDIA_VOLUME_MUTED)
if level is not None:
# Convert 0.0-1.0 to 0-100
response['currentVolume'] = int(level * 100)
response['isMuted'] = bool(muted)
return response
async def _execute_set_volume(self, data, params):
level = params['volumeLevel']
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:
level / 100
}, blocking=True, context=data.context)
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)
async def execute(self, command, data, params, challenge):
"""Execute a brightness command."""
if command == COMMAND_SET_VOLUME:
await self._execute_set_volume(data, params)
elif command == COMMAND_VOLUME_RELATIVE:
await self._execute_volume_relative(data, params)
else:
raise SmartHomeError(
ERR_NOT_SUPPORTED, 'Command not supported')
def _verify_pin_challenge(data, challenge):
"""Verify a pin challenge."""
if not data.config.secure_devices_pin: