Add color setting trait (#22894)

This commit is contained in:
Paulus Schoutsen 2019-04-09 20:17:13 -07:00 committed by GitHub
parent 37f3eccb1e
commit 51e6d5380e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 97 additions and 137 deletions

View File

@ -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,44 +290,64 @@ 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
features = attrs.get(ATTR_SUPPORTED_FEATURES, 0)
response = {}
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
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)),
}
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 query_attributes(self):
"""Return color temperature query attributes."""
response = {}
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:
response['color'] = {
'temperature':
color['temperature'] = \
color_util.color_temperature_mired_to_kelvin(temp)
}
response = {}
if color:
response['color'] = color
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', {}))
async def execute(self, command, data, params):
"""Execute a color temperature command."""
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]
@ -396,11 +359,24 @@ class ColorTemperatureTrait(_Trait):
"Temperature should be between {} and {}".format(min_temp,
max_temp))
await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, {
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
class SceneTrait(_Trait):

View File

@ -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',

View File

@ -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,

View File

@ -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,
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,