Intent: Set light color (#12633)
* Make color_name_to_rgb raise * Add Light Set Color intent * Move some methods around * Cleanup * Prevent 1 more func call * Make a generic Set intent for light * Lint * lint
This commit is contained in:
parent
14d052d242
commit
efd155dd3c
6 changed files with 231 additions and 47 deletions
|
@ -21,6 +21,7 @@ import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.helpers import intent
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
|
|
||||||
|
@ -135,6 +136,8 @@ PROFILE_SCHEMA = vol.Schema(
|
||||||
vol.ExactSequence((str, cv.small_float, cv.small_float, cv.byte))
|
vol.ExactSequence((str, cv.small_float, cv.small_float, cv.byte))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
INTENT_SET = 'HassLightSet'
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -228,7 +231,12 @@ def preprocess_turn_on_alternatives(params):
|
||||||
|
|
||||||
color_name = params.pop(ATTR_COLOR_NAME, None)
|
color_name = params.pop(ATTR_COLOR_NAME, None)
|
||||||
if color_name is not None:
|
if color_name is not None:
|
||||||
|
try:
|
||||||
params[ATTR_RGB_COLOR] = color_util.color_name_to_rgb(color_name)
|
params[ATTR_RGB_COLOR] = color_util.color_name_to_rgb(color_name)
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.warning('Got unknown color %s, falling back to white',
|
||||||
|
color_name)
|
||||||
|
params[ATTR_RGB_COLOR] = (255, 255, 255)
|
||||||
|
|
||||||
kelvin = params.pop(ATTR_KELVIN, None)
|
kelvin = params.pop(ATTR_KELVIN, None)
|
||||||
if kelvin is not None:
|
if kelvin is not None:
|
||||||
|
@ -240,6 +248,67 @@ def preprocess_turn_on_alternatives(params):
|
||||||
params[ATTR_BRIGHTNESS] = int(255 * brightness_pct/100)
|
params[ATTR_BRIGHTNESS] = int(255 * brightness_pct/100)
|
||||||
|
|
||||||
|
|
||||||
|
class SetIntentHandler(intent.IntentHandler):
|
||||||
|
"""Handle set color intents."""
|
||||||
|
|
||||||
|
intent_type = INTENT_SET
|
||||||
|
slot_schema = {
|
||||||
|
vol.Required('name'): cv.string,
|
||||||
|
vol.Optional('color'): color_util.color_name_to_rgb,
|
||||||
|
vol.Optional('brightness'): vol.All(vol.Coerce(int), vol.Range(0, 100))
|
||||||
|
}
|
||||||
|
|
||||||
|
async def async_handle(self, intent_obj):
|
||||||
|
"""Handle the hass intent."""
|
||||||
|
hass = intent_obj.hass
|
||||||
|
slots = self.async_validate_slots(intent_obj.slots)
|
||||||
|
state = hass.helpers.intent.async_match_state(
|
||||||
|
slots['name']['value'],
|
||||||
|
[state for state in hass.states.async_all()
|
||||||
|
if state.domain == DOMAIN])
|
||||||
|
|
||||||
|
service_data = {
|
||||||
|
ATTR_ENTITY_ID: state.entity_id,
|
||||||
|
}
|
||||||
|
speech_parts = []
|
||||||
|
|
||||||
|
if 'color' in slots:
|
||||||
|
intent.async_test_feature(
|
||||||
|
state, SUPPORT_RGB_COLOR, 'changing colors')
|
||||||
|
service_data[ATTR_RGB_COLOR] = slots['color']['value']
|
||||||
|
# Use original passed in value of the color because we don't have
|
||||||
|
# human readable names for that internally.
|
||||||
|
speech_parts.append('the color {}'.format(
|
||||||
|
intent_obj.slots['color']['value']))
|
||||||
|
|
||||||
|
if 'brightness' in slots:
|
||||||
|
intent.async_test_feature(
|
||||||
|
state, SUPPORT_BRIGHTNESS, 'changing brightness')
|
||||||
|
service_data[ATTR_BRIGHTNESS_PCT] = slots['brightness']['value']
|
||||||
|
speech_parts.append('{}% brightness'.format(
|
||||||
|
slots['brightness']['value']))
|
||||||
|
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, service_data)
|
||||||
|
|
||||||
|
response = intent_obj.create_response()
|
||||||
|
|
||||||
|
if not speech_parts: # No attributes changed
|
||||||
|
speech = 'Turned on {}'.format(state.name)
|
||||||
|
else:
|
||||||
|
parts = ['Changed {} to'.format(state.name)]
|
||||||
|
for index, part in enumerate(speech_parts):
|
||||||
|
if index == 0:
|
||||||
|
parts.append(' {}'.format(part))
|
||||||
|
elif index != len(speech_parts) - 1:
|
||||||
|
parts.append(', {}'.format(part))
|
||||||
|
else:
|
||||||
|
parts.append(' and {}'.format(part))
|
||||||
|
speech = ''.join(parts)
|
||||||
|
|
||||||
|
response.async_set_speech(speech)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
"""Expose light control via state machine and services."""
|
"""Expose light control via state machine and services."""
|
||||||
component = EntityComponent(
|
component = EntityComponent(
|
||||||
|
@ -291,6 +360,8 @@ async def async_setup(hass, config):
|
||||||
DOMAIN, SERVICE_TOGGLE, async_handle_light_service,
|
DOMAIN, SERVICE_TOGGLE, async_handle_light_service,
|
||||||
schema=LIGHT_TOGGLE_SCHEMA)
|
schema=LIGHT_TOGGLE_SCHEMA)
|
||||||
|
|
||||||
|
hass.helpers.intent.async_register(SetIntentHandler())
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import re
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.const import ATTR_SUPPORTED_FEATURES
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
@ -33,6 +34,8 @@ def async_register(hass, handler):
|
||||||
if intents is None:
|
if intents is None:
|
||||||
intents = hass.data[DATA_KEY] = {}
|
intents = hass.data[DATA_KEY] = {}
|
||||||
|
|
||||||
|
assert handler.intent_type is not None, 'intent_type cannot be None'
|
||||||
|
|
||||||
if handler.intent_type in intents:
|
if handler.intent_type in intents:
|
||||||
_LOGGER.warning('Intent %s is being overwritten by %s.',
|
_LOGGER.warning('Intent %s is being overwritten by %s.',
|
||||||
handler.intent_type, handler)
|
handler.intent_type, handler)
|
||||||
|
@ -56,35 +59,59 @@ async def async_handle(hass, platform, intent_type, slots=None,
|
||||||
result = await handler.async_handle(intent)
|
result = await handler.async_handle(intent)
|
||||||
return result
|
return result
|
||||||
except vol.Invalid as err:
|
except vol.Invalid as err:
|
||||||
|
_LOGGER.warning('Received invalid slot info for %s: %s',
|
||||||
|
intent_type, err)
|
||||||
raise InvalidSlotInfo(
|
raise InvalidSlotInfo(
|
||||||
'Received invalid slot info for {}'.format(intent_type)) from err
|
'Received invalid slot info for {}'.format(intent_type)) from err
|
||||||
|
except IntentHandleError:
|
||||||
|
raise
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise IntentHandleError(
|
raise IntentUnexpectedError(
|
||||||
'Error handling {}'.format(intent_type)) from err
|
'Error handling {}'.format(intent_type)) from err
|
||||||
|
|
||||||
|
|
||||||
class IntentError(HomeAssistantError):
|
class IntentError(HomeAssistantError):
|
||||||
"""Base class for intent related errors."""
|
"""Base class for intent related errors."""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class UnknownIntent(IntentError):
|
class UnknownIntent(IntentError):
|
||||||
"""When the intent is not registered."""
|
"""When the intent is not registered."""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidSlotInfo(IntentError):
|
class InvalidSlotInfo(IntentError):
|
||||||
"""When the slot data is invalid."""
|
"""When the slot data is invalid."""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class IntentHandleError(IntentError):
|
class IntentHandleError(IntentError):
|
||||||
"""Error while handling intent."""
|
"""Error while handling intent."""
|
||||||
|
|
||||||
pass
|
|
||||||
|
class IntentUnexpectedError(IntentError):
|
||||||
|
"""Unexpected error while handling intent."""
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@bind_hass
|
||||||
|
def async_match_state(hass, name, states=None):
|
||||||
|
"""Find a state that matches the name."""
|
||||||
|
if states is None:
|
||||||
|
states = hass.states.async_all()
|
||||||
|
|
||||||
|
entity = _fuzzymatch(name, states, lambda state: state.name)
|
||||||
|
|
||||||
|
if entity is None:
|
||||||
|
raise IntentHandleError('Unable to find entity {}'.format(name))
|
||||||
|
|
||||||
|
return entity
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_test_feature(state, feature, feature_name):
|
||||||
|
"""Test is state supports a feature."""
|
||||||
|
if state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) & feature == 0:
|
||||||
|
raise IntentHandleError(
|
||||||
|
'Entity {} does not support {}'.format(
|
||||||
|
state.name, feature_name))
|
||||||
|
|
||||||
|
|
||||||
class IntentHandler:
|
class IntentHandler:
|
||||||
|
@ -122,16 +149,17 @@ class IntentHandler:
|
||||||
return '<{} - {}>'.format(self.__class__.__name__, self.intent_type)
|
return '<{} - {}>'.format(self.__class__.__name__, self.intent_type)
|
||||||
|
|
||||||
|
|
||||||
def fuzzymatch(name, entities):
|
def _fuzzymatch(name, items, key):
|
||||||
"""Fuzzy matching function."""
|
"""Fuzzy matching function."""
|
||||||
matches = []
|
matches = []
|
||||||
pattern = '.*?'.join(name)
|
pattern = '.*?'.join(name)
|
||||||
regex = re.compile(pattern, re.IGNORECASE)
|
regex = re.compile(pattern, re.IGNORECASE)
|
||||||
for entity_id, entity_name in entities.items():
|
for item in items:
|
||||||
match = regex.search(entity_name)
|
match = regex.search(key(item))
|
||||||
if match:
|
if match:
|
||||||
matches.append((len(match.group()), match.start(), entity_id))
|
matches.append((len(match.group()), match.start(), item))
|
||||||
return [x for _, _, x in sorted(matches)]
|
|
||||||
|
return sorted(matches)[0][2] if matches else None
|
||||||
|
|
||||||
|
|
||||||
class ServiceIntentHandler(IntentHandler):
|
class ServiceIntentHandler(IntentHandler):
|
||||||
|
@ -141,7 +169,7 @@ class ServiceIntentHandler(IntentHandler):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
slot_schema = {
|
slot_schema = {
|
||||||
'name': cv.string,
|
vol.Required('name'): cv.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, intent_type, domain, service, speech):
|
def __init__(self, intent_type, domain, service, speech):
|
||||||
|
@ -155,30 +183,14 @@ class ServiceIntentHandler(IntentHandler):
|
||||||
"""Handle the hass intent."""
|
"""Handle the hass intent."""
|
||||||
hass = intent_obj.hass
|
hass = intent_obj.hass
|
||||||
slots = self.async_validate_slots(intent_obj.slots)
|
slots = self.async_validate_slots(intent_obj.slots)
|
||||||
response = intent_obj.create_response()
|
state = async_match_state(hass, slots['name']['value'])
|
||||||
|
|
||||||
name = slots['name']['value']
|
await hass.services.async_call(self.domain, self.service, {
|
||||||
entities = {state.entity_id: state.name for state
|
ATTR_ENTITY_ID: state.entity_id
|
||||||
in hass.states.async_all()}
|
|
||||||
|
|
||||||
matches = fuzzymatch(name, entities)
|
|
||||||
entity_id = matches[0] if matches else None
|
|
||||||
_LOGGER.debug("%s matched entity: %s", name, entity_id)
|
|
||||||
|
|
||||||
response = intent_obj.create_response()
|
|
||||||
if not entity_id:
|
|
||||||
response.async_set_speech(
|
|
||||||
"Could not find entity id matching {}.".format(name))
|
|
||||||
_LOGGER.error("Could not find entity id matching %s", name)
|
|
||||||
return response
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
self.domain, self.service, {
|
|
||||||
ATTR_ENTITY_ID: entity_id
|
|
||||||
})
|
})
|
||||||
|
|
||||||
response.async_set_speech(
|
response = intent_obj.create_response()
|
||||||
self.speech.format(name))
|
response.async_set_speech(self.speech.format(state.name))
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
"""Color util methods."""
|
"""Color util methods."""
|
||||||
import logging
|
|
||||||
import math
|
import math
|
||||||
import colorsys
|
import colorsys
|
||||||
|
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# Official CSS3 colors from w3.org:
|
# Official CSS3 colors from w3.org:
|
||||||
# https://www.w3.org/TR/2010/PR-css3-color-20101028/#html4
|
# https://www.w3.org/TR/2010/PR-css3-color-20101028/#html4
|
||||||
# names do not have spaces in them so that we can compare against
|
# names do not have spaces in them so that we can compare against
|
||||||
|
@ -171,8 +168,7 @@ def color_name_to_rgb(color_name):
|
||||||
# spaces in it as well for matching purposes
|
# spaces in it as well for matching purposes
|
||||||
hex_value = COLORS.get(color_name.replace(' ', '').lower())
|
hex_value = COLORS.get(color_name.replace(' ', '').lower())
|
||||||
if not hex_value:
|
if not hex_value:
|
||||||
_LOGGER.error('unknown color supplied %s default to white', color_name)
|
raise ValueError('Unknown color')
|
||||||
hex_value = COLORS['white']
|
|
||||||
|
|
||||||
return hex_value
|
return hex_value
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,12 @@ from homeassistant.setup import setup_component
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, CONF_PLATFORM,
|
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, CONF_PLATFORM,
|
||||||
SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE)
|
SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE, ATTR_SUPPORTED_FEATURES)
|
||||||
import homeassistant.components.light as light
|
import homeassistant.components.light as light
|
||||||
|
from homeassistant.helpers.intent import IntentHandleError
|
||||||
|
|
||||||
from tests.common import mock_service, get_test_home_assistant
|
from tests.common import (
|
||||||
|
async_mock_service, mock_service, get_test_home_assistant)
|
||||||
|
|
||||||
|
|
||||||
class TestLight(unittest.TestCase):
|
class TestLight(unittest.TestCase):
|
||||||
|
@ -302,3 +304,93 @@ class TestLight(unittest.TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{light.ATTR_XY_COLOR: (.4, .6), light.ATTR_BRIGHTNESS: 100},
|
{light.ATTR_XY_COLOR: (.4, .6), light.ATTR_BRIGHTNESS: 100},
|
||||||
data)
|
data)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_set_color(hass):
|
||||||
|
"""Test the set color intent."""
|
||||||
|
hass.states.async_set('light.hello_2', 'off', {
|
||||||
|
ATTR_SUPPORTED_FEATURES: light.SUPPORT_RGB_COLOR
|
||||||
|
})
|
||||||
|
hass.states.async_set('switch.hello', 'off')
|
||||||
|
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
||||||
|
hass.helpers.intent.async_register(light.SetIntentHandler())
|
||||||
|
|
||||||
|
result = await hass.helpers.intent.async_handle(
|
||||||
|
'test', light.INTENT_SET, {
|
||||||
|
'name': {
|
||||||
|
'value': 'Hello',
|
||||||
|
},
|
||||||
|
'color': {
|
||||||
|
'value': 'blue'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result.speech['plain']['speech'] == \
|
||||||
|
'Changed hello 2 to the color blue'
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
call = calls[0]
|
||||||
|
assert call.domain == light.DOMAIN
|
||||||
|
assert call.service == SERVICE_TURN_ON
|
||||||
|
assert call.data.get(ATTR_ENTITY_ID) == 'light.hello_2'
|
||||||
|
assert call.data.get(light.ATTR_RGB_COLOR) == (0, 0, 255)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_set_color_tests_feature(hass):
|
||||||
|
"""Test the set color intent."""
|
||||||
|
hass.states.async_set('light.hello', 'off')
|
||||||
|
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
||||||
|
hass.helpers.intent.async_register(light.SetIntentHandler())
|
||||||
|
|
||||||
|
try:
|
||||||
|
await hass.helpers.intent.async_handle(
|
||||||
|
'test', light.INTENT_SET, {
|
||||||
|
'name': {
|
||||||
|
'value': 'Hello',
|
||||||
|
},
|
||||||
|
'color': {
|
||||||
|
'value': 'blue'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
assert False, 'handling intent should have raised'
|
||||||
|
except IntentHandleError as err:
|
||||||
|
assert str(err) == 'Entity hello does not support changing colors'
|
||||||
|
|
||||||
|
assert len(calls) == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_set_color_and_brightness(hass):
|
||||||
|
"""Test the set color intent."""
|
||||||
|
hass.states.async_set('light.hello_2', 'off', {
|
||||||
|
ATTR_SUPPORTED_FEATURES: (
|
||||||
|
light.SUPPORT_RGB_COLOR | light.SUPPORT_BRIGHTNESS)
|
||||||
|
})
|
||||||
|
hass.states.async_set('switch.hello', 'off')
|
||||||
|
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
||||||
|
hass.helpers.intent.async_register(light.SetIntentHandler())
|
||||||
|
|
||||||
|
result = await hass.helpers.intent.async_handle(
|
||||||
|
'test', light.INTENT_SET, {
|
||||||
|
'name': {
|
||||||
|
'value': 'Hello',
|
||||||
|
},
|
||||||
|
'color': {
|
||||||
|
'value': 'blue'
|
||||||
|
},
|
||||||
|
'brightness': {
|
||||||
|
'value': '20'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result.speech['plain']['speech'] == \
|
||||||
|
'Changed hello 2 to the color blue and 20% brightness'
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
call = calls[0]
|
||||||
|
assert call.domain == light.DOMAIN
|
||||||
|
assert call.service == SERVICE_TURN_ON
|
||||||
|
assert call.data.get(ATTR_ENTITY_ID) == 'light.hello_2'
|
||||||
|
assert call.data.get(light.ATTR_RGB_COLOR) == (0, 0, 255)
|
||||||
|
assert call.data.get(light.ATTR_BRIGHTNESS_PCT) == 20
|
||||||
|
|
|
@ -283,7 +283,7 @@ def test_turn_on_multiple_intent(hass):
|
||||||
)
|
)
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert response.speech['plain']['speech'] == 'Turned on test lights'
|
assert response.speech['plain']['speech'] == 'Turned on test lights 2'
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
call = calls[0]
|
call = calls[0]
|
||||||
assert call.domain == 'light'
|
assert call.domain == 'light'
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
import unittest
|
import unittest
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
|
||||||
class TestColorUtil(unittest.TestCase):
|
class TestColorUtil(unittest.TestCase):
|
||||||
"""Test color util methods."""
|
"""Test color util methods."""
|
||||||
|
@ -150,10 +153,10 @@ class TestColorUtil(unittest.TestCase):
|
||||||
self.assertEqual((72, 61, 139),
|
self.assertEqual((72, 61, 139),
|
||||||
color_util.color_name_to_rgb('darkslate blue'))
|
color_util.color_name_to_rgb('darkslate blue'))
|
||||||
|
|
||||||
def test_color_name_to_rgb_unknown_name_default_white(self):
|
def test_color_name_to_rgb_unknown_name_raises_value_error(self):
|
||||||
"""Test color_name_to_rgb."""
|
"""Test color_name_to_rgb."""
|
||||||
self.assertEqual((255, 255, 255),
|
with pytest.raises(ValueError):
|
||||||
color_util.color_name_to_rgb('not a color'))
|
color_util.color_name_to_rgb('not a color')
|
||||||
|
|
||||||
def test_color_rgb_to_rgbw(self):
|
def test_color_rgb_to_rgbw(self):
|
||||||
"""Test color_rgb_to_rgbw."""
|
"""Test color_rgb_to_rgbw."""
|
||||||
|
@ -280,3 +283,13 @@ class ColorTemperatureToRGB(unittest.TestCase):
|
||||||
rgb = color_util.color_temperature_to_rgb(6500)
|
rgb = color_util.color_temperature_to_rgb(6500)
|
||||||
self.assertGreater(rgb[0], rgb[1])
|
self.assertGreater(rgb[0], rgb[1])
|
||||||
self.assertGreater(rgb[0], rgb[2])
|
self.assertGreater(rgb[0], rgb[2])
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_color_in_voluptuous():
|
||||||
|
"""Test using the get method in color validation."""
|
||||||
|
schema = vol.Schema(color_util.color_name_to_rgb)
|
||||||
|
|
||||||
|
with pytest.raises(vol.Invalid):
|
||||||
|
schema('not a color')
|
||||||
|
|
||||||
|
assert schema('red') == (255, 0, 0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue