diff --git a/homeassistant/components/climate/const.py b/homeassistant/components/climate/const.py index e213ae09de6..364c452bf4d 100644 --- a/homeassistant/components/climate/const.py +++ b/homeassistant/components/climate/const.py @@ -1,4 +1,4 @@ -"""Proides the constants needed for component.""" +"""Provides the constants needed for component.""" ATTR_AUX_HEAT = 'aux_heat' ATTR_AWAY_MODE = 'away_mode' diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py index 95b3c470d9e..4c329cac28f 100644 --- a/homeassistant/components/emulated_hue/hue_api.py +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -5,13 +5,16 @@ from aiohttp import web from homeassistant import core from homeassistant.const import ( - ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_SET, - SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER, STATE_ON, STATE_OFF, - HTTP_BAD_REQUEST, HTTP_NOT_FOUND, ATTR_SUPPORTED_FEATURES, + ATTR_ENTITY_ID, ATTR_TEMPERATURE, SERVICE_TURN_OFF, SERVICE_TURN_ON, + SERVICE_VOLUME_SET, SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER, STATE_ON, + STATE_OFF, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, ATTR_SUPPORTED_FEATURES ) from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS ) +from homeassistant.components.climate.const import ( + SERVICE_SET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE +) from homeassistant.components.media_player.const import ( ATTR_MEDIA_VOLUME_LEVEL, SUPPORT_VOLUME_SET, ) @@ -26,7 +29,7 @@ from homeassistant.components.cover import ( ) from homeassistant.components import ( - cover, fan, media_player, light, script, scene + climate, cover, fan, media_player, light, script, scene ) from homeassistant.components.http import HomeAssistantView @@ -262,6 +265,18 @@ class HueOneLightChangeView(HomeAssistantView): if brightness is not None: data['variables']['requested_level'] = brightness + # If the requested entity is a climate, set the temperature + elif entity.domain == climate.DOMAIN: + # We don't support turning climate devices on or off, + # only setting the temperature + service = None + + if entity_features & SUPPORT_TARGET_TEMPERATURE: + if brightness is not None: + domain = entity.domain + service = SERVICE_SET_TEMPERATURE + data[ATTR_TEMPERATURE] = brightness + # If the requested entity is a media player, convert to volume elif entity.domain == media_player.DOMAIN: if entity_features & SUPPORT_VOLUME_SET: @@ -318,8 +333,9 @@ class HueOneLightChangeView(HomeAssistantView): core.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True)) - hass.async_create_task(hass.services.async_call( - domain, service, data, blocking=True)) + if service is not None: + hass.async_create_task(hass.services.async_call( + domain, service, data, blocking=True)) json_response = \ [create_hue_success_response(entity_id, HUE_API_STATE_ON, result)] @@ -371,7 +387,7 @@ def parse_hue_api_put_light_body(request_json, entity): elif entity.domain in [ script.DOMAIN, media_player.DOMAIN, - fan.DOMAIN, cover.DOMAIN]: + fan.DOMAIN, cover.DOMAIN, climate.DOMAIN]: # Convert 0-255 to 0-100 level = brightness / 255 * 100 brightness = round(level) @@ -397,6 +413,10 @@ def get_entity_state(config, entity): if entity_features & SUPPORT_BRIGHTNESS: pass + elif entity.domain == climate.DOMAIN: + temperature = entity.attributes.get(ATTR_TEMPERATURE, 0) + # Convert 0-100 to 0-255 + final_brightness = round(temperature * 255 / 100) elif entity.domain == media_player.DOMAIN: level = entity.attributes.get( ATTR_MEDIA_VOLUME_LEVEL, 1.0 if final_state else 0.0) diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index 5e3d6d1019c..8be99a02148 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -11,7 +11,7 @@ from tests.common import get_test_instance_port from homeassistant import core, const, setup import homeassistant.components as core_components from homeassistant.components import ( - fan, http, light, script, emulated_hue, media_player, cover) + fan, http, light, script, emulated_hue, media_player, cover, climate) from homeassistant.components.emulated_hue import Config from homeassistant.components.emulated_hue.hue_api import ( HUE_API_STATE_ON, HUE_API_STATE_BRI, HueUsernameView, HueOneLightStateView, @@ -77,6 +77,15 @@ def hass_hue(loop, hass): } })) + loop.run_until_complete( + setup.async_setup_component(hass, climate.DOMAIN, { + 'climate': [ + { + 'platform': 'demo', + } + ] + })) + loop.run_until_complete( setup.async_setup_component(hass, media_player.DOMAIN, { 'media_player': [ @@ -136,6 +145,22 @@ def hass_hue(loop, hass): cover_entity.entity_id, cover_entity.state, attributes=attrs ) + # Expose Hvac + hvac_entity = hass.states.get('climate.hvac') + attrs = dict(hvac_entity.attributes) + attrs[emulated_hue.ATTR_EMULATED_HUE_HIDDEN] = False + hass.states.async_set( + hvac_entity.entity_id, hvac_entity.state, attributes=attrs + ) + + # Expose HeatPump + hp_entity = hass.states.get('climate.heatpump') + attrs = dict(hp_entity.attributes) + attrs[emulated_hue.ATTR_EMULATED_HUE_HIDDEN] = False + hass.states.async_set( + hp_entity.entity_id, hp_entity.state, attributes=attrs + ) + return hass @@ -189,6 +214,9 @@ def test_discover_lights(hue_client): assert 'fan.living_room_fan' in devices assert 'fan.ceiling_fan' not in devices assert 'cover.living_room_window' in devices + assert 'climate.hvac' in devices + assert 'climate.heatpump' in devices + assert 'climate.ecobee' not in devices @asyncio.coroutine @@ -316,6 +344,84 @@ def test_put_light_state_script(hass_hue, hue_client): assert kitchen_light.attributes[light.ATTR_BRIGHTNESS] == level +@asyncio.coroutine +def test_put_light_state_climate_set_temperature(hass_hue, hue_client): + """Test setting climate temperature.""" + brightness = 19 + temperature = round(brightness / 255 * 100) + + hvac_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'climate.hvac', True, brightness) + + hvac_result_json = yield from hvac_result.json() + + assert hvac_result.status == 200 + assert len(hvac_result_json) == 2 + + hvac = hass_hue.states.get('climate.hvac') + assert hvac.state == climate.const.STATE_COOL + assert hvac.attributes[climate.ATTR_TEMPERATURE] == temperature + assert hvac.attributes[climate.ATTR_OPERATION_MODE] == \ + climate.const.STATE_COOL + + # Make sure we can't change the ecobee temperature since it's not exposed + ecobee_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'climate.ecobee', True) + assert ecobee_result.status == 404 + + +@asyncio.coroutine +def test_put_light_state_climate_turn_on(hass_hue, hue_client): + """Test inability to turn climate on.""" + yield from hass_hue.services.async_call( + climate.DOMAIN, const.SERVICE_TURN_OFF, + {const.ATTR_ENTITY_ID: 'climate.heatpump'}, + blocking=True) + + # Somehow after calling the above service the device gets unexposed, + # so we need to expose it again + hp_entity = hass_hue.states.get('climate.heatpump') + attrs = dict(hp_entity.attributes) + attrs[emulated_hue.ATTR_EMULATED_HUE_HIDDEN] = False + hass_hue.states.async_set( + hp_entity.entity_id, hp_entity.state, attributes=attrs + ) + + hp_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'climate.heatpump', True) + + hp_result_json = yield from hp_result.json() + + assert hp_result.status == 200 + assert len(hp_result_json) == 1 + + hp = hass_hue.states.get('climate.heatpump') + assert hp.state == STATE_OFF + assert hp.attributes[climate.ATTR_OPERATION_MODE] == \ + climate.const.STATE_HEAT + + +@asyncio.coroutine +def test_put_light_state_climate_turn_off(hass_hue, hue_client): + """Test inability to turn climate off.""" + hp_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'climate.heatpump', False) + + hp_result_json = yield from hp_result.json() + + assert hp_result.status == 200 + assert len(hp_result_json) == 1 + + hp = hass_hue.states.get('climate.heatpump') + assert hp.state == climate.const.STATE_HEAT + assert hp.attributes[climate.ATTR_OPERATION_MODE] == \ + climate.const.STATE_HEAT + + @asyncio.coroutine def test_put_light_state_media_player(hass_hue, hue_client): """Test turning on media player and setting volume."""