diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index d35d38c6455..0b4cdf15fb5 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -127,6 +127,7 @@ CHAR_VALVE_TYPE = 'ValveType' # #### Properties #### PROP_MAX_VALUE = 'maxValue' PROP_MIN_VALUE = 'minValue' +PROP_MIN_STEP = 'minStep' PROP_CELSIUS = {'minValue': -273, 'maxValue': 999} # #### Device Classes #### diff --git a/homeassistant/components/homekit/type_thermostats.py b/homeassistant/components/homekit/type_thermostats.py index 49da6db6125..f78a05b1a45 100644 --- a/homeassistant/components/homekit/type_thermostats.py +++ b/homeassistant/components/homekit/type_thermostats.py @@ -28,7 +28,7 @@ from .const import ( CHAR_HEATING_THRESHOLD_TEMPERATURE, CHAR_TARGET_TEMPERATURE, CHAR_TEMP_DISPLAY_UNITS, DEFAULT_MAX_TEMP_WATER_HEATER, DEFAULT_MIN_TEMP_WATER_HEATER, - PROP_MAX_VALUE, PROP_MIN_VALUE, SERV_THERMOSTAT) + PROP_MAX_VALUE, PROP_MIN_STEP, PROP_MIN_VALUE, SERV_THERMOSTAT) from .util import temperature_to_homekit, temperature_to_states _LOGGER = logging.getLogger(__name__) @@ -83,7 +83,8 @@ class Thermostat(HomeAccessory): self.char_target_temp = serv_thermostat.configure_char( CHAR_TARGET_TEMPERATURE, value=21.0, properties={PROP_MIN_VALUE: min_temp, - PROP_MAX_VALUE: max_temp}, + PROP_MAX_VALUE: max_temp, + PROP_MIN_STEP: 0.5}, setter_callback=self.set_target_temperature) # Display units characteristic @@ -97,13 +98,15 @@ class Thermostat(HomeAccessory): self.char_cooling_thresh_temp = serv_thermostat.configure_char( CHAR_COOLING_THRESHOLD_TEMPERATURE, value=23.0, properties={PROP_MIN_VALUE: min_temp, - PROP_MAX_VALUE: max_temp}, + PROP_MAX_VALUE: max_temp, + PROP_MIN_STEP: 0.5}, setter_callback=self.set_cooling_threshold) if CHAR_HEATING_THRESHOLD_TEMPERATURE in self.chars: self.char_heating_thresh_temp = serv_thermostat.configure_char( CHAR_HEATING_THRESHOLD_TEMPERATURE, value=19.0, properties={PROP_MIN_VALUE: min_temp, - PROP_MAX_VALUE: max_temp}, + PROP_MAX_VALUE: max_temp, + PROP_MIN_STEP: 0.5}, setter_callback=self.set_heating_threshold) def get_temperature_range(self): @@ -112,11 +115,13 @@ class Thermostat(HomeAccessory): .attributes.get(ATTR_MAX_TEMP) max_temp = temperature_to_homekit(max_temp, self._unit) if max_temp \ else DEFAULT_MAX_TEMP + max_temp = round(max_temp * 2) / 2 min_temp = self.hass.states.get(self.entity_id) \ .attributes.get(ATTR_MIN_TEMP) min_temp = temperature_to_homekit(min_temp, self._unit) if min_temp \ else DEFAULT_MIN_TEMP + min_temp = round(min_temp * 2) / 2 return min_temp, max_temp @@ -140,7 +145,7 @@ class Thermostat(HomeAccessory): @debounce def set_cooling_threshold(self, value): """Set cooling threshold temp to value if call came from HomeKit.""" - _LOGGER.debug('%s: Set cooling threshold temperature to %.2f°C', + _LOGGER.debug('%s: Set cooling threshold temperature to %.1f°C', self.entity_id, value) self._flag_coolingthresh = True low = self.char_heating_thresh_temp.value @@ -156,7 +161,7 @@ class Thermostat(HomeAccessory): @debounce def set_heating_threshold(self, value): """Set heating threshold temp to value if call came from HomeKit.""" - _LOGGER.debug('%s: Set heating threshold temperature to %.2f°C', + _LOGGER.debug('%s: Set heating threshold temperature to %.1f°C', self.entity_id, value) self._flag_heatingthresh = True high = self.char_cooling_thresh_temp.value @@ -172,7 +177,7 @@ class Thermostat(HomeAccessory): @debounce def set_target_temperature(self, value): """Set target temperature to value if call came from HomeKit.""" - _LOGGER.debug('%s: Set target temperature to %.2f°C', + _LOGGER.debug('%s: Set target temperature to %.1f°C', self.entity_id, value) self._flag_temperature = True temperature = temperature_to_states(value, self._unit) @@ -301,7 +306,8 @@ class WaterHeater(HomeAccessory): self.char_target_temp = serv_thermostat.configure_char( CHAR_TARGET_TEMPERATURE, value=50.0, properties={PROP_MIN_VALUE: min_temp, - PROP_MAX_VALUE: max_temp}, + PROP_MAX_VALUE: max_temp, + PROP_MIN_STEP: 0.5}, setter_callback=self.set_target_temperature) self.char_display_units = serv_thermostat.configure_char( @@ -313,11 +319,13 @@ class WaterHeater(HomeAccessory): .attributes.get(ATTR_MAX_TEMP) max_temp = temperature_to_homekit(max_temp, self._unit) if max_temp \ else DEFAULT_MAX_TEMP_WATER_HEATER + max_temp = round(max_temp * 2) / 2 min_temp = self.hass.states.get(self.entity_id) \ .attributes.get(ATTR_MIN_TEMP) min_temp = temperature_to_homekit(min_temp, self._unit) if min_temp \ else DEFAULT_MIN_TEMP_WATER_HEATER + min_temp = round(min_temp * 2) / 2 return min_temp, max_temp @@ -332,7 +340,7 @@ class WaterHeater(HomeAccessory): @debounce def set_target_temperature(self, value): """Set target temperature to value if call came from HomeKit.""" - _LOGGER.debug('%s: Set target temperature to %.2f°C', + _LOGGER.debug('%s: Set target temperature to %.1f°C', self.entity_id, value) self._flag_temperature = True temperature = temperature_to_states(value, self._unit) diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 43ae4df3b50..10fdc07e7b4 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -135,12 +135,12 @@ def convert_to_float(state): def temperature_to_homekit(temperature, unit): """Convert temperature to Celsius for HomeKit.""" - return round(temp_util.convert(temperature, unit, TEMP_CELSIUS), 1) + return round(temp_util.convert(temperature, unit, TEMP_CELSIUS) * 2) / 2 def temperature_to_states(temperature, unit): """Convert temperature back from Celsius to Home Assistant unit.""" - return round(temp_util.convert(temperature, TEMP_CELSIUS, unit), 1) + return round(temp_util.convert(temperature, TEMP_CELSIUS, unit) * 2) / 2 def density_to_air_quality(density): diff --git a/tests/components/homekit/test_type_thermostats.py b/tests/components/homekit/test_type_thermostats.py index 795cb5db7d2..f645cddf730 100644 --- a/tests/components/homekit/test_type_thermostats.py +++ b/tests/components/homekit/test_type_thermostats.py @@ -11,7 +11,7 @@ from homeassistant.components.climate import ( DOMAIN as DOMAIN_CLIMATE, STATE_AUTO, STATE_COOL, STATE_HEAT) from homeassistant.components.homekit.const import ( ATTR_VALUE, DEFAULT_MAX_TEMP_WATER_HEATER, DEFAULT_MIN_TEMP_WATER_HEATER, - PROP_MAX_VALUE, PROP_MIN_VALUE) + PROP_MAX_VALUE, PROP_MIN_STEP, PROP_MIN_VALUE) from homeassistant.components.water_heater import ( DOMAIN as DOMAIN_WATER_HEATER) from homeassistant.const import ( @@ -48,6 +48,7 @@ async def test_thermostat(hass, hk_driver, cls, events): assert acc.aid == 2 assert acc.category == 9 # Thermostat + assert acc.get_temperature_range() == (7.0, 35.0) assert acc.char_current_heat_cool.value == 0 assert acc.char_target_heat_cool.value == 0 assert acc.char_current_temp.value == 21.0 @@ -58,11 +59,12 @@ async def test_thermostat(hass, hk_driver, cls, events): assert acc.char_target_temp.properties[PROP_MAX_VALUE] == DEFAULT_MAX_TEMP assert acc.char_target_temp.properties[PROP_MIN_VALUE] == DEFAULT_MIN_TEMP + assert acc.char_target_temp.properties[PROP_MIN_STEP] == 0.5 hass.states.async_set(entity_id, STATE_HEAT, {ATTR_OPERATION_MODE: STATE_HEAT, - ATTR_TEMPERATURE: 22.0, - ATTR_CURRENT_TEMPERATURE: 18.0}) + ATTR_TEMPERATURE: 22.2, + ATTR_CURRENT_TEMPERATURE: 17.8}) await hass.async_block_till_done() assert acc.char_target_temp.value == 22.0 assert acc.char_current_heat_cool.value == 1 @@ -193,10 +195,12 @@ async def test_thermostat_auto(hass, hk_driver, cls, events): == DEFAULT_MAX_TEMP assert acc.char_cooling_thresh_temp.properties[PROP_MIN_VALUE] \ == DEFAULT_MIN_TEMP + assert acc.char_cooling_thresh_temp.properties[PROP_MIN_STEP] == 0.5 assert acc.char_heating_thresh_temp.properties[PROP_MAX_VALUE] \ == DEFAULT_MAX_TEMP assert acc.char_heating_thresh_temp.properties[PROP_MIN_VALUE] \ == DEFAULT_MIN_TEMP + assert acc.char_heating_thresh_temp.properties[PROP_MIN_STEP] == 0.5 hass.states.async_set(entity_id, STATE_AUTO, {ATTR_OPERATION_MODE: STATE_AUTO, @@ -339,10 +343,11 @@ async def test_thermostat_fahrenheit(hass, hk_driver, cls, events): hass.states.async_set(entity_id, STATE_AUTO, {ATTR_OPERATION_MODE: STATE_AUTO, ATTR_TARGET_TEMP_HIGH: 75.2, - ATTR_TARGET_TEMP_LOW: 68, + ATTR_TARGET_TEMP_LOW: 68.1, ATTR_TEMPERATURE: 71.6, ATTR_CURRENT_TEMPERATURE: 73.4}) await hass.async_block_till_done() + assert acc.get_temperature_range() == (7.0, 35.0) assert acc.char_heating_thresh_temp.value == 20.0 assert acc.char_cooling_thresh_temp.value == 24.0 assert acc.char_current_temp.value == 23.0 @@ -358,28 +363,28 @@ async def test_thermostat_fahrenheit(hass, hk_driver, cls, events): await hass.async_block_till_done() assert call_set_temperature[0] assert call_set_temperature[0].data[ATTR_ENTITY_ID] == entity_id - assert call_set_temperature[0].data[ATTR_TARGET_TEMP_HIGH] == 73.4 + assert call_set_temperature[0].data[ATTR_TARGET_TEMP_HIGH] == 73.5 assert call_set_temperature[0].data[ATTR_TARGET_TEMP_LOW] == 68 assert len(events) == 1 - assert events[-1].data[ATTR_VALUE] == 'cooling threshold 73.4°F' + assert events[-1].data[ATTR_VALUE] == 'cooling threshold 73.5°F' await hass.async_add_job( acc.char_heating_thresh_temp.client_update_value, 22) await hass.async_block_till_done() assert call_set_temperature[1] assert call_set_temperature[1].data[ATTR_ENTITY_ID] == entity_id - assert call_set_temperature[1].data[ATTR_TARGET_TEMP_HIGH] == 73.4 - assert call_set_temperature[1].data[ATTR_TARGET_TEMP_LOW] == 71.6 + assert call_set_temperature[1].data[ATTR_TARGET_TEMP_HIGH] == 73.5 + assert call_set_temperature[1].data[ATTR_TARGET_TEMP_LOW] == 71.5 assert len(events) == 2 - assert events[-1].data[ATTR_VALUE] == 'heating threshold 71.6°F' + assert events[-1].data[ATTR_VALUE] == 'heating threshold 71.5°F' await hass.async_add_job(acc.char_target_temp.client_update_value, 24.0) await hass.async_block_till_done() assert call_set_temperature[2] assert call_set_temperature[2].data[ATTR_ENTITY_ID] == entity_id - assert call_set_temperature[2].data[ATTR_TEMPERATURE] == 75.2 + assert call_set_temperature[2].data[ATTR_TEMPERATURE] == 75.0 assert len(events) == 3 - assert events[-1].data[ATTR_VALUE] == '75.2°F' + assert events[-1].data[ATTR_VALUE] == '75.0°F' async def test_thermostat_get_temperature_range(hass, hk_driver, cls): @@ -399,7 +404,7 @@ async def test_thermostat_get_temperature_range(hass, hk_driver, cls): hass.states.async_set(entity_id, STATE_OFF, {ATTR_MIN_TEMP: 60, ATTR_MAX_TEMP: 70}) await hass.async_block_till_done() - assert acc.get_temperature_range() == (15.6, 21.1) + assert acc.get_temperature_range() == (15.5, 21.0) async def test_water_heater(hass, hk_driver, cls, events): @@ -425,6 +430,7 @@ async def test_water_heater(hass, hk_driver, cls, events): DEFAULT_MAX_TEMP_WATER_HEATER assert acc.char_target_temp.properties[PROP_MIN_VALUE] == \ DEFAULT_MIN_TEMP_WATER_HEATER + assert acc.char_target_temp.properties[PROP_MIN_STEP] == 0.5 hass.states.async_set(entity_id, STATE_HEAT, {ATTR_OPERATION_MODE: STATE_HEAT, @@ -508,7 +514,7 @@ async def test_water_heater_get_temperature_range(hass, hk_driver, cls): hass.states.async_set(entity_id, STATE_HEAT) await hass.async_block_till_done() - acc = cls.thermostat(hass, hk_driver, 'Climate', entity_id, 2, None) + acc = cls.thermostat(hass, hk_driver, 'WaterHeater', entity_id, 2, None) hass.states.async_set(entity_id, STATE_HEAT, {ATTR_MIN_TEMP: 20, ATTR_MAX_TEMP: 25}) @@ -519,4 +525,4 @@ async def test_water_heater_get_temperature_range(hass, hk_driver, cls): hass.states.async_set(entity_id, STATE_OFF, {ATTR_MIN_TEMP: 60, ATTR_MAX_TEMP: 70}) await hass.async_block_till_done() - assert acc.get_temperature_range() == (15.6, 21.1) + assert acc.get_temperature_range() == (15.5, 21.0) diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index 0368dfa642e..a2849a77396 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -99,13 +99,13 @@ def test_convert_to_float(): def test_temperature_to_homekit(): """Test temperature conversion from HA to HomeKit.""" assert temperature_to_homekit(20.46, TEMP_CELSIUS) == 20.5 - assert temperature_to_homekit(92.1, TEMP_FAHRENHEIT) == 33.4 + assert temperature_to_homekit(92.1, TEMP_FAHRENHEIT) == 33.5 def test_temperature_to_states(): """Test temperature conversion from HomeKit to HA.""" assert temperature_to_states(20, TEMP_CELSIUS) == 20.0 - assert temperature_to_states(20.2, TEMP_FAHRENHEIT) == 68.4 + assert temperature_to_states(20.2, TEMP_FAHRENHEIT) == 68.5 def test_density_to_air_quality():