Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix homekit_controller on climate-1.0 #24948

Merged
merged 3 commits into from
Jul 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions homeassistant/components/climate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

DEFAULT_MIN_TEMP = 7
DEFAULT_MAX_TEMP = 35
DEFAULT_MIN_HUMITIDY = 30
DEFAULT_MIN_HUMIDITY = 30
DEFAULT_MAX_HUMIDITY = 99

ENTITY_ID_FORMAT = DOMAIN + '.{}'
Expand Down Expand Up @@ -443,7 +443,7 @@ def max_temp(self) -> float:
@property
def min_humidity(self) -> int:
"""Return the minimum humidity."""
return DEFAULT_MIN_HUMITIDY
return DEFAULT_MIN_HUMIDITY

@property
def max_humidity(self) -> int:
Expand Down
57 changes: 31 additions & 26 deletions homeassistant/components/homekit_controller/climate.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Support for Homekit climate devices."""
import logging

from homeassistant.components.climate import ClimateDevice
from homeassistant.components.climate import (
ClimateDevice, DEFAULT_MIN_HUMIDITY, DEFAULT_MAX_HUMIDITY,
)
from homeassistant.components.climate.const import (
HVAC_MODE_AUTO, HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF,

SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_HUMIDITY,
SUPPORT_TARGET_HUMIDITY_HIGH, SUPPORT_TARGET_HUMIDITY_LOW)
CURRENT_HVAC_OFF, CURRENT_HVAC_HEAT, CURRENT_HVAC_COOL,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_HUMIDITY)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS

from . import KNOWN_DEVICES, HomeKitEntity
Expand All @@ -26,6 +27,12 @@

DEFAULT_VALID_MODES = list(MODE_HOMEKIT_TO_HASS)

CURRENT_MODE_HOMEKIT_TO_HASS = {
0: CURRENT_HVAC_OFF,
1: CURRENT_HVAC_HEAT,
2: CURRENT_HVAC_COOL,
}


async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
Expand Down Expand Up @@ -54,6 +61,7 @@ class HomeKitClimateDevice(HomeKitEntity, ClimateDevice):
def __init__(self, *args):
"""Initialise the device."""
self._state = None
self._target_mode = None
self._current_mode = None
self._valid_modes = []
self._current_temp = None
Expand All @@ -62,8 +70,8 @@ def __init__(self, *args):
self._target_humidity = None
self._min_target_temp = None
self._max_target_temp = None
self._min_target_humidity = None
self._max_target_humidity = None
self._min_target_humidity = DEFAULT_MIN_HUMIDITY
self._max_target_humidity = DEFAULT_MAX_HUMIDITY
super().__init__(*args)

def get_characteristic_types(self):
Expand Down Expand Up @@ -116,17 +124,22 @@ def _setup_relative_humidity_target(self, characteristic):

if 'minValue' in characteristic:
self._min_target_humidity = characteristic['minValue']
self._features |= SUPPORT_TARGET_HUMIDITY_LOW

if 'maxValue' in characteristic:
self._max_target_humidity = characteristic['maxValue']
self._features |= SUPPORT_TARGET_HUMIDITY_HIGH

def _update_heating_cooling_current(self, value):
self._state = MODE_HOMEKIT_TO_HASS.get(value)
# This characteristic describes the current mode of a device,
# e.g. a thermostat is "heating" a room to 75 degrees Fahrenheit.
# Can be 0 - 2 (Off, Heat, Cool)
self._current_mode = CURRENT_MODE_HOMEKIT_TO_HASS.get(value)

def _update_heating_cooling_target(self, value):
self._current_mode = MODE_HOMEKIT_TO_HASS.get(value)
# This characteristic describes the target mode
# E.g. should the device start heating a room if the temperature
# falls below the target temperature.
# Can be 0 - 3 (Off, Heat, Cool, Auto)
self._target_mode = MODE_HOMEKIT_TO_HASS.get(value)

def _update_temperature_current(self, value):
self._current_temp = value
Expand Down Expand Up @@ -163,19 +176,6 @@ async def async_set_hvac_mode(self, hvac_mode):
'value': MODE_HASS_TO_HOMEKIT[hvac_mode]}]
await self._accessory.put_characteristics(characteristics)

@property
def state(self):
"""Return the current state."""
# If the device reports its operating mode as off, it sometimes doesn't
# report a new state.
if self._current_mode == HVAC_MODE_OFF:
return HVAC_MODE_OFF

if self._state == HVAC_MODE_OFF and \
self._current_mode != HVAC_MODE_OFF:
return HVAC_MODE_OFF
return self._state

@property
def current_temperature(self):
"""Return the current temperature."""
Expand Down Expand Up @@ -221,13 +221,18 @@ def max_humidity(self):
return self._max_target_humidity

@property
def hvac_mode(self):
"""Return current operation ie. heat, cool, idle."""
def hvac_action(self):
"""Return the current running hvac operation."""
return self._current_mode

@property
def hvac_mode(self):
"""Return hvac operation ie. heat, cool mode."""
return self._target_mode

@property
def hvac_modes(self):
"""Return the list of available operation modes."""
"""Return the list of available hvac operation modes."""
return self._valid_modes

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@

from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_HUMIDITY,
SUPPORT_TARGET_HUMIDITY_HIGH, SUPPORT_TARGET_HUMIDITY_LOW,
SUPPORT_OPERATION_MODE)
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_HUMIDITY)


from tests.components.homekit_controller.common import (
Expand All @@ -36,12 +34,10 @@ async def test_ecobee3_setup(hass):
climate_state = await climate_helper.poll_and_get_state()
assert climate_state.attributes['friendly_name'] == 'HomeW'
assert climate_state.attributes['supported_features'] == (
SUPPORT_TARGET_TEMPERATURE | SUPPORT_TARGET_HUMIDITY |
SUPPORT_TARGET_HUMIDITY_HIGH | SUPPORT_TARGET_HUMIDITY_LOW |
SUPPORT_OPERATION_MODE
SUPPORT_TARGET_TEMPERATURE | SUPPORT_TARGET_HUMIDITY
)

assert climate_state.attributes['operation_list'] == [
assert climate_state.attributes['hvac_modes'] == [
'off',
'heat',
'cool',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""

from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE)
SUPPORT_TARGET_TEMPERATURE)
from tests.components.homekit_controller.common import (
setup_accessories_from_file, setup_test_accessories, Helper
)
Expand All @@ -25,7 +25,7 @@ async def test_lennox_e30_setup(hass):
climate_state = await climate_helper.poll_and_get_state()
assert climate_state.attributes['friendly_name'] == 'Lennox'
assert climate_state.attributes['supported_features'] == (
SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE
SUPPORT_TARGET_TEMPERATURE
)

device_registry = await hass.helpers.device_registry.async_get_registry()
Expand Down
41 changes: 34 additions & 7 deletions tests/components/homekit_controller/test_climate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Basic checks for HomeKitclimate."""
from homeassistant.components.climate.const import (
DOMAIN, SERVICE_SET_OPERATION_MODE, SERVICE_SET_TEMPERATURE,
DOMAIN, SERVICE_SET_HVAC_MODE, SERVICE_SET_TEMPERATURE,
SERVICE_SET_HUMIDITY)
from tests.components.homekit_controller.common import (
FakeService, setup_test_component)
Expand Down Expand Up @@ -50,7 +50,7 @@ async def test_climate_respect_supported_op_modes_1(hass, utcnow):
helper = await setup_test_component(hass, [service])

state = await helper.poll_and_get_state()
assert state.attributes['operation_list'] == ['off', 'heat']
assert state.attributes['hvac_modes'] == ['off', 'heat']


async def test_climate_respect_supported_op_modes_2(hass, utcnow):
Expand All @@ -63,7 +63,7 @@ async def test_climate_respect_supported_op_modes_2(hass, utcnow):
helper = await setup_test_component(hass, [service])

state = await helper.poll_and_get_state()
assert state.attributes['operation_list'] == ['off', 'heat', 'cool']
assert state.attributes['hvac_modes'] == ['off', 'heat', 'cool']


async def test_climate_change_thermostat_state(hass, utcnow):
Expand All @@ -72,16 +72,16 @@ async def test_climate_change_thermostat_state(hass, utcnow):

helper = await setup_test_component(hass, [ThermostatService()])

await hass.services.async_call(DOMAIN, SERVICE_SET_OPERATION_MODE, {
await hass.services.async_call(DOMAIN, SERVICE_SET_HVAC_MODE, {
'entity_id': 'climate.testdevice',
'operation_mode': 'heat',
'hvac_mode': 'heat',
}, blocking=True)

assert helper.characteristics[HEATING_COOLING_TARGET].value == 1

await hass.services.async_call(DOMAIN, SERVICE_SET_OPERATION_MODE, {
await hass.services.async_call(DOMAIN, SERVICE_SET_HVAC_MODE, {
'entity_id': 'climate.testdevice',
'operation_mode': 'cool',
'hvac_mode': 'cool',
}, blocking=True)
assert helper.characteristics[HEATING_COOLING_TARGET].value == 2

Expand Down Expand Up @@ -153,3 +153,30 @@ async def test_climate_read_thermostat_state(hass, utcnow):
assert state.state == 'cool'
assert state.attributes['current_temperature'] == 21
assert state.attributes['current_humidity'] == 45


async def test_hvac_mode_vs_hvac_action(hass, utcnow):
"""Check that we haven't conflated hvac_mode and hvac_action."""
helper = await setup_test_component(hass, [create_thermostat_service()])

# Simulate that current temperature is above target temp
# Heating might be on, but hvac_action currently 'off'
helper.characteristics[TEMPERATURE_CURRENT].value = 22
helper.characteristics[TEMPERATURE_TARGET].value = 21
helper.characteristics[HEATING_COOLING_CURRENT].value = 0
helper.characteristics[HEATING_COOLING_TARGET].value = 1
helper.characteristics[HUMIDITY_CURRENT].value = 50
helper.characteristics[HUMIDITY_TARGET].value = 45

state = await helper.poll_and_get_state()
assert state.state == 'heat'
assert state.attributes['hvac_action'] == 'off'

# Simulate that current temperature is below target temp
# Heating might be on and hvac_action currently 'heat'
helper.characteristics[TEMPERATURE_CURRENT].value = 19
helper.characteristics[HEATING_COOLING_CURRENT].value = 1

state = await helper.poll_and_get_state()
assert state.state == 'heat'
assert state.attributes['hvac_action'] == 'heating'