mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Add color setting trait (#22894)
This commit is contained in:
parent
37f3eccb1e
commit
51e6d5380e
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user