hass-core/tests/components/climate/test_honeywell.py
Dan Sarginson 365f21b209 Honeywell fixes and improvements (#8756)
* Honeywell fixes and improvements

Give the Honeywell device a state ('On', 'Off', etc) that
can be displayed to user and understood by other components.
Previously this was always 'Unknown'. Update also raises a
state_changed event when a new temperature is polled.

These two together fix an issue (#8688) where Honeywell
climate data couldn't be logged in InfluxDB.

* Roll back some changes

These were not necessary to achieve the result I wanted.

* Renamed RoundThermostat's 'device' member for greater clarity

Now called 'client'

* Improve and simplify discovering thermostat mode

Per code review, this is a rather neater way to discover the thermostat mode

* Update tests for compatibility with new component

The tests previously relied upon the update() method being
called in the constructor. This is no longer the case.

* Address formatting review comment

Parens not necessary

* This system mode is not certain to apply to domestic hot water

Moved the mode lookup to only happen on update of radiator devices,
since hot water devices seem to be treated differently and I can't test.
2017-08-01 16:18:14 +02:00

446 lines
17 KiB
Python

"""The test the Honeywell thermostat module."""
import socket
import unittest
from unittest import mock
import voluptuous as vol
import somecomfort
from homeassistant.const import (
CONF_USERNAME, CONF_PASSWORD, TEMP_CELSIUS, TEMP_FAHRENHEIT)
from homeassistant.components.climate import (
ATTR_FAN_MODE, ATTR_OPERATION_MODE, ATTR_FAN_LIST, ATTR_OPERATION_LIST)
import homeassistant.components.climate.honeywell as honeywell
class TestHoneywell(unittest.TestCase):
"""A test class for Honeywell themostats."""
@mock.patch('somecomfort.SomeComfort')
@mock.patch('homeassistant.components.climate.'
'honeywell.HoneywellUSThermostat')
def test_setup_us(self, mock_ht, mock_sc):
"""Test for the US setup."""
config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
honeywell.CONF_COOL_AWAY_TEMPERATURE: 18,
honeywell.CONF_HEAT_AWAY_TEMPERATURE: 28,
honeywell.CONF_REGION: 'us',
}
bad_pass_config = {
CONF_USERNAME: 'user',
honeywell.CONF_COOL_AWAY_TEMPERATURE: 18,
honeywell.CONF_HEAT_AWAY_TEMPERATURE: 28,
honeywell.CONF_REGION: 'us',
}
bad_region_config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
honeywell.CONF_COOL_AWAY_TEMPERATURE: 18,
honeywell.CONF_HEAT_AWAY_TEMPERATURE: 28,
honeywell.CONF_REGION: 'un',
}
with self.assertRaises(vol.Invalid):
honeywell.PLATFORM_SCHEMA(None)
with self.assertRaises(vol.Invalid):
honeywell.PLATFORM_SCHEMA({})
with self.assertRaises(vol.Invalid):
honeywell.PLATFORM_SCHEMA(bad_pass_config)
with self.assertRaises(vol.Invalid):
honeywell.PLATFORM_SCHEMA(bad_region_config)
hass = mock.MagicMock()
add_devices = mock.MagicMock()
locations = [
mock.MagicMock(),
mock.MagicMock(),
]
devices_1 = [mock.MagicMock()]
devices_2 = [mock.MagicMock(), mock.MagicMock]
mock_sc.return_value.locations_by_id.values.return_value = \
locations
locations[0].devices_by_id.values.return_value = devices_1
locations[1].devices_by_id.values.return_value = devices_2
result = honeywell.setup_platform(hass, config, add_devices)
self.assertTrue(result)
self.assertEqual(mock_sc.call_count, 1)
self.assertEqual(mock_sc.call_args, mock.call('user', 'pass'))
mock_ht.assert_has_calls([
mock.call(mock_sc.return_value, devices_1[0], 18, 28,
'user', 'pass'),
mock.call(mock_sc.return_value, devices_2[0], 18, 28,
'user', 'pass'),
mock.call(mock_sc.return_value, devices_2[1], 18, 28,
'user', 'pass'),
])
@mock.patch('somecomfort.SomeComfort')
def test_setup_us_failures(self, mock_sc):
"""Test the US setup."""
hass = mock.MagicMock()
add_devices = mock.MagicMock()
config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
honeywell.CONF_REGION: 'us',
}
mock_sc.side_effect = somecomfort.AuthError
result = honeywell.setup_platform(hass, config, add_devices)
self.assertFalse(result)
self.assertFalse(add_devices.called)
mock_sc.side_effect = somecomfort.SomeComfortError
result = honeywell.setup_platform(hass, config, add_devices)
self.assertFalse(result)
self.assertFalse(add_devices.called)
@mock.patch('somecomfort.SomeComfort')
@mock.patch('homeassistant.components.climate.'
'honeywell.HoneywellUSThermostat')
def _test_us_filtered_devices(self, mock_ht, mock_sc, loc=None, dev=None):
"""Test for US filtered thermostats."""
config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
honeywell.CONF_REGION: 'us',
'location': loc,
'thermostat': dev,
}
locations = {
1: mock.MagicMock(locationid=mock.sentinel.loc1,
devices_by_id={
11: mock.MagicMock(
deviceid=mock.sentinel.loc1dev1),
12: mock.MagicMock(
deviceid=mock.sentinel.loc1dev2),
}),
2: mock.MagicMock(locationid=mock.sentinel.loc2,
devices_by_id={
21: mock.MagicMock(
deviceid=mock.sentinel.loc2dev1),
}),
3: mock.MagicMock(locationid=mock.sentinel.loc3,
devices_by_id={
31: mock.MagicMock(
deviceid=mock.sentinel.loc3dev1),
}),
}
mock_sc.return_value = mock.MagicMock(locations_by_id=locations)
hass = mock.MagicMock()
add_devices = mock.MagicMock()
self.assertEqual(True,
honeywell.setup_platform(hass, config, add_devices))
return mock_ht.call_args_list, mock_sc
def test_us_filtered_thermostat_1(self):
"""Test for US filtered thermostats."""
result, client = self._test_us_filtered_devices(
dev=mock.sentinel.loc1dev1)
devices = [x[0][1].deviceid for x in result]
self.assertEqual([mock.sentinel.loc1dev1], devices)
def test_us_filtered_thermostat_2(self):
"""Test for US filtered location."""
result, client = self._test_us_filtered_devices(
dev=mock.sentinel.loc2dev1)
devices = [x[0][1].deviceid for x in result]
self.assertEqual([mock.sentinel.loc2dev1], devices)
def test_us_filtered_location_1(self):
"""Test for US filtered locations."""
result, client = self._test_us_filtered_devices(
loc=mock.sentinel.loc1)
devices = [x[0][1].deviceid for x in result]
self.assertEqual([mock.sentinel.loc1dev1,
mock.sentinel.loc1dev2], devices)
def test_us_filtered_location_2(self):
"""Test for US filtered locations."""
result, client = self._test_us_filtered_devices(
loc=mock.sentinel.loc2)
devices = [x[0][1].deviceid for x in result]
self.assertEqual([mock.sentinel.loc2dev1], devices)
@mock.patch('evohomeclient.EvohomeClient')
@mock.patch('homeassistant.components.climate.honeywell.'
'RoundThermostat')
def test_eu_setup_full_config(self, mock_round, mock_evo):
"""Test the EU setup with complete configuration."""
config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
honeywell.CONF_AWAY_TEMPERATURE: 20.0,
honeywell.CONF_REGION: 'eu',
}
mock_evo.return_value.temperatures.return_value = [
{'id': 'foo'}, {'id': 'bar'}]
hass = mock.MagicMock()
add_devices = mock.MagicMock()
self.assertTrue(honeywell.setup_platform(hass, config, add_devices))
self.assertEqual(mock_evo.call_count, 1)
self.assertEqual(mock_evo.call_args, mock.call('user', 'pass'))
self.assertEqual(mock_evo.return_value.temperatures.call_count, 1)
self.assertEqual(
mock_evo.return_value.temperatures.call_args,
mock.call(force_refresh=True)
)
mock_round.assert_has_calls([
mock.call(mock_evo.return_value, 'foo', True, 20.0),
mock.call(mock_evo.return_value, 'bar', False, 20.0),
])
self.assertEqual(2, add_devices.call_count)
@mock.patch('evohomeclient.EvohomeClient')
@mock.patch('homeassistant.components.climate.honeywell.'
'RoundThermostat')
def test_eu_setup_partial_config(self, mock_round, mock_evo):
"""Test the EU setup with partial configuration."""
config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
honeywell.CONF_REGION: 'eu',
}
mock_evo.return_value.temperatures.return_value = [
{'id': 'foo'}, {'id': 'bar'}]
config[honeywell.CONF_AWAY_TEMPERATURE] = \
honeywell.DEFAULT_AWAY_TEMPERATURE
hass = mock.MagicMock()
add_devices = mock.MagicMock()
self.assertTrue(honeywell.setup_platform(hass, config, add_devices))
mock_round.assert_has_calls([
mock.call(mock_evo.return_value, 'foo', True, 16),
mock.call(mock_evo.return_value, 'bar', False, 16),
])
@mock.patch('evohomeclient.EvohomeClient')
@mock.patch('homeassistant.components.climate.honeywell.'
'RoundThermostat')
def test_eu_setup_bad_temp(self, mock_round, mock_evo):
"""Test the EU setup with invalid temperature."""
config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
honeywell.CONF_AWAY_TEMPERATURE: 'ponies',
honeywell.CONF_REGION: 'eu',
}
with self.assertRaises(vol.Invalid):
honeywell.PLATFORM_SCHEMA(config)
@mock.patch('evohomeclient.EvohomeClient')
@mock.patch('homeassistant.components.climate.honeywell.'
'RoundThermostat')
def test_eu_setup_error(self, mock_round, mock_evo):
"""Test the EU setup with errors."""
config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
honeywell.CONF_AWAY_TEMPERATURE: 20,
honeywell.CONF_REGION: 'eu',
}
mock_evo.return_value.temperatures.side_effect = socket.error
add_devices = mock.MagicMock()
hass = mock.MagicMock()
self.assertFalse(honeywell.setup_platform(hass, config, add_devices))
class TestHoneywellRound(unittest.TestCase):
"""A test class for Honeywell Round thermostats."""
def setup_method(self, method):
"""Test the setup method."""
def fake_temperatures(force_refresh=None):
"""Create fake temperatures."""
temps = [
{'id': '1', 'temp': 20, 'setpoint': 21,
'thermostat': 'main', 'name': 'House'},
{'id': '2', 'temp': 21, 'setpoint': 22,
'thermostat': 'DOMESTIC_HOT_WATER'},
]
return temps
self.device = mock.MagicMock()
self.device.temperatures.side_effect = fake_temperatures
self.round1 = honeywell.RoundThermostat(self.device, '1',
True, 16)
self.round1.update()
self.round2 = honeywell.RoundThermostat(self.device, '2',
False, 17)
self.round2.update()
def test_attributes(self):
"""Test the attributes."""
self.assertEqual('House', self.round1.name)
self.assertEqual(TEMP_CELSIUS, self.round1.temperature_unit)
self.assertEqual(20, self.round1.current_temperature)
self.assertEqual(21, self.round1.target_temperature)
self.assertFalse(self.round1.is_away_mode_on)
self.assertEqual('Hot Water', self.round2.name)
self.assertEqual(TEMP_CELSIUS, self.round2.temperature_unit)
self.assertEqual(21, self.round2.current_temperature)
self.assertEqual(None, self.round2.target_temperature)
self.assertFalse(self.round2.is_away_mode_on)
def test_away_mode(self):
"""Test setting the away mode."""
self.assertFalse(self.round1.is_away_mode_on)
self.round1.turn_away_mode_on()
self.assertTrue(self.round1.is_away_mode_on)
self.assertEqual(self.device.set_temperature.call_count, 1)
self.assertEqual(
self.device.set_temperature.call_args, mock.call('House', 16)
)
self.device.set_temperature.reset_mock()
self.round1.turn_away_mode_off()
self.assertFalse(self.round1.is_away_mode_on)
self.assertEqual(self.device.cancel_temp_override.call_count, 1)
self.assertEqual(
self.device.cancel_temp_override.call_args, mock.call('House')
)
def test_set_temperature(self):
"""Test setting the temperature."""
self.round1.set_temperature(temperature=25)
self.assertEqual(self.device.set_temperature.call_count, 1)
self.assertEqual(
self.device.set_temperature.call_args, mock.call('House', 25)
)
def test_set_operation_mode(self: unittest.TestCase) -> None:
"""Test setting the system operation."""
self.round1.set_operation_mode('cool')
self.assertEqual('cool', self.round1.current_operation)
self.assertEqual('cool', self.device.system_mode)
self.round1.set_operation_mode('heat')
self.assertEqual('heat', self.round1.current_operation)
self.assertEqual('heat', self.device.system_mode)
class TestHoneywellUS(unittest.TestCase):
"""A test class for Honeywell US thermostats."""
def setup_method(self, method):
"""Test the setup method."""
self.client = mock.MagicMock()
self.device = mock.MagicMock()
self.cool_away_temp = 18
self.heat_away_temp = 28
self.honeywell = honeywell.HoneywellUSThermostat(
self.client, self.device,
self.cool_away_temp, self.heat_away_temp,
'user', 'password')
self.device.fan_running = True
self.device.name = 'test'
self.device.temperature_unit = 'F'
self.device.current_temperature = 72
self.device.setpoint_cool = 78
self.device.setpoint_heat = 65
self.device.system_mode = 'heat'
self.device.fan_mode = 'auto'
def test_properties(self):
"""Test the properties."""
self.assertTrue(self.honeywell.is_fan_on)
self.assertEqual('test', self.honeywell.name)
self.assertEqual(72, self.honeywell.current_temperature)
def test_unit_of_measurement(self):
"""Test the unit of measurement."""
self.assertEqual(TEMP_FAHRENHEIT, self.honeywell.temperature_unit)
self.device.temperature_unit = 'C'
self.assertEqual(TEMP_CELSIUS, self.honeywell.temperature_unit)
def test_target_temp(self):
"""Test the target temperature."""
self.assertEqual(65, self.honeywell.target_temperature)
self.device.system_mode = 'cool'
self.assertEqual(78, self.honeywell.target_temperature)
def test_set_temp(self):
"""Test setting the temperature."""
self.honeywell.set_temperature(temperature=70)
self.assertEqual(70, self.device.setpoint_heat)
self.assertEqual(70, self.honeywell.target_temperature)
self.device.system_mode = 'cool'
self.assertEqual(78, self.honeywell.target_temperature)
self.honeywell.set_temperature(temperature=74)
self.assertEqual(74, self.device.setpoint_cool)
self.assertEqual(74, self.honeywell.target_temperature)
def test_set_operation_mode(self: unittest.TestCase) -> None:
"""Test setting the operation mode."""
self.honeywell.set_operation_mode('cool')
self.assertEqual('cool', self.device.system_mode)
self.honeywell.set_operation_mode('heat')
self.assertEqual('heat', self.device.system_mode)
def test_set_temp_fail(self):
"""Test if setting the temperature fails."""
self.device.setpoint_heat = mock.MagicMock(
side_effect=somecomfort.SomeComfortError)
self.honeywell.set_temperature(temperature=123)
def test_attributes(self):
"""Test the attributes."""
expected = {
honeywell.ATTR_FAN: 'running',
ATTR_FAN_MODE: 'auto',
ATTR_OPERATION_MODE: 'heat',
ATTR_FAN_LIST: somecomfort.FAN_MODES,
ATTR_OPERATION_LIST: somecomfort.SYSTEM_MODES,
}
self.assertEqual(expected, self.honeywell.device_state_attributes)
expected['fan'] = 'idle'
self.device.fan_running = False
self.assertEqual(expected, self.honeywell.device_state_attributes)
def test_with_no_fan(self):
"""Test if there is on fan."""
self.device.fan_running = False
self.device.fan_mode = None
expected = {
honeywell.ATTR_FAN: 'idle',
ATTR_FAN_MODE: None,
ATTR_OPERATION_MODE: 'heat',
ATTR_FAN_LIST: somecomfort.FAN_MODES,
ATTR_OPERATION_LIST: somecomfort.SYSTEM_MODES,
}
self.assertEqual(expected, self.honeywell.device_state_attributes)
def test_heat_away_mode(self):
"""Test setting the heat away mode."""
self.honeywell.set_operation_mode('heat')
self.assertFalse(self.honeywell.is_away_mode_on)
self.honeywell.turn_away_mode_on()
self.assertTrue(self.honeywell.is_away_mode_on)
self.assertEqual(self.device.setpoint_heat, self.heat_away_temp)
self.assertEqual(self.device.hold_heat, True)
self.honeywell.turn_away_mode_off()
self.assertFalse(self.honeywell.is_away_mode_on)
self.assertEqual(self.device.hold_heat, False)
@mock.patch('somecomfort.SomeComfort')
def test_retry(self, test_somecomfort):
"""Test retry connection."""
old_device = self.honeywell._device
self.honeywell._retry()
self.assertEqual(self.honeywell._device, old_device)