Skip to content

Commit

Permalink
Allow emulated hue to set climate component temperature (#19034)
Browse files Browse the repository at this point in the history
  • Loading branch information
chilicheech authored and amelchio committed Mar 10, 2019
1 parent 5debc88 commit 77dc759
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 9 deletions.
2 changes: 1 addition & 1 deletion 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'
Expand Down
34 changes: 27 additions & 7 deletions homeassistant/components/emulated_hue/hue_api.py
Expand Up @@ -5,13 +5,16 @@

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,
)
Expand All @@ -26,7 +29,7 @@
)

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
Expand Down Expand Up @@ -262,6 +265,18 @@ async def put(self, request, username, entity_number):
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:
Expand Down Expand Up @@ -318,8 +333,9 @@ async def put(self, request, username, entity_number):
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)]
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
108 changes: 107 additions & 1 deletion tests/components/emulated_hue/test_hue_api.py
Expand Up @@ -11,7 +11,7 @@
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,
Expand Down Expand Up @@ -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': [
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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."""
Expand Down

0 comments on commit 77dc759

Please sign in to comment.