From c9de2f015b1e438be7a943b29d567987eac7149f Mon Sep 17 00:00:00 2001 From: roiff Date: Fri, 4 May 2018 00:22:43 +0800 Subject: [PATCH] HomeKit - Climate: power state on/off support (#14082) * add power state support on off * Added check for current operation mode * Extended 'set_heat_cool' * Added tests --- .../components/homekit/type_thermostats.py | 24 ++++++-- .../homekit/test_type_thermostats.py | 61 ++++++++++++++++++- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/homekit/type_thermostats.py b/homeassistant/components/homekit/type_thermostats.py index ce10b96c51c..4faceefe850 100644 --- a/homeassistant/components/homekit/type_thermostats.py +++ b/homeassistant/components/homekit/type_thermostats.py @@ -5,10 +5,10 @@ from homeassistant.components.climate import ( ATTR_CURRENT_TEMPERATURE, ATTR_TEMPERATURE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, ATTR_OPERATION_MODE, ATTR_OPERATION_LIST, - STATE_HEAT, STATE_COOL, STATE_AUTO, + STATE_HEAT, STATE_COOL, STATE_AUTO, SUPPORT_ON_OFF, SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW) from homeassistant.const import ( - ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, + ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT) from . import TYPES @@ -41,6 +41,7 @@ class Thermostat(HomeAccessory): """Initialize a Thermostat accessory object.""" super().__init__(*args, category=CATEGORY_THERMOSTAT) self._unit = TEMP_CELSIUS + self.support_power_state = False self.heat_cool_flag_target_state = False self.temperature_flag_target_state = False self.coolingthresh_flag_target_state = False @@ -50,6 +51,8 @@ class Thermostat(HomeAccessory): self.chars = [] features = self.hass.states.get(self.entity_id) \ .attributes.get(ATTR_SUPPORTED_FEATURES) + if features & SUPPORT_ON_OFF: + self.support_power_state = True if features & SUPPORT_TEMP_RANGE: self.chars.extend((CHAR_COOLING_THRESHOLD_TEMPERATURE, CHAR_HEATING_THRESHOLD_TEMPERATURE)) @@ -93,6 +96,13 @@ class Thermostat(HomeAccessory): _LOGGER.debug('%s: Set heat-cool to %d', self.entity_id, value) self.heat_cool_flag_target_state = True hass_value = HC_HOMEKIT_TO_HASS[value] + if self.support_power_state is True: + params = {ATTR_ENTITY_ID: self.entity_id} + if hass_value == STATE_OFF: + self.hass.services.call('climate', 'turn_off', params) + return + else: + self.hass.services.call('climate', 'turn_on', params) self.hass.components.climate.set_operation_mode( operation_mode=hass_value, entity_id=self.entity_id) @@ -178,15 +188,19 @@ class Thermostat(HomeAccessory): # Update target operation mode operation_mode = new_state.attributes.get(ATTR_OPERATION_MODE) - if operation_mode \ - and operation_mode in HC_HASS_TO_HOMEKIT: + if self.support_power_state is True and new_state.state == STATE_OFF: + self.char_target_heat_cool.set_value( + HC_HASS_TO_HOMEKIT[STATE_OFF]) + elif operation_mode and operation_mode in HC_HASS_TO_HOMEKIT: if not self.heat_cool_flag_target_state: self.char_target_heat_cool.set_value( HC_HASS_TO_HOMEKIT[operation_mode]) self.heat_cool_flag_target_state = False # Set current operation mode based on temperatures and target mode - if operation_mode == STATE_HEAT: + if self.support_power_state is True and new_state.state == STATE_OFF: + current_operation_mode = STATE_OFF + elif operation_mode == STATE_HEAT: if isinstance(target_temp, float) and current_temp < target_temp: current_operation_mode = STATE_HEAT else: diff --git a/tests/components/homekit/test_type_thermostats.py b/tests/components/homekit/test_type_thermostats.py index adc3fb018f8..fe2a7f6cd02 100644 --- a/tests/components/homekit/test_type_thermostats.py +++ b/tests/components/homekit/test_type_thermostats.py @@ -7,7 +7,7 @@ from homeassistant.components.climate import ( ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH, ATTR_OPERATION_MODE, ATTR_OPERATION_LIST, STATE_COOL, STATE_HEAT, STATE_AUTO) from homeassistant.const import ( - ATTR_SERVICE, ATTR_SERVICE_DATA, ATTR_SUPPORTED_FEATURES, + ATTR_ENTITY_ID, ATTR_SERVICE, ATTR_SERVICE_DATA, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, EVENT_CALL_SERVICE, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT) @@ -261,6 +261,65 @@ class TestHomekitThermostats(unittest.TestCase): 25.0) self.assertEqual(acc.char_cooling_thresh_temp.value, 25.0) + def test_power_state(self): + """Test if accessory and HA are updated accordingly.""" + climate = 'climate.test' + + # SUPPORT_ON_OFF = True + self.hass.states.set(climate, STATE_HEAT, + {ATTR_SUPPORTED_FEATURES: 4096, + ATTR_OPERATION_MODE: STATE_HEAT, + ATTR_TEMPERATURE: 23.0, + ATTR_CURRENT_TEMPERATURE: 18.0}) + self.hass.block_till_done() + acc = self.thermostat_cls(self.hass, 'Climate', climate, + 2, config=None) + acc.run() + self.assertTrue(acc.support_power_state) + + self.assertEqual(acc.char_current_heat_cool.value, 1) + self.assertEqual(acc.char_target_heat_cool.value, 1) + + self.hass.states.set(climate, STATE_OFF, + {ATTR_OPERATION_MODE: STATE_HEAT, + ATTR_TEMPERATURE: 23.0, + ATTR_CURRENT_TEMPERATURE: 18.0}) + self.hass.block_till_done() + self.assertEqual(acc.char_current_heat_cool.value, 0) + self.assertEqual(acc.char_target_heat_cool.value, 0) + + self.hass.states.set(climate, STATE_OFF, + {ATTR_OPERATION_MODE: STATE_OFF, + ATTR_TEMPERATURE: 23.0, + ATTR_CURRENT_TEMPERATURE: 18.0}) + self.hass.block_till_done() + self.assertEqual(acc.char_current_heat_cool.value, 0) + self.assertEqual(acc.char_target_heat_cool.value, 0) + + # Set from HomeKit + acc.char_target_heat_cool.client_update_value(1) + self.hass.block_till_done() + self.assertEqual( + self.events[0].data[ATTR_SERVICE], 'turn_on') + self.assertEqual( + self.events[0].data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID], + climate) + self.assertEqual( + self.events[1].data[ATTR_SERVICE], 'set_operation_mode') + self.assertEqual( + self.events[1].data[ATTR_SERVICE_DATA][ATTR_OPERATION_MODE], + STATE_HEAT) + self.assertEqual(acc.char_target_heat_cool.value, 1) + + acc.char_target_heat_cool.client_update_value(0) + self.hass.block_till_done() + self.assertEqual( + self.events[2].data[ATTR_SERVICE], 'turn_off') + self.assertEqual( + self.events[2].data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID], + climate) + self.assertEqual(acc.char_target_heat_cool.value, 0) + def test_thermostat_fahrenheit(self): """Test if accessory and HA are updated accordingly.""" climate = 'climate.test'