diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 41549c021fe..12464998211 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -43,8 +43,7 @@ TRAIT_ONOFF = PREFIX_TRAITS + 'OnOff' TRAIT_DOCK = PREFIX_TRAITS + 'Dock' TRAIT_STARTSTOP = PREFIX_TRAITS + 'StartStop' TRAIT_BRIGHTNESS = PREFIX_TRAITS + 'Brightness' -TRAIT_COLOR_SPECTRUM = PREFIX_TRAITS + 'ColorSpectrum' -TRAIT_COLOR_TEMP = PREFIX_TRAITS + 'ColorTemperature' +TRAIT_COLOR_SETTING = PREFIX_TRAITS + 'ColorSetting' TRAIT_SCENE = PREFIX_TRAITS + 'Scene' TRAIT_TEMPERATURE_SETTING = PREFIX_TRAITS + 'TemperatureSetting' TRAIT_LOCKUNLOCK = PREFIX_TRAITS + 'LockUnlock' @@ -274,69 +273,13 @@ class OnOffTrait(_Trait): @register_trait -class ColorSpectrumTrait(_Trait): - """Trait to offer color spectrum functionality. - - https://developers.google.com/actions/smarthome/traits/colorspectrum - """ - - name = TRAIT_COLOR_SPECTRUM - commands = [ - COMMAND_COLOR_ABSOLUTE - ] - - @staticmethod - def supported(domain, features, device_class): - """Test if state is supported.""" - if domain != light.DOMAIN: - return False - - return features & light.SUPPORT_COLOR - - def sync_attributes(self): - """Return color spectrum attributes for a sync request.""" - # Other colorModel is hsv - return {'colorModel': 'rgb'} - - def query_attributes(self): - """Return color spectrum query attributes.""" - response = {} - - color_hs = self.state.attributes.get(light.ATTR_HS_COLOR) - if color_hs is not None: - response['color'] = { - 'spectrumRGB': int(color_util.color_rgb_to_hex( - *color_util.color_hs_to_RGB(*color_hs)), 16), - } - - return response - - def can_execute(self, command, params): - """Test if command can be executed.""" - return (command in self.commands and - 'spectrumRGB' in params.get('color', {})) - - async def execute(self, command, data, params): - """Execute a color spectrum command.""" - # Convert integer to hex format and left pad with 0's till length 6 - hex_value = "{0:06x}".format(params['color']['spectrumRGB']) - color = color_util.color_RGB_to_hs( - *color_util.rgb_hex_to_rgb_list(hex_value)) - - await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: self.state.entity_id, - light.ATTR_HS_COLOR: color - }, blocking=True, context=data.context) - - -@register_trait -class ColorTemperatureTrait(_Trait): +class ColorSettingTrait(_Trait): """Trait to offer color temperature functionality. https://developers.google.com/actions/smarthome/traits/colortemperature """ - name = TRAIT_COLOR_TEMP + name = TRAIT_COLOR_SETTING commands = [ COMMAND_COLOR_ABSOLUTE ] @@ -347,59 +290,92 @@ class ColorTemperatureTrait(_Trait): if domain != light.DOMAIN: return False - return features & light.SUPPORT_COLOR_TEMP + return (features & light.SUPPORT_COLOR_TEMP or + features & light.SUPPORT_COLOR) def sync_attributes(self): """Return color temperature attributes for a sync request.""" attrs = self.state.attributes - # Max Kelvin is Min Mireds K = 1000000 / mireds - # Min Kevin is Max Mireds K = 1000000 / mireds - return { - 'temperatureMaxK': color_util.color_temperature_mired_to_kelvin( - attrs.get(light.ATTR_MIN_MIREDS)), - 'temperatureMinK': color_util.color_temperature_mired_to_kelvin( - attrs.get(light.ATTR_MAX_MIREDS)), - } - - def query_attributes(self): - """Return color temperature query attributes.""" + features = attrs.get(ATTR_SUPPORTED_FEATURES, 0) response = {} - temp = self.state.attributes.get(light.ATTR_COLOR_TEMP) - # Some faulty integrations might put 0 in here, raising exception. - if temp == 0: - _LOGGER.warning('Entity %s has incorrect color temperature %s', - self.state.entity_id, temp) - elif temp is not None: - response['color'] = { - 'temperature': - color_util.color_temperature_mired_to_kelvin(temp) - } + if features & light.SUPPORT_COLOR: + response['colorModel'] = 'rgb' + + if features & light.SUPPORT_COLOR_TEMP: + # Max Kelvin is Min Mireds K = 1000000 / mireds + # Min Kevin is Max Mireds K = 1000000 / mireds + response['temperatureMaxK'] = \ + color_util.color_temperature_mired_to_kelvin( + attrs.get(light.ATTR_MIN_MIREDS)) + response['temperatureMinK'] = \ + color_util.color_temperature_mired_to_kelvin( + attrs.get(light.ATTR_MAX_MIREDS)) return response - def can_execute(self, command, params): - """Test if command can be executed.""" - return (command in self.commands and - 'temperature' in params.get('color', {})) + def query_attributes(self): + """Return color temperature query attributes.""" + features = self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + color = {} + + if features & light.SUPPORT_COLOR: + color_hs = self.state.attributes.get(light.ATTR_HS_COLOR) + if color_hs is not None: + color['spectrumRGB'] = int( + color_util.color_rgb_to_hex( + *color_util.color_hs_to_RGB(*color_hs)), + 16 + ) + + if features & light.SUPPORT_COLOR_TEMP: + temp = self.state.attributes.get(light.ATTR_COLOR_TEMP) + # Some faulty integrations might put 0 in here, raising exception. + if temp == 0: + _LOGGER.warning('Entity %s has incorrect color temperature %s', + self.state.entity_id, temp) + elif temp is not None: + color['temperature'] = \ + color_util.color_temperature_mired_to_kelvin(temp) + + response = {} + + if color: + response['color'] = color + + return response async def execute(self, command, data, params): """Execute a color temperature command.""" - temp = color_util.color_temperature_kelvin_to_mired( - params['color']['temperature']) - min_temp = self.state.attributes[light.ATTR_MIN_MIREDS] - max_temp = self.state.attributes[light.ATTR_MAX_MIREDS] + if 'temperature' in params['color']: + temp = color_util.color_temperature_kelvin_to_mired( + params['color']['temperature']) + min_temp = self.state.attributes[light.ATTR_MIN_MIREDS] + max_temp = self.state.attributes[light.ATTR_MAX_MIREDS] - if temp < min_temp or temp > max_temp: - raise SmartHomeError( - ERR_VALUE_OUT_OF_RANGE, - "Temperature should be between {} and {}".format(min_temp, - max_temp)) + if temp < min_temp or temp > max_temp: + raise SmartHomeError( + ERR_VALUE_OUT_OF_RANGE, + "Temperature should be between {} and {}".format(min_temp, + max_temp)) - await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: self.state.entity_id, - light.ATTR_COLOR_TEMP: temp, - }, blocking=True, context=data.context) + await self.hass.services.async_call( + light.DOMAIN, SERVICE_TURN_ON, { + ATTR_ENTITY_ID: self.state.entity_id, + light.ATTR_COLOR_TEMP: temp, + }, blocking=True, context=data.context) + + elif 'spectrumRGB' in params['color']: + # Convert integer to hex format and left pad with 0's till length 6 + hex_value = "{0:06x}".format(params['color']['spectrumRGB']) + color = color_util.color_RGB_to_hs( + *color_util.rgb_hex_to_rgb_list(hex_value)) + + await self.hass.services.async_call( + light.DOMAIN, SERVICE_TURN_ON, { + ATTR_ENTITY_ID: self.state.entity_id, + light.ATTR_HS_COLOR: color + }, blocking=True, context=data.context) @register_trait diff --git a/tests/components/google_assistant/__init__.py b/tests/components/google_assistant/__init__.py index 331c6d2d9f5..898bc04fbd9 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -10,8 +10,7 @@ DEMO_DEVICES = [{ }, 'traits': [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSpectrum', - 'action.devices.traits.ColorTemperature' + 'action.devices.traits.ColorSetting', ], 'type': 'action.devices.types.LIGHT', @@ -50,8 +49,7 @@ DEMO_DEVICES = [{ }, 'traits': [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSpectrum', - 'action.devices.traits.ColorTemperature' + 'action.devices.traits.ColorSetting', ], 'type': 'action.devices.types.LIGHT', @@ -65,8 +63,7 @@ DEMO_DEVICES = [{ }, 'traits': [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSpectrum', - 'action.devices.traits.ColorTemperature' + 'action.devices.traits.ColorSetting', ], 'type': 'action.devices.types.LIGHT', diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index bc59cc8ff2d..d33c1f1bc5b 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -93,8 +93,7 @@ async def test_sync_message(hass): 'traits': [ trait.TRAIT_BRIGHTNESS, trait.TRAIT_ONOFF, - trait.TRAIT_COLOR_SPECTRUM, - trait.TRAIT_COLOR_TEMP, + trait.TRAIT_COLOR_SETTING, ], 'type': sh.TYPE_LIGHT, 'willReportState': False, @@ -172,8 +171,7 @@ async def test_sync_in_area(hass, registries): 'traits': [ trait.TRAIT_BRIGHTNESS, trait.TRAIT_ONOFF, - trait.TRAIT_COLOR_SPECTRUM, - trait.TRAIT_COLOR_TEMP, + trait.TRAIT_COLOR_SETTING, ], 'type': sh.TYPE_LIGHT, 'willReportState': False, diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index d85fc692cb9..4a84d789068 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -452,14 +452,15 @@ async def test_startstop_vacuum(hass): } -async def test_color_spectrum_light(hass): +async def test_color_setting_color_light(hass): """Test ColorSpectrum trait support for light domain.""" - assert not trait.ColorSpectrumTrait.supported(light.DOMAIN, 0, None) - assert trait.ColorSpectrumTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR, None) + assert not trait.ColorSettingTrait.supported(light.DOMAIN, 0, None) + assert trait.ColorSettingTrait.supported(light.DOMAIN, + light.SUPPORT_COLOR, None) - trt = trait.ColorSpectrumTrait(hass, State('light.bla', STATE_ON, { + trt = trait.ColorSettingTrait(hass, State('light.bla', STATE_ON, { light.ATTR_HS_COLOR: (0, 94), + ATTR_SUPPORTED_FEATURES: light.SUPPORT_COLOR, }), BASIC_CONFIG) assert trt.sync_attributes() == { @@ -472,11 +473,6 @@ async def test_color_spectrum_light(hass): } } - assert not trt.can_execute(trait.COMMAND_COLOR_ABSOLUTE, { - 'color': { - 'temperature': 400 - } - }) assert trt.can_execute(trait.COMMAND_COLOR_ABSOLUTE, { 'color': { 'spectrumRGB': 16715792 @@ -496,17 +492,17 @@ async def test_color_spectrum_light(hass): } -async def test_color_temperature_light(hass): +async def test_color_setting_temperature_light(hass): """Test ColorTemperature trait support for light domain.""" - assert not trait.ColorTemperatureTrait.supported(light.DOMAIN, 0, None) - assert trait.ColorTemperatureTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR_TEMP, - None) + assert not trait.ColorSettingTrait.supported(light.DOMAIN, 0, None) + assert trait.ColorSettingTrait.supported(light.DOMAIN, + light.SUPPORT_COLOR_TEMP, None) - trt = trait.ColorTemperatureTrait(hass, State('light.bla', STATE_ON, { + trt = trait.ColorSettingTrait(hass, State('light.bla', STATE_ON, { light.ATTR_MIN_MIREDS: 200, light.ATTR_COLOR_TEMP: 300, light.ATTR_MAX_MIREDS: 500, + ATTR_SUPPORTED_FEATURES: light.SUPPORT_COLOR_TEMP, }), BASIC_CONFIG) assert trt.sync_attributes() == { @@ -525,12 +521,6 @@ async def test_color_temperature_light(hass): 'temperature': 400 } }) - assert not trt.can_execute(trait.COMMAND_COLOR_ABSOLUTE, { - 'color': { - 'spectrumRGB': 16715792 - } - }) - calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON) with pytest.raises(helpers.SmartHomeError) as err: @@ -553,14 +543,13 @@ async def test_color_temperature_light(hass): } -async def test_color_temperature_light_bad_temp(hass): +async def test_color_light_temperature_light_bad_temp(hass): """Test ColorTemperature trait support for light domain.""" - assert not trait.ColorTemperatureTrait.supported(light.DOMAIN, 0, None) - assert trait.ColorTemperatureTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR_TEMP, - None) + assert not trait.ColorSettingTrait.supported(light.DOMAIN, 0, None) + assert trait.ColorSettingTrait.supported(light.DOMAIN, + light.SUPPORT_COLOR_TEMP, None) - trt = trait.ColorTemperatureTrait(hass, State('light.bla', STATE_ON, { + trt = trait.ColorSettingTrait(hass, State('light.bla', STATE_ON, { light.ATTR_MIN_MIREDS: 200, light.ATTR_COLOR_TEMP: 0, light.ATTR_MAX_MIREDS: 500,