"""The tests for the Google Assistant component.""" # pylint: disable=protected-access import asyncio import json from aiohttp.hdrs import CONTENT_TYPE, AUTHORIZATION import pytest from homeassistant import core, const, setup from homeassistant.components import ( fan, cover, light, switch, climate, async_setup, media_player) from homeassistant.components import google_assistant as ga from homeassistant.util.unit_system import IMPERIAL_SYSTEM from . import DEMO_DEVICES API_PASSWORD = "test1234" HA_HEADERS = { const.HTTP_HEADER_HA_AUTH: API_PASSWORD, CONTENT_TYPE: const.CONTENT_TYPE_JSON, } PROJECT_ID = 'hasstest-1234' CLIENT_ID = 'helloworld' ACCESS_TOKEN = 'superdoublesecret' AUTH_HEADER = {AUTHORIZATION: 'Bearer {}'.format(ACCESS_TOKEN)} @pytest.fixture def assistant_client(loop, hass, test_client): """Create web client for the Google Assistant API.""" loop.run_until_complete( setup.async_setup_component(hass, 'google_assistant', { 'google_assistant': { 'project_id': PROJECT_ID, 'client_id': CLIENT_ID, 'access_token': ACCESS_TOKEN, 'entity_config': { 'light.ceiling_lights': { 'aliases': ['top lights', 'ceiling lights'], 'name': 'Roof Lights', }, 'switch.decorative_lights': { 'type': 'light' } } } })) return loop.run_until_complete(test_client(hass.http.app)) @pytest.fixture def hass_fixture(loop, hass): """Set up a HOme Assistant instance for these tests.""" # We need to do this to get access to homeassistant/turn_(on,off) loop.run_until_complete(async_setup(hass, {core.DOMAIN: {}})) loop.run_until_complete( setup.async_setup_component(hass, light.DOMAIN, { 'light': [{ 'platform': 'demo' }] })) loop.run_until_complete( setup.async_setup_component(hass, switch.DOMAIN, { 'switch': [{ 'platform': 'demo' }] })) loop.run_until_complete( setup.async_setup_component(hass, cover.DOMAIN, { 'cover': [{ 'platform': 'demo' }], })) loop.run_until_complete( setup.async_setup_component(hass, media_player.DOMAIN, { 'media_player': [{ 'platform': 'demo' }] })) loop.run_until_complete( setup.async_setup_component(hass, fan.DOMAIN, { 'fan': [{ 'platform': 'demo' }] })) loop.run_until_complete( setup.async_setup_component(hass, climate.DOMAIN, { 'climate': [{ 'platform': 'demo' }] })) return hass @asyncio.coroutine def test_auth(assistant_client): """Test the auth process.""" result = yield from assistant_client.get( ga.const.GOOGLE_ASSISTANT_API_ENDPOINT + '/auth', params={ 'redirect_uri': 'http://testurl/r/{}'.format(PROJECT_ID), 'client_id': CLIENT_ID, 'state': 'random1234', }, allow_redirects=False) assert result.status == 301 loc = result.headers.get('Location') assert ACCESS_TOKEN in loc @asyncio.coroutine def test_sync_request(hass_fixture, assistant_client): """Test a sync request.""" reqid = '5711642932632160983' data = {'requestId': reqid, 'inputs': [{'intent': 'action.devices.SYNC'}]} result = yield from assistant_client.post( ga.const.GOOGLE_ASSISTANT_API_ENDPOINT, data=json.dumps(data), headers=AUTH_HEADER) assert result.status == 200 body = yield from result.json() assert body.get('requestId') == reqid devices = body['payload']['devices'] assert ( sorted([dev['id'] for dev in devices]) == sorted([dev['id'] for dev in DEMO_DEVICES])) for dev, demo in zip( sorted(devices, key=lambda d: d['id']), sorted(DEMO_DEVICES, key=lambda d: d['id'])): assert dev['name'] == demo['name'] assert set(dev['traits']) == set(demo['traits']) assert dev['type'] == demo['type'] if 'attributes' in demo: assert dev['attributes'] == demo['attributes'] @asyncio.coroutine def test_query_request(hass_fixture, assistant_client): """Test a query request.""" reqid = '5711642932632160984' data = { 'requestId': reqid, 'inputs': [{ 'intent': 'action.devices.QUERY', 'payload': { 'devices': [{ 'id': "light.ceiling_lights", }, { 'id': "light.bed_light", }, { 'id': "light.kitchen_lights", }] } }] } result = yield from assistant_client.post( ga.const.GOOGLE_ASSISTANT_API_ENDPOINT, data=json.dumps(data), headers=AUTH_HEADER) assert result.status == 200 body = yield from result.json() assert body.get('requestId') == reqid devices = body['payload']['devices'] assert len(devices) == 3 assert devices['light.bed_light']['on'] is False assert devices['light.ceiling_lights']['on'] is True assert devices['light.ceiling_lights']['brightness'] == 70 assert devices['light.kitchen_lights']['color']['spectrumRGB'] == 16727919 assert devices['light.kitchen_lights']['color']['temperature'] == 4166 @asyncio.coroutine def test_query_climate_request(hass_fixture, assistant_client): """Test a query request.""" reqid = '5711642932632160984' data = { 'requestId': reqid, 'inputs': [{ 'intent': 'action.devices.QUERY', 'payload': { 'devices': [ {'id': 'climate.hvac'}, {'id': 'climate.heatpump'}, {'id': 'climate.ecobee'}, ] } }] } result = yield from assistant_client.post( ga.const.GOOGLE_ASSISTANT_API_ENDPOINT, data=json.dumps(data), headers=AUTH_HEADER) assert result.status == 200 body = yield from result.json() assert body.get('requestId') == reqid devices = body['payload']['devices'] assert devices == { 'climate.heatpump': { 'thermostatTemperatureSetpoint': 20.0, 'thermostatTemperatureAmbient': 25.0, 'thermostatMode': 'heat', }, 'climate.ecobee': { 'thermostatTemperatureSetpointHigh': 24, 'thermostatTemperatureAmbient': 23, 'thermostatMode': 'heat', 'thermostatTemperatureSetpointLow': 21 }, 'climate.hvac': { 'thermostatTemperatureSetpoint': 21, 'thermostatTemperatureAmbient': 22, 'thermostatMode': 'cool', 'thermostatHumidityAmbient': 54, } } @asyncio.coroutine def test_query_climate_request_f(hass_fixture, assistant_client): """Test a query request.""" hass_fixture.config.units = IMPERIAL_SYSTEM reqid = '5711642932632160984' data = { 'requestId': reqid, 'inputs': [{ 'intent': 'action.devices.QUERY', 'payload': { 'devices': [ {'id': 'climate.hvac'}, {'id': 'climate.heatpump'}, {'id': 'climate.ecobee'}, ] } }] } result = yield from assistant_client.post( ga.const.GOOGLE_ASSISTANT_API_ENDPOINT, data=json.dumps(data), headers=AUTH_HEADER) assert result.status == 200 body = yield from result.json() assert body.get('requestId') == reqid devices = body['payload']['devices'] assert devices == { 'climate.heatpump': { 'thermostatTemperatureSetpoint': -6.7, 'thermostatTemperatureAmbient': -3.9, 'thermostatMode': 'heat', }, 'climate.ecobee': { 'thermostatTemperatureSetpointHigh': -4.4, 'thermostatTemperatureAmbient': -5, 'thermostatMode': 'heat', 'thermostatTemperatureSetpointLow': -6.1, }, 'climate.hvac': { 'thermostatTemperatureSetpoint': -6.1, 'thermostatTemperatureAmbient': -5.6, 'thermostatMode': 'cool', 'thermostatHumidityAmbient': 54, } } @asyncio.coroutine def test_execute_request(hass_fixture, assistant_client): """Test a execute request.""" reqid = '5711642932632160985' data = { 'requestId': reqid, 'inputs': [{ 'intent': 'action.devices.EXECUTE', 'payload': { "commands": [{ "devices": [{ "id": "light.ceiling_lights", }, { "id": "switch.decorative_lights", }], "execution": [{ "command": "action.devices.commands.OnOff", "params": { "on": False } }] }, { "devices": [{ "id": "light.kitchen_lights", }], "execution": [{ "command": "action.devices.commands.ColorAbsolute", "params": { "color": { "spectrumRGB": 16711680, "temperature": 2100 } } }] }, { "devices": [{ "id": "light.kitchen_lights", }], "execution": [{ "command": "action.devices.commands.ColorAbsolute", "params": { "color": { "spectrumRGB": 16711680 } } }] }, { "devices": [{ "id": "light.bed_light" }], "execution": [{ "command": "action.devices.commands.ColorAbsolute", "params": { "color": { "spectrumRGB": 65280 } } }, { "command": "action.devices.commands.ColorAbsolute", "params": { "color": { "temperature": 4700 } } }] }] } }] } result = yield from assistant_client.post( ga.const.GOOGLE_ASSISTANT_API_ENDPOINT, data=json.dumps(data), headers=AUTH_HEADER) assert result.status == 200 body = yield from result.json() assert body.get('requestId') == reqid commands = body['payload']['commands'] assert len(commands) == 6 ceiling = hass_fixture.states.get('light.ceiling_lights') assert ceiling.state == 'off' kitchen = hass_fixture.states.get('light.kitchen_lights') assert kitchen.attributes.get(light.ATTR_COLOR_TEMP) == 476 assert kitchen.attributes.get(light.ATTR_RGB_COLOR) == (255, 0, 0) bed = hass_fixture.states.get('light.bed_light') assert bed.attributes.get(light.ATTR_COLOR_TEMP) == 212 assert bed.attributes.get(light.ATTR_RGB_COLOR) == (0, 255, 0) assert hass_fixture.states.get('switch.decorative_lights').state == 'off'