Merge pull request #1573 from MartinHjelmare/service-warnings

Log error for servicecall without required data
pull/1579/head
Martin Hjelmare 2016-03-19 08:45:40 +01:00
commit 0bf13dbf26
14 changed files with 489 additions and 90 deletions

View File

@ -19,6 +19,8 @@ from homeassistant.const import (
SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK)
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'media_player'
SCAN_INTERVAL = 10
@ -230,11 +232,9 @@ def setup(hass, config):
def media_player_service_handler(service):
"""Map services to methods on MediaPlayerDevice."""
target_players = component.extract_from_service(service)
method = SERVICE_TO_METHOD[service.service]
for player in target_players:
for player in component.extract_from_service(service):
getattr(player, method)()
if player.should_poll:
@ -246,14 +246,15 @@ def setup(hass, config):
def volume_set_service(service):
"""Set specified volume on the media player."""
target_players = component.extract_from_service(service)
volume = service.data.get(ATTR_MEDIA_VOLUME_LEVEL)
if ATTR_MEDIA_VOLUME_LEVEL not in service.data:
if volume is None:
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, ATTR_MEDIA_VOLUME_LEVEL)
return
volume = service.data[ATTR_MEDIA_VOLUME_LEVEL]
for player in target_players:
for player in component.extract_from_service(service):
player.set_volume_level(volume)
if player.should_poll:
@ -264,14 +265,15 @@ def setup(hass, config):
def volume_mute_service(service):
"""Mute (true) or unmute (false) the media player."""
target_players = component.extract_from_service(service)
mute = service.data.get(ATTR_MEDIA_VOLUME_MUTED)
if ATTR_MEDIA_VOLUME_MUTED not in service.data:
if mute is None:
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, ATTR_MEDIA_VOLUME_MUTED)
return
mute = service.data[ATTR_MEDIA_VOLUME_MUTED]
for player in target_players:
for player in component.extract_from_service(service):
player.mute_volume(mute)
if player.should_poll:
@ -282,14 +284,15 @@ def setup(hass, config):
def media_seek_service(service):
"""Seek to a position."""
target_players = component.extract_from_service(service)
position = service.data.get(ATTR_MEDIA_SEEK_POSITION)
if ATTR_MEDIA_SEEK_POSITION not in service.data:
if position is None:
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, ATTR_MEDIA_SEEK_POSITION)
return
position = service.data[ATTR_MEDIA_SEEK_POSITION]
for player in target_players:
for player in component.extract_from_service(service):
player.media_seek(position)
if player.should_poll:
@ -303,10 +306,12 @@ def setup(hass, config):
media_type = service.data.get(ATTR_MEDIA_CONTENT_TYPE)
media_id = service.data.get(ATTR_MEDIA_CONTENT_ID)
if media_type is None:
return
if media_id is None:
if media_type is None or media_id is None:
missing_attr = (ATTR_MEDIA_CONTENT_TYPE if media_type is None
else ATTR_MEDIA_CONTENT_ID)
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, missing_attr)
return
for player in component.extract_from_service(service):

View File

@ -239,7 +239,7 @@ class DemoMusicPlayer(AbstractDemoPlayer):
if self._cur_track > 0:
support |= SUPPORT_PREVIOUS_TRACK
if self._cur_track < len(self.tracks)-1:
if self._cur_track < len(self.tracks) - 1:
support |= SUPPORT_NEXT_TRACK
return support
@ -252,7 +252,7 @@ class DemoMusicPlayer(AbstractDemoPlayer):
def media_next_track(self):
"""Send next track command."""
if self._cur_track < len(self.tracks)-1:
if self._cur_track < len(self.tracks) - 1:
self._cur_track += 1
self.update_ha_state()

View File

@ -71,6 +71,9 @@ def setup(hass, config):
message = call.data.get(ATTR_MESSAGE)
if message is None:
_LOGGER.error(
'Received call to %s without attribute %s',
call.service, ATTR_MESSAGE)
return
title = template.render(

View File

@ -45,7 +45,7 @@ class FileNotificationService(BaseNotificationService):
title = '{} notifications (Log started: {})\n{}\n'.format(
kwargs.get(ATTR_TITLE),
dt_util.strip_microseconds(dt_util.utcnow()),
'-'*80)
'-' * 80)
file.write(title)
if self.add_timestamp == 1:

View File

@ -123,6 +123,9 @@ def setup(hass, config):
service.data.get(ATTR_TEMPERATURE), float)
if temperature is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_TEMPERATURE, ATTR_TEMPERATURE)
return
for thermostat in target_thermostats:

View File

@ -11,8 +11,8 @@ from homeassistant.const import TEMP_CELCIUS, TEMP_FAHRENHEIT
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo thermostats."""
add_devices([
DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19),
DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77),
DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19, False),
DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77, True),
])
@ -21,13 +21,14 @@ class DemoThermostat(ThermostatDevice):
"""Representation of a demo thermostat."""
def __init__(self, name, target_temperature, unit_of_measurement,
away, current_temperature):
away, current_temperature, is_fan_on):
"""Initialize the thermostat."""
self._name = name
self._target_temperature = target_temperature
self._unit_of_measurement = unit_of_measurement
self._away = away
self._current_temperature = current_temperature
self._is_fan_on = is_fan_on
@property
def should_poll(self):
@ -59,6 +60,11 @@ class DemoThermostat(ThermostatDevice):
"""Return if away mode is on."""
return self._away
@property
def is_fan_on(self):
"""Return true if the fan is on."""
return self._is_fan_on
def set_temperature(self, temperature):
"""Set new target temperature."""
self._target_temperature = temperature
@ -70,3 +76,11 @@ class DemoThermostat(ThermostatDevice):
def turn_away_mode_off(self):
"""Turn away mode off."""
self._away = False
def turn_fan_on(self):
"""Turn fan on."""
self._is_fan_on = True
def turn_fan_off(self):
"""Turn fan off."""
self._is_fan_on = False

View File

@ -25,6 +25,11 @@ class TestDemoMediaPlayer(unittest.TestCase):
state = self.hass.states.get(entity_id)
assert 1.0 == state.attributes.get('volume_level')
mp.set_volume_level(self.hass, None, entity_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id)
assert 1.0 == state.attributes.get('volume_level')
mp.set_volume_level(self.hass, 0.5, entity_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id)
@ -41,6 +46,12 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert 0.5 == state.attributes.get('volume_level')
assert False is state.attributes.get('is_volume_muted')
mp.mute_volume(self.hass, None, entity_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id)
assert False is state.attributes.get('is_volume_muted')
mp.mute_volume(self.hass, True, entity_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id)
@ -87,7 +98,7 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert self.hass.states.is_state(entity_id, 'playing')
def test_prev_next_track(self):
"""Test media_next_track and media_prevoius_track ."""
"""Test media_next_track and media_previous_track ."""
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
state = self.hass.states.get(entity_id)
assert 1 == state.attributes.get('media_track')
@ -115,6 +126,27 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands'))
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
ent_id = 'media_player.lounge_room'
state = self.hass.states.get(ent_id)
assert 1 == state.attributes.get('media_episode')
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands'))
mp.media_next_track(self.hass, ent_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id)
assert 2 == state.attributes.get('media_episode')
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands'))
mp.media_previous_track(self.hass, ent_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id)
assert 1 == state.attributes.get('media_episode')
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands'))
@patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.'
'media_seek')
def test_play_media(self, mock_seek):
@ -126,6 +158,13 @@ class TestDemoMediaPlayer(unittest.TestCase):
state.attributes.get('supported_media_commands'))
assert state.attributes.get('media_content_id') is not None
mp.play_media(self.hass, None, 'some_id', ent_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id)
assert 0 < (mp.SUPPORT_PLAY_MEDIA &
state.attributes.get('supported_media_commands'))
assert not 'some_id' == state.attributes.get('media_content_id')
mp.play_media(self.hass, 'youtube', 'some_id', ent_id)
self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id)
@ -133,6 +172,9 @@ class TestDemoMediaPlayer(unittest.TestCase):
state.attributes.get('supported_media_commands'))
assert 'some_id' == state.attributes.get('media_content_id')
assert not mock_seek.called
mp.media_seek(self.hass, None, ent_id)
self.hass.pool.block_till_done()
assert not mock_seek.called
mp.media_seek(self.hass, 100, ent_id)
self.hass.pool.block_till_done()

View File

@ -25,8 +25,37 @@ class MockMediaPlayer(media_player.MediaPlayerDevice):
self._media_title = None
self._supported_media_commands = 0
self.turn_off_service_calls = mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TURN_OFF)
self.service_calls = {
'turn_on': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TURN_ON),
'turn_off': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_TURN_OFF),
'mute_volume': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_MUTE),
'set_volume_level': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_SET),
'media_play': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PLAY),
'media_pause': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PAUSE),
'media_previous_track': mock_service(
hass, media_player.DOMAIN,
media_player.SERVICE_MEDIA_PREVIOUS_TRACK),
'media_next_track': mock_service(
hass, media_player.DOMAIN,
media_player.SERVICE_MEDIA_NEXT_TRACK),
'media_seek': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_SEEK),
'play_media': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_PLAY_MEDIA),
'volume_up': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_UP),
'volume_down': mock_service(
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_DOWN),
'media_play_pause': mock_service(
hass, media_player.DOMAIN,
media_player.SERVICE_MEDIA_PLAY_PAUSE),
}
@property
def name(self):
@ -97,23 +126,26 @@ class TestMediaPlayer(unittest.TestCase):
self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format('state')
self.hass.states.set(self.mock_state_switch_id, STATE_OFF)
self.config_children_only = \
{'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')]}
self.config_children_and_attr = \
{'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')],
'attributes': {
'is_volume_muted': self.mock_mute_switch_id,
'state': self.mock_state_switch_id}}
self.config_children_only = {
'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')]
}
self.config_children_and_attr = {
'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')],
'attributes': {
'is_volume_muted': self.mock_mute_switch_id,
'state': self.mock_state_switch_id
}
}
def tearDown(self): # pylint: disable=invalid-name
"""Stop everything that was started."""
self.hass.stop()
def test_check_config_children_only(self):
def test_config_children_only(self):
"""Check config with only children."""
config_start = copy(self.config_children_only)
del config_start['platform']
@ -125,7 +157,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual(config_start, self.config_children_only)
def test_check_config_children_and_attr(self):
def test_config_children_and_attr(self):
"""Check config with children and attributes."""
config_start = copy(self.config_children_and_attr)
del config_start['platform']
@ -136,13 +168,13 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual(config_start, self.config_children_and_attr)
def test_check_config_no_name(self):
def test_config_no_name(self):
"""Check config with no Name entry."""
response = universal.validate_config({'platform': 'universal'})
self.assertFalse(response)
def test_check_config_bad_children(self):
def test_config_bad_children(self):
"""Check config with bad children entry."""
config_no_children = {'name': 'test', 'platform': 'universal'}
config_bad_children = {'name': 'test', 'children': {},
@ -156,7 +188,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual([], config_bad_children['children'])
def test_check_config_bad_commands(self):
def test_config_bad_commands(self):
"""Check config with bad commands entry."""
config = {'name': 'test', 'commands': [], 'platform': 'universal'}
@ -164,7 +196,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual({}, config['commands'])
def test_check_config_bad_attributes(self):
def test_config_bad_attributes(self):
"""Check config with bad attributes."""
config = {'name': 'test', 'attributes': [], 'platform': 'universal'}
@ -172,7 +204,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response)
self.assertEqual({}, config['attributes'])
def test_check_config_bad_key(self):
def test_config_bad_key(self):
"""Check config with bad key."""
config = {'name': 'test', 'asdf': 5, 'platform': 'universal'}
@ -183,6 +215,7 @@ class TestMediaPlayer(unittest.TestCase):
def test_platform_setup(self):
"""Test platform setup."""
config = {'name': 'test', 'platform': 'universal'}
bad_config = {'platform': 'universal'}
entities = []
def add_devices(new_entities):
@ -190,8 +223,10 @@ class TestMediaPlayer(unittest.TestCase):
for dev in new_entities:
entities.append(dev)
universal.setup_platform(self.hass, config, add_devices)
universal.setup_platform(self.hass, bad_config, add_devices)
self.assertEqual(0, len(entities))
universal.setup_platform(self.hass, config, add_devices)
self.assertEqual(1, len(entities))
self.assertEqual('test', entities[0].name)
@ -263,6 +298,15 @@ class TestMediaPlayer(unittest.TestCase):
self.assertEqual(config['name'], ump.name)
def test_polling(self):
"""Test should_poll property."""
config = self.config_children_only
universal.validate_config(config)
ump = universal.UniversalMediaPlayer(self.hass, **config)
self.assertEqual(False, ump.should_poll)
def test_state_children_only(self):
"""Test media player state with only children."""
config = self.config_children_only
@ -388,8 +432,7 @@ class TestMediaPlayer(unittest.TestCase):
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name'])
ump.update()
self.mock_mp_1._supported_media_commands = \
universal.SUPPORT_VOLUME_SET
self.mock_mp_1._supported_media_commands = universal.SUPPORT_VOLUME_SET
self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.update_ha_state()
ump.update()
@ -400,7 +443,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertEqual(check_flags, ump.supported_media_commands)
def test_service_call_to_child(self):
"""Test a service call that should be routed to a child."""
"""Test service calls that should be routed to a child."""
config = self.config_children_only
universal.validate_config(config)
@ -413,13 +456,53 @@ class TestMediaPlayer(unittest.TestCase):
ump.update()
ump.turn_off()
self.assertEqual(1, len(self.mock_mp_2.turn_off_service_calls))
self.assertEqual(1, len(self.mock_mp_2.service_calls['turn_off']))
ump.turn_on()
self.assertEqual(1, len(self.mock_mp_2.service_calls['turn_on']))
ump.mute_volume(True)
self.assertEqual(1, len(self.mock_mp_2.service_calls['mute_volume']))
ump.set_volume_level(0.5)
self.assertEqual(
1, len(self.mock_mp_2.service_calls['set_volume_level']))
ump.media_play()
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_play']))
ump.media_pause()
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_pause']))
ump.media_previous_track()
self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_previous_track']))
ump.media_next_track()
self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_next_track']))
ump.media_seek(100)
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_seek']))
ump.play_media('movie', 'batman')
self.assertEqual(1, len(self.mock_mp_2.service_calls['play_media']))
ump.volume_up()
self.assertEqual(1, len(self.mock_mp_2.service_calls['volume_up']))
ump.volume_down()
self.assertEqual(1, len(self.mock_mp_2.service_calls['volume_down']))
ump.media_play_pause()
self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_play_pause']))
def test_service_call_to_command(self):
"""Test service call to command."""
config = self.config_children_only
config['commands'] = \
{'turn_off': {'service': 'test.turn_off', 'data': {}}}
config['commands'] = {'turn_off': {
'service': 'test.turn_off', 'data': {}}}
universal.validate_config(config)
service = mock_service(self.hass, 'test', 'turn_off')

View File

@ -3,8 +3,10 @@ import os
import tempfile
import unittest
from homeassistant import core
import homeassistant.components.notify as notify
from tests.common import get_test_home_assistant
from unittest.mock import patch
@ -13,12 +15,27 @@ class TestCommandLine(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = core.HomeAssistant()
self.hass = get_test_home_assistant()
def tearDown(self): # pylint: disable=invalid-name
"""Stop down everything that was started."""
self.hass.stop()
def test_bad_config(self):
"""Test set up the platform with bad/missing config."""
self.assertFalse(notify.setup(self.hass, {
'notify': {
'name': 'test',
'platform': 'bad_platform',
}
}))
self.assertFalse(notify.setup(self.hass, {
'notify': {
'name': 'test',
'platform': 'command_line',
}
}))
def test_command_line_output(self):
"""Test the command line output."""
with tempfile.TemporaryDirectory() as tempdirname:
@ -41,7 +58,7 @@ class TestCommandLine(unittest.TestCase):
@patch('homeassistant.components.notify.command_line._LOGGER.error')
def test_error_for_none_zero_exit_code(self, mock_error):
"""Test if an error if logged for non zero exit codes."""
"""Test if an error is logged for non zero exit codes."""
self.assertTrue(notify.setup(self.hass, {
'notify': {
'name': 'test',

View File

@ -30,6 +30,12 @@ class TestNotifyDemo(unittest.TestCase):
""""Stop down everything that was started."""
self.hass.stop()
def test_sending_none_message(self):
"""Test send with None as message."""
notify.send_message(self.hass, None)
self.hass.pool.block_till_done()
self.assertTrue(len(self.events) == 0)
def test_sending_templated_message(self):
"""Send a templated message."""
self.hass.states.set('sensor.temperature', 10)

View File

@ -0,0 +1,56 @@
"""The tests for the notify file platform."""
import os
import unittest
import tempfile
import homeassistant.components.notify as notify
from homeassistant.components.notify import (
ATTR_TITLE_DEFAULT)
import homeassistant.util.dt as dt_util
from tests.common import get_test_home_assistant
class TestNotifyFile(unittest.TestCase):
"""Test the file notify."""
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
def tearDown(self): # pylint: disable=invalid-name
""""Stop down everything that was started."""
self.hass.stop()
def test_bad_config(self):
"""Test set up the platform with bad/missing config."""
self.assertFalse(notify.setup(self.hass, {
'notify': {
'name': 'test',
'platform': 'file',
}
}))
def test_notify_file(self):
"""Test the notify file output."""
with tempfile.TemporaryDirectory() as tempdirname:
filename = os.path.join(tempdirname, 'notify.txt')
message = 'one, two, testing, testing'
self.assertTrue(notify.setup(self.hass, {
'notify': {
'name': 'test',
'platform': 'file',
'filename': filename,
'timestamp': 0
}
}))
title = '{} notifications (Log started: {})\n{}\n'.format(
ATTR_TITLE_DEFAULT,
dt_util.strip_microseconds(dt_util.utcnow()),
'-' * 80)
self.hass.services.call('notify', 'test', {'message': message},
blocking=True)
result = open(filename).read()
self.assertEqual(result, "{}{}\n".format(title, message))

View File

@ -0,0 +1,101 @@
"""The tests for the demo thermostat."""
import unittest
from homeassistant.const import (
TEMP_CELCIUS,
)
from homeassistant.components import thermostat
from tests.common import get_test_home_assistant
ENTITY_NEST = 'thermostat.nest'
class TestDemoThermostat(unittest.TestCase):
"""Test the Heat Control thermostat."""
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
self.hass.config.temperature_unit = TEMP_CELCIUS
self.assertTrue(thermostat.setup(self.hass, {'thermostat': {
'platform': 'demo',
}}))
def tearDown(self): # pylint: disable=invalid-name
"""Stop down everything that was started."""
self.hass.stop()
def test_setup_params(self):
"""Test the inititial parameters."""
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual(21, state.attributes.get('temperature'))
self.assertEqual('off', state.attributes.get('away_mode'))
self.assertEqual(19, state.attributes.get('current_temperature'))
self.assertEqual('off', state.attributes.get('fan'))
def test_default_setup_params(self):
"""Test the setup with default parameters."""
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual(7, state.attributes.get('min_temp'))
self.assertEqual(35, state.attributes.get('max_temp'))
def test_set_target_temp_bad_attr(self):
"""Test setting the target temperature without required attribute."""
self.assertEqual('21', self.hass.states.get(ENTITY_NEST).state)
thermostat.set_temperature(self.hass, None, ENTITY_NEST)
self.hass.pool.block_till_done()
self.assertEqual('21', self.hass.states.get(ENTITY_NEST).state)
def test_set_target_temp(self):
"""Test the setting of the target temperature."""
thermostat.set_temperature(self.hass, 30, ENTITY_NEST)
self.hass.pool.block_till_done()
self.assertEqual('30.0', self.hass.states.get(ENTITY_NEST).state)
def test_set_away_mode_bad_attr(self):
"""Test setting the away mode without required attribute."""
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual('off', state.attributes.get('away_mode'))
thermostat.set_away_mode(self.hass, None, ENTITY_NEST)
self.hass.pool.block_till_done()
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual('off', state.attributes.get('away_mode'))
def test_set_away_mode_on(self):
"""Test setting the away mode on/true."""
thermostat.set_away_mode(self.hass, True, ENTITY_NEST)
self.hass.pool.block_till_done()
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual('on', state.attributes.get('away_mode'))
def test_set_away_mode_off(self):
"""Test setting the away mode off/false."""
thermostat.set_away_mode(self.hass, False, ENTITY_NEST)
self.hass.pool.block_till_done()
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual('off', state.attributes.get('away_mode'))
def test_set_fan_mode_on_bad_attr(self):
"""Test setting the fan mode on/true without required attribute."""
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual('off', state.attributes.get('fan'))
thermostat.set_fan_mode(self.hass, None, ENTITY_NEST)
self.hass.pool.block_till_done()
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual('off', state.attributes.get('fan'))
def test_set_fan_mode_on(self):
"""Test setting the fan mode on/true."""
thermostat.set_fan_mode(self.hass, True, ENTITY_NEST)
self.hass.pool.block_till_done()
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual('on', state.attributes.get('fan'))
def test_set_fan_mode_off(self):
"""Test setting the fan mode off/false."""
thermostat.set_fan_mode(self.hass, False, ENTITY_NEST)
self.hass.pool.block_till_done()
state = self.hass.states.get(ENTITY_NEST)
self.assertEqual('off', state.attributes.get('fan'))

View File

@ -1,5 +1,6 @@
"""The tests for the heat control thermostat."""
import unittest
from unittest import mock
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
@ -10,16 +11,55 @@ from homeassistant.const import (
TEMP_CELCIUS,
)
from homeassistant.components import thermostat
import homeassistant.components.thermostat.heat_control as heat_control
from tests.common import get_test_home_assistant
entity = 'thermostat.test'
ent_sensor = 'sensor.test'
ent_switch = 'switch.test'
min_temp = 3.0
max_temp = 65.0
target_temp = 42.0
ENTITY = 'thermostat.test'
ENT_SENSOR = 'sensor.test'
ENT_SWITCH = 'switch.test'
MIN_TEMP = 3.0
MAX_TEMP = 65.0
TARGET_TEMP = 42.0
class TestSetupThermostatHeatControl(unittest.TestCase):
"""Test the Heat Control thermostat with custom config."""
def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
def tearDown(self): # pylint: disable=invalid-name
"""Stop down everything that was started."""
self.hass.stop()
def test_setup_missing_conf(self):
"""Test set up heat_control with missing config values."""
config = {
'name': 'test',
'target_sensor': ENT_SENSOR
}
add_devices = mock.MagicMock()
result = heat_control.setup_platform(self.hass, config, add_devices)
self.assertEqual(False, result)
def test_setup_with_sensor(self):
"""Test set up heat_control with sensor to trigger update at init."""
self.hass.states.set(ENT_SENSOR, 22.0, {
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELCIUS
})
thermostat.setup(self.hass, {'thermostat': {
'platform': 'heat_control',
'name': 'test',
'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR
}})
state = self.hass.states.get(ENTITY)
self.assertEqual(
TEMP_CELCIUS, state.attributes.get('unit_of_measurement'))
self.assertEqual(22.0, state.attributes.get('current_temperature'))
class TestThermostatHeatControl(unittest.TestCase):
@ -32,8 +72,8 @@ class TestThermostatHeatControl(unittest.TestCase):
thermostat.setup(self.hass, {'thermostat': {
'platform': 'heat_control',
'name': 'test',
'heater': ent_switch,
'target_sensor': ent_sensor
'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR
}})
def tearDown(self): # pylint: disable=invalid-name
@ -42,11 +82,11 @@ class TestThermostatHeatControl(unittest.TestCase):
def test_setup_defaults_to_unknown(self):
"""Test the setting of defaults to unknown."""
self.assertEqual('unknown', self.hass.states.get(entity).state)
self.assertEqual('unknown', self.hass.states.get(ENTITY).state)
def test_default_setup_params(self):
"""Test the setup with default parameters."""
state = self.hass.states.get(entity)
state = self.hass.states.get(ENTITY)
self.assertEqual(7, state.attributes.get('min_temp'))
self.assertEqual(35, state.attributes.get('max_temp'))
self.assertEqual(None, state.attributes.get('temperature'))
@ -56,25 +96,41 @@ class TestThermostatHeatControl(unittest.TestCase):
thermostat.setup(self.hass, {'thermostat': {
'platform': 'heat_control',
'name': 'test',
'heater': ent_switch,
'target_sensor': ent_sensor,
'min_temp': min_temp,
'max_temp': max_temp,
'target_temp': target_temp
'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR,
'min_temp': MIN_TEMP,
'max_temp': MAX_TEMP,
'target_temp': TARGET_TEMP
}})
state = self.hass.states.get(entity)
self.assertEqual(min_temp, state.attributes.get('min_temp'))
self.assertEqual(max_temp, state.attributes.get('max_temp'))
self.assertEqual(target_temp, state.attributes.get('temperature'))
self.assertEqual(str(target_temp), self.hass.states.get(entity).state)
state = self.hass.states.get(ENTITY)
self.assertEqual(MIN_TEMP, state.attributes.get('min_temp'))
self.assertEqual(MAX_TEMP, state.attributes.get('max_temp'))
self.assertEqual(TARGET_TEMP, state.attributes.get('temperature'))
self.assertEqual(str(TARGET_TEMP), self.hass.states.get(ENTITY).state)
def test_set_target_temp(self):
"""Test the setting of the target temperature."""
thermostat.set_temperature(self.hass, 30)
self.hass.pool.block_till_done()
self.assertEqual('30.0', self.hass.states.get(entity).state)
self.assertEqual('30.0', self.hass.states.get(ENTITY).state)
def test_set_target_temp_turns_on_heater(self):
def test_sensor_bad_unit(self):
"""Test sensor that have bad unit."""
self._setup_sensor(22.0, unit='bad_unit')
self.hass.pool.block_till_done()
state = self.hass.states.get(ENTITY)
self.assertEqual(None, state.attributes.get('unit_of_measurement'))
self.assertEqual(None, state.attributes.get('current_temperature'))
def test_sensor_bad_value(self):
"""Test sensor that have None as state."""
self._setup_sensor(None)
self.hass.pool.block_till_done()
state = self.hass.states.get(ENTITY)
self.assertEqual(None, state.attributes.get('unit_of_measurement'))
self.assertEqual(None, state.attributes.get('current_temperature'))
def test_set_target_temp_heater_on(self):
"""Test if target temperature turn heater on."""
self._setup_switch(False)
self._setup_sensor(25)
@ -85,9 +141,9 @@ class TestThermostatHeatControl(unittest.TestCase):
call = self.calls[0]
self.assertEqual('switch', call.domain)
self.assertEqual(SERVICE_TURN_ON, call.service)
self.assertEqual(ent_switch, call.data['entity_id'])
self.assertEqual(ENT_SWITCH, call.data['entity_id'])
def test_set_target_temp_turns_off_heater(self):
def test_set_target_temp_heater_off(self):
"""Test if target temperature turn heater off."""
self._setup_switch(True)
self._setup_sensor(30)
@ -98,9 +154,9 @@ class TestThermostatHeatControl(unittest.TestCase):
call = self.calls[0]
self.assertEqual('switch', call.domain)
self.assertEqual(SERVICE_TURN_OFF, call.service)
self.assertEqual(ent_switch, call.data['entity_id'])
self.assertEqual(ENT_SWITCH, call.data['entity_id'])
def test_set_temp_change_turns_on_heater(self):
def test_set_temp_change_heater_on(self):
"""Test if temperature change turn heater on."""
self._setup_switch(False)
thermostat.set_temperature(self.hass, 30)
@ -111,9 +167,9 @@ class TestThermostatHeatControl(unittest.TestCase):
call = self.calls[0]
self.assertEqual('switch', call.domain)
self.assertEqual(SERVICE_TURN_ON, call.service)
self.assertEqual(ent_switch, call.data['entity_id'])
self.assertEqual(ENT_SWITCH, call.data['entity_id'])
def test_temp_change_turns_off_heater(self):
def test_temp_change_heater_off(self):
"""Test if temperature change turn heater off."""
self._setup_switch(True)
thermostat.set_temperature(self.hass, 25)
@ -124,17 +180,17 @@ class TestThermostatHeatControl(unittest.TestCase):
call = self.calls[0]
self.assertEqual('switch', call.domain)
self.assertEqual(SERVICE_TURN_OFF, call.service)
self.assertEqual(ent_switch, call.data['entity_id'])
self.assertEqual(ENT_SWITCH, call.data['entity_id'])
def _setup_sensor(self, temp, unit=TEMP_CELCIUS):
"""Setup the test sensor."""
self.hass.states.set(ent_sensor, temp, {
self.hass.states.set(ENT_SENSOR, temp, {
ATTR_UNIT_OF_MEASUREMENT: unit
})
def _setup_switch(self, is_on):
"""Setup the test switch."""
self.hass.states.set(ent_switch, STATE_ON if is_on else STATE_OFF)
self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF)
self.calls = []
def log_call(call):

View File

@ -23,6 +23,15 @@ class TestHoneywell(unittest.TestCase):
CONF_PASSWORD: 'pass',
'region': 'us',
}
bad_pass_config = {
CONF_USERNAME: 'user',
'region': 'us',
}
bad_region_config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
'region': 'un',
}
hass = mock.MagicMock()
add_devices = mock.MagicMock()
@ -37,6 +46,10 @@ class TestHoneywell(unittest.TestCase):
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, bad_pass_config, add_devices)
self.assertFalse(result)
result = honeywell.setup_platform(hass, bad_region_config, add_devices)
self.assertFalse(result)
result = honeywell.setup_platform(hass, config, add_devices)
self.assertTrue(result)
mock_sc.assert_called_once_with('user', 'pass')