Skip to content
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
1 change: 1 addition & 0 deletions homeassistant/components/homekit/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
# #### Properties ####
PROP_MAX_VALUE = 'maxValue'
PROP_MIN_VALUE = 'minValue'
PROP_MIN_STEP = 'minStep'
PROP_CELSIUS = {'minValue': -273, 'maxValue': 999}

# #### Device Classes ####
Expand Down
26 changes: 17 additions & 9 deletions homeassistant/components/homekit/type_thermostats.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
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__)
Expand Down Expand Up @@ -83,7 +83,8 @@ def __init__(self, *args):
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
Expand All @@ -97,13 +98,15 @@ def __init__(self, *args):
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):
Expand All @@ -112,11 +115,13 @@ def get_temperature_range(self):
.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

Expand All @@ -140,7 +145,7 @@ def set_heat_cool(self, value):
@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
Expand All @@ -156,7 +161,7 @@ def set_cooling_threshold(self, value):
@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
Expand All @@ -172,7 +177,7 @@ def set_heating_threshold(self, value):
@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)
Expand Down Expand Up @@ -301,7 +306,8 @@ def __init__(self, *args):
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(
Expand All @@ -313,11 +319,13 @@ def get_temperature_range(self):
.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

Expand All @@ -332,7 +340,7 @@ def set_heat_cool(self, value):
@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)
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/homekit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
34 changes: 20 additions & 14 deletions tests/components/homekit/test_type_thermostats.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
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 (
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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,
Expand Down Expand Up @@ -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})
Expand All @@ -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)
4 changes: 2 additions & 2 deletions tests/components/homekit/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down