diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 375d4467c05572..24637430342ab6 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -136,7 +136,7 @@ vol.ExactSequence((str, cv.small_float, cv.small_float, cv.byte)) ) -INTENT_SET_COLOR = 'HassLightSetColor' +INTENT_SET = 'HassLightSet' _LOGGER = logging.getLogger(__name__) @@ -248,13 +248,14 @@ def preprocess_turn_on_alternatives(params): params[ATTR_BRIGHTNESS] = int(255 * brightness_pct/100) -class SetColorIntentHandler(intent.IntentHandler): +class SetIntentHandler(intent.IntentHandler): """Handle set color intents.""" - intent_type = INTENT_SET_COLOR + intent_type = INTENT_SET slot_schema = { - 'name': cv.string, - 'color': color_util.color_name_to_rgb + 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): @@ -265,19 +266,46 @@ async def async_handle(self, intent_obj): slots['name']['value'], [state for state in hass.states.async_all() if state.domain == DOMAIN]) - intent.async_test_feature(state, SUPPORT_RGB_COLOR, 'changing colors') - await hass.services.async_call( - DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: state.entity_id, - ATTR_RGB_COLOR: slots['color']['value'] - }) + 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) - # Use original passed in value of the color because we don't have human - # readable names for that internally. response = intent_obj.create_response() - response.async_set_speech('Changed the color of {} to {}'.format( - state.name, intent_obj.slots['color']['value'])) + + if len(speech_parts) == 0: + 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 @@ -332,7 +360,7 @@ async def async_handle_light_service(service): DOMAIN, SERVICE_TOGGLE, async_handle_light_service, schema=LIGHT_TOGGLE_SCHEMA) - hass.helpers.intent.async_register(SetColorIntentHandler()) + hass.helpers.intent.async_register(SetIntentHandler()) return True diff --git a/homeassistant/helpers/intent.py b/homeassistant/helpers/intent.py index 48bfb1524a41e9..26c2bca34c7807 100644 --- a/homeassistant/helpers/intent.py +++ b/homeassistant/helpers/intent.py @@ -59,6 +59,8 @@ async def async_handle(hass, platform, intent_type, slots=None, result = await handler.async_handle(intent) return result except vol.Invalid as err: + _LOGGER.warning('Received invalid slot info for %s: %s', + intent_type, err) raise InvalidSlotInfo( 'Received invalid slot info for {}'.format(intent_type)) from err except IntentHandleError: @@ -167,7 +169,7 @@ class ServiceIntentHandler(IntentHandler): """ slot_schema = { - 'name': cv.string, + vol.Required('name'): cv.string, } def __init__(self, intent_type, domain, service, speech): diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index fc105b4e80b00a..5e9e63416ab71a 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -306,17 +306,17 @@ def test_light_profiles(self): data) -async def test_set_color_intent(hass): +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.SetColorIntentHandler()) + hass.helpers.intent.async_register(light.SetIntentHandler()) result = await hass.helpers.intent.async_handle( - 'test', light.INTENT_SET_COLOR, { + 'test', light.INTENT_SET, { 'name': { 'value': 'Hello', }, @@ -327,7 +327,7 @@ async def test_set_color_intent(hass): await hass.async_block_till_done() assert result.speech['plain']['speech'] == \ - 'Changed the color of hello 2 to blue' + 'Changed hello 2 to the color blue' assert len(calls) == 1 call = calls[0] @@ -337,15 +337,15 @@ async def test_set_color_intent(hass): assert call.data.get(light.ATTR_RGB_COLOR) == (0, 0, 255) -async def test_set_color_intent_tests_feature(hass): +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.SetColorIntentHandler()) + hass.helpers.intent.async_register(light.SetIntentHandler()) try: await hass.helpers.intent.async_handle( - 'test', light.INTENT_SET_COLOR, { + 'test', light.INTENT_SET, { 'name': { 'value': 'Hello', }, @@ -358,3 +358,38 @@ async def test_set_color_intent_tests_feature(hass): 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) \ No newline at end of file