Log error for servicecall without required data

* Log error for services called without required attributes, in
	media_player, notify and thermostat platforms.
* Add fan property and methods in thermostat demo component.
* Add tests for notify file and thermostat demo component.
* Increase coverage of tests for media_player, notify and thermostat
	platforms.
* Fix some PEP issues, but not all. Tests still have old linting
	errors.
This commit is contained in:
MartinHjelmare 2016-03-18 13:33:52 +01:00
parent 72fdd94b29
commit 4e4b24fcff
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_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK) SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK)
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'media_player' DOMAIN = 'media_player'
SCAN_INTERVAL = 10 SCAN_INTERVAL = 10
@ -230,11 +232,9 @@ def setup(hass, config):
def media_player_service_handler(service): def media_player_service_handler(service):
"""Map services to methods on MediaPlayerDevice.""" """Map services to methods on MediaPlayerDevice."""
target_players = component.extract_from_service(service)
method = SERVICE_TO_METHOD[service.service] method = SERVICE_TO_METHOD[service.service]
for player in target_players: for player in component.extract_from_service(service):
getattr(player, method)() getattr(player, method)()
if player.should_poll: if player.should_poll:
@ -246,14 +246,15 @@ def setup(hass, config):
def volume_set_service(service): def volume_set_service(service):
"""Set specified volume on the media player.""" """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 return
volume = service.data[ATTR_MEDIA_VOLUME_LEVEL] for player in component.extract_from_service(service):
for player in target_players:
player.set_volume_level(volume) player.set_volume_level(volume)
if player.should_poll: if player.should_poll:
@ -264,14 +265,15 @@ def setup(hass, config):
def volume_mute_service(service): def volume_mute_service(service):
"""Mute (true) or unmute (false) the media player.""" """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 return
mute = service.data[ATTR_MEDIA_VOLUME_MUTED] for player in component.extract_from_service(service):
for player in target_players:
player.mute_volume(mute) player.mute_volume(mute)
if player.should_poll: if player.should_poll:
@ -282,14 +284,15 @@ def setup(hass, config):
def media_seek_service(service): def media_seek_service(service):
"""Seek to a position.""" """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 return
position = service.data[ATTR_MEDIA_SEEK_POSITION] for player in component.extract_from_service(service):
for player in target_players:
player.media_seek(position) player.media_seek(position)
if player.should_poll: if player.should_poll:
@ -303,10 +306,12 @@ def setup(hass, config):
media_type = service.data.get(ATTR_MEDIA_CONTENT_TYPE) media_type = service.data.get(ATTR_MEDIA_CONTENT_TYPE)
media_id = service.data.get(ATTR_MEDIA_CONTENT_ID) media_id = service.data.get(ATTR_MEDIA_CONTENT_ID)
if media_type is None: if media_type is None or media_id is None:
return missing_attr = (ATTR_MEDIA_CONTENT_TYPE if media_type is None
else ATTR_MEDIA_CONTENT_ID)
if media_id is None: _LOGGER.error(
'Received call to %s without attribute %s',
service.service, missing_attr)
return return
for player in component.extract_from_service(service): for player in component.extract_from_service(service):

View File

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

View File

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

View File

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

View File

@ -123,6 +123,9 @@ def setup(hass, config):
service.data.get(ATTR_TEMPERATURE), float) service.data.get(ATTR_TEMPERATURE), float)
if temperature is None: if temperature is None:
_LOGGER.error(
"Received call to %s without attribute %s",
SERVICE_SET_TEMPERATURE, ATTR_TEMPERATURE)
return return
for thermostat in target_thermostats: 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): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo thermostats.""" """Setup the Demo thermostats."""
add_devices([ add_devices([
DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19), DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19, False),
DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77), DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77, True),
]) ])
@ -21,13 +21,14 @@ class DemoThermostat(ThermostatDevice):
"""Representation of a demo thermostat.""" """Representation of a demo thermostat."""
def __init__(self, name, target_temperature, unit_of_measurement, def __init__(self, name, target_temperature, unit_of_measurement,
away, current_temperature): away, current_temperature, is_fan_on):
"""Initialize the thermostat.""" """Initialize the thermostat."""
self._name = name self._name = name
self._target_temperature = target_temperature self._target_temperature = target_temperature
self._unit_of_measurement = unit_of_measurement self._unit_of_measurement = unit_of_measurement
self._away = away self._away = away
self._current_temperature = current_temperature self._current_temperature = current_temperature
self._is_fan_on = is_fan_on
@property @property
def should_poll(self): def should_poll(self):
@ -59,6 +60,11 @@ class DemoThermostat(ThermostatDevice):
"""Return if away mode is on.""" """Return if away mode is on."""
return self._away 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): def set_temperature(self, temperature):
"""Set new target temperature.""" """Set new target temperature."""
self._target_temperature = temperature self._target_temperature = temperature
@ -70,3 +76,11 @@ class DemoThermostat(ThermostatDevice):
def turn_away_mode_off(self): def turn_away_mode_off(self):
"""Turn away mode off.""" """Turn away mode off."""
self._away = False 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) state = self.hass.states.get(entity_id)
assert 1.0 == state.attributes.get('volume_level') 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) mp.set_volume_level(self.hass, 0.5, entity_id)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)
@ -41,6 +46,12 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert 0.5 == state.attributes.get('volume_level') assert 0.5 == state.attributes.get('volume_level')
assert False is state.attributes.get('is_volume_muted') 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) mp.mute_volume(self.hass, True, entity_id)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)
@ -87,7 +98,7 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert self.hass.states.is_state(entity_id, 'playing') assert self.hass.states.is_state(entity_id, 'playing')
def test_prev_next_track(self): 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'}}) assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)
assert 1 == state.attributes.get('media_track') assert 1 == state.attributes.get('media_track')
@ -115,6 +126,27 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK & assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) 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.' @patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.'
'media_seek') 'media_seek')
def test_play_media(self, mock_seek): def test_play_media(self, mock_seek):
@ -126,6 +158,13 @@ class TestDemoMediaPlayer(unittest.TestCase):
state.attributes.get('supported_media_commands')) state.attributes.get('supported_media_commands'))
assert state.attributes.get('media_content_id') is not None 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) mp.play_media(self.hass, 'youtube', 'some_id', ent_id)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
state = self.hass.states.get(ent_id) state = self.hass.states.get(ent_id)
@ -133,6 +172,9 @@ class TestDemoMediaPlayer(unittest.TestCase):
state.attributes.get('supported_media_commands')) state.attributes.get('supported_media_commands'))
assert 'some_id' == state.attributes.get('media_content_id') 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 assert not mock_seek.called
mp.media_seek(self.hass, 100, ent_id) mp.media_seek(self.hass, 100, ent_id)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()

View File

@ -25,8 +25,37 @@ class MockMediaPlayer(media_player.MediaPlayerDevice):
self._media_title = None self._media_title = None
self._supported_media_commands = 0 self._supported_media_commands = 0
self.turn_off_service_calls = mock_service( self.service_calls = {
hass, media_player.DOMAIN, media_player.SERVICE_TURN_OFF) '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 @property
def name(self): def name(self):
@ -97,23 +126,26 @@ class TestMediaPlayer(unittest.TestCase):
self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format('state') self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format('state')
self.hass.states.set(self.mock_state_switch_id, STATE_OFF) self.hass.states.set(self.mock_state_switch_id, STATE_OFF)
self.config_children_only = \ self.config_children_only = {
{'name': 'test', 'platform': 'universal', 'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'), 'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')]} media_player.ENTITY_ID_FORMAT.format('mock2')]
self.config_children_and_attr = \ }
{'name': 'test', 'platform': 'universal', self.config_children_and_attr = {
'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'), 'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
media_player.ENTITY_ID_FORMAT.format('mock2')], media_player.ENTITY_ID_FORMAT.format('mock2')],
'attributes': { 'attributes': {
'is_volume_muted': self.mock_mute_switch_id, 'is_volume_muted': self.mock_mute_switch_id,
'state': self.mock_state_switch_id}} 'state': self.mock_state_switch_id
}
}
def tearDown(self): # pylint: disable=invalid-name def tearDown(self): # pylint: disable=invalid-name
"""Stop everything that was started.""" """Stop everything that was started."""
self.hass.stop() self.hass.stop()
def test_check_config_children_only(self): def test_config_children_only(self):
"""Check config with only children.""" """Check config with only children."""
config_start = copy(self.config_children_only) config_start = copy(self.config_children_only)
del config_start['platform'] del config_start['platform']
@ -125,7 +157,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response) self.assertTrue(response)
self.assertEqual(config_start, self.config_children_only) 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.""" """Check config with children and attributes."""
config_start = copy(self.config_children_and_attr) config_start = copy(self.config_children_and_attr)
del config_start['platform'] del config_start['platform']
@ -136,13 +168,13 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response) self.assertTrue(response)
self.assertEqual(config_start, self.config_children_and_attr) 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.""" """Check config with no Name entry."""
response = universal.validate_config({'platform': 'universal'}) response = universal.validate_config({'platform': 'universal'})
self.assertFalse(response) self.assertFalse(response)
def test_check_config_bad_children(self): def test_config_bad_children(self):
"""Check config with bad children entry.""" """Check config with bad children entry."""
config_no_children = {'name': 'test', 'platform': 'universal'} config_no_children = {'name': 'test', 'platform': 'universal'}
config_bad_children = {'name': 'test', 'children': {}, config_bad_children = {'name': 'test', 'children': {},
@ -156,7 +188,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response) self.assertTrue(response)
self.assertEqual([], config_bad_children['children']) 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.""" """Check config with bad commands entry."""
config = {'name': 'test', 'commands': [], 'platform': 'universal'} config = {'name': 'test', 'commands': [], 'platform': 'universal'}
@ -164,7 +196,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response) self.assertTrue(response)
self.assertEqual({}, config['commands']) self.assertEqual({}, config['commands'])
def test_check_config_bad_attributes(self): def test_config_bad_attributes(self):
"""Check config with bad attributes.""" """Check config with bad attributes."""
config = {'name': 'test', 'attributes': [], 'platform': 'universal'} config = {'name': 'test', 'attributes': [], 'platform': 'universal'}
@ -172,7 +204,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertTrue(response) self.assertTrue(response)
self.assertEqual({}, config['attributes']) self.assertEqual({}, config['attributes'])
def test_check_config_bad_key(self): def test_config_bad_key(self):
"""Check config with bad key.""" """Check config with bad key."""
config = {'name': 'test', 'asdf': 5, 'platform': 'universal'} config = {'name': 'test', 'asdf': 5, 'platform': 'universal'}
@ -183,6 +215,7 @@ class TestMediaPlayer(unittest.TestCase):
def test_platform_setup(self): def test_platform_setup(self):
"""Test platform setup.""" """Test platform setup."""
config = {'name': 'test', 'platform': 'universal'} config = {'name': 'test', 'platform': 'universal'}
bad_config = {'platform': 'universal'}
entities = [] entities = []
def add_devices(new_entities): def add_devices(new_entities):
@ -190,8 +223,10 @@ class TestMediaPlayer(unittest.TestCase):
for dev in new_entities: for dev in new_entities:
entities.append(dev) 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(1, len(entities))
self.assertEqual('test', entities[0].name) self.assertEqual('test', entities[0].name)
@ -263,6 +298,15 @@ class TestMediaPlayer(unittest.TestCase):
self.assertEqual(config['name'], ump.name) 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): def test_state_children_only(self):
"""Test media player state with only children.""" """Test media player state with only children."""
config = self.config_children_only 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.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name'])
ump.update() ump.update()
self.mock_mp_1._supported_media_commands = \ self.mock_mp_1._supported_media_commands = universal.SUPPORT_VOLUME_SET
universal.SUPPORT_VOLUME_SET
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.update_ha_state() self.mock_mp_1.update_ha_state()
ump.update() ump.update()
@ -400,7 +443,7 @@ class TestMediaPlayer(unittest.TestCase):
self.assertEqual(check_flags, ump.supported_media_commands) self.assertEqual(check_flags, ump.supported_media_commands)
def test_service_call_to_child(self): 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 config = self.config_children_only
universal.validate_config(config) universal.validate_config(config)
@ -413,13 +456,53 @@ class TestMediaPlayer(unittest.TestCase):
ump.update() ump.update()
ump.turn_off() 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): def test_service_call_to_command(self):
"""Test service call to command.""" """Test service call to command."""
config = self.config_children_only config = self.config_children_only
config['commands'] = \ config['commands'] = {'turn_off': {
{'turn_off': {'service': 'test.turn_off', 'data': {}}} 'service': 'test.turn_off', 'data': {}}}
universal.validate_config(config) universal.validate_config(config)
service = mock_service(self.hass, 'test', 'turn_off') service = mock_service(self.hass, 'test', 'turn_off')

View File

@ -3,8 +3,10 @@ import os
import tempfile import tempfile
import unittest import unittest
from homeassistant import core
import homeassistant.components.notify as notify import homeassistant.components.notify as notify
from tests.common import get_test_home_assistant
from unittest.mock import patch from unittest.mock import patch
@ -13,12 +15,27 @@ class TestCommandLine(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """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 def tearDown(self): # pylint: disable=invalid-name
"""Stop down everything that was started.""" """Stop down everything that was started."""
self.hass.stop() 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): def test_command_line_output(self):
"""Test the command line output.""" """Test the command line output."""
with tempfile.TemporaryDirectory() as tempdirname: with tempfile.TemporaryDirectory() as tempdirname:
@ -41,7 +58,7 @@ class TestCommandLine(unittest.TestCase):
@patch('homeassistant.components.notify.command_line._LOGGER.error') @patch('homeassistant.components.notify.command_line._LOGGER.error')
def test_error_for_none_zero_exit_code(self, mock_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, { self.assertTrue(notify.setup(self.hass, {
'notify': { 'notify': {
'name': 'test', 'name': 'test',

View File

@ -30,6 +30,12 @@ class TestNotifyDemo(unittest.TestCase):
""""Stop down everything that was started.""" """"Stop down everything that was started."""
self.hass.stop() 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): def test_sending_templated_message(self):
"""Send a templated message.""" """Send a templated message."""
self.hass.states.set('sensor.temperature', 10) 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.""" """The tests for the heat control thermostat."""
import unittest import unittest
from unittest import mock
from homeassistant.const import ( from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT, ATTR_UNIT_OF_MEASUREMENT,
@ -10,16 +11,55 @@ from homeassistant.const import (
TEMP_CELCIUS, TEMP_CELCIUS,
) )
from homeassistant.components import thermostat from homeassistant.components import thermostat
import homeassistant.components.thermostat.heat_control as heat_control
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
entity = 'thermostat.test' ENTITY = 'thermostat.test'
ent_sensor = 'sensor.test' ENT_SENSOR = 'sensor.test'
ent_switch = 'switch.test' ENT_SWITCH = 'switch.test'
min_temp = 3.0 MIN_TEMP = 3.0
max_temp = 65.0 MAX_TEMP = 65.0
target_temp = 42.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): class TestThermostatHeatControl(unittest.TestCase):
@ -32,8 +72,8 @@ class TestThermostatHeatControl(unittest.TestCase):
thermostat.setup(self.hass, {'thermostat': { thermostat.setup(self.hass, {'thermostat': {
'platform': 'heat_control', 'platform': 'heat_control',
'name': 'test', 'name': 'test',
'heater': ent_switch, 'heater': ENT_SWITCH,
'target_sensor': ent_sensor 'target_sensor': ENT_SENSOR
}}) }})
def tearDown(self): # pylint: disable=invalid-name def tearDown(self): # pylint: disable=invalid-name
@ -42,11 +82,11 @@ class TestThermostatHeatControl(unittest.TestCase):
def test_setup_defaults_to_unknown(self): def test_setup_defaults_to_unknown(self):
"""Test the setting of defaults to unknown.""" """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): def test_default_setup_params(self):
"""Test the setup with default parameters.""" """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(7, state.attributes.get('min_temp'))
self.assertEqual(35, state.attributes.get('max_temp')) self.assertEqual(35, state.attributes.get('max_temp'))
self.assertEqual(None, state.attributes.get('temperature')) self.assertEqual(None, state.attributes.get('temperature'))
@ -56,25 +96,41 @@ class TestThermostatHeatControl(unittest.TestCase):
thermostat.setup(self.hass, {'thermostat': { thermostat.setup(self.hass, {'thermostat': {
'platform': 'heat_control', 'platform': 'heat_control',
'name': 'test', 'name': 'test',
'heater': ent_switch, 'heater': ENT_SWITCH,
'target_sensor': ent_sensor, 'target_sensor': ENT_SENSOR,
'min_temp': min_temp, 'min_temp': MIN_TEMP,
'max_temp': max_temp, 'max_temp': MAX_TEMP,
'target_temp': target_temp 'target_temp': TARGET_TEMP
}}) }})
state = self.hass.states.get(entity) state = self.hass.states.get(ENTITY)
self.assertEqual(min_temp, state.attributes.get('min_temp')) self.assertEqual(MIN_TEMP, state.attributes.get('min_temp'))
self.assertEqual(max_temp, state.attributes.get('max_temp')) self.assertEqual(MAX_TEMP, state.attributes.get('max_temp'))
self.assertEqual(target_temp, state.attributes.get('temperature')) self.assertEqual(TARGET_TEMP, state.attributes.get('temperature'))
self.assertEqual(str(target_temp), self.hass.states.get(entity).state) self.assertEqual(str(TARGET_TEMP), self.hass.states.get(ENTITY).state)
def test_set_target_temp(self): def test_set_target_temp(self):
"""Test the setting of the target temperature.""" """Test the setting of the target temperature."""
thermostat.set_temperature(self.hass, 30) thermostat.set_temperature(self.hass, 30)
self.hass.pool.block_till_done() 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.""" """Test if target temperature turn heater on."""
self._setup_switch(False) self._setup_switch(False)
self._setup_sensor(25) self._setup_sensor(25)
@ -85,9 +141,9 @@ class TestThermostatHeatControl(unittest.TestCase):
call = self.calls[0] call = self.calls[0]
self.assertEqual('switch', call.domain) self.assertEqual('switch', call.domain)
self.assertEqual(SERVICE_TURN_ON, call.service) 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.""" """Test if target temperature turn heater off."""
self._setup_switch(True) self._setup_switch(True)
self._setup_sensor(30) self._setup_sensor(30)
@ -98,9 +154,9 @@ class TestThermostatHeatControl(unittest.TestCase):
call = self.calls[0] call = self.calls[0]
self.assertEqual('switch', call.domain) self.assertEqual('switch', call.domain)
self.assertEqual(SERVICE_TURN_OFF, call.service) 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.""" """Test if temperature change turn heater on."""
self._setup_switch(False) self._setup_switch(False)
thermostat.set_temperature(self.hass, 30) thermostat.set_temperature(self.hass, 30)
@ -111,9 +167,9 @@ class TestThermostatHeatControl(unittest.TestCase):
call = self.calls[0] call = self.calls[0]
self.assertEqual('switch', call.domain) self.assertEqual('switch', call.domain)
self.assertEqual(SERVICE_TURN_ON, call.service) 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.""" """Test if temperature change turn heater off."""
self._setup_switch(True) self._setup_switch(True)
thermostat.set_temperature(self.hass, 25) thermostat.set_temperature(self.hass, 25)
@ -124,17 +180,17 @@ class TestThermostatHeatControl(unittest.TestCase):
call = self.calls[0] call = self.calls[0]
self.assertEqual('switch', call.domain) self.assertEqual('switch', call.domain)
self.assertEqual(SERVICE_TURN_OFF, call.service) 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): def _setup_sensor(self, temp, unit=TEMP_CELCIUS):
"""Setup the test sensor.""" """Setup the test sensor."""
self.hass.states.set(ent_sensor, temp, { self.hass.states.set(ENT_SENSOR, temp, {
ATTR_UNIT_OF_MEASUREMENT: unit ATTR_UNIT_OF_MEASUREMENT: unit
}) })
def _setup_switch(self, is_on): def _setup_switch(self, is_on):
"""Setup the test switch.""" """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 = [] self.calls = []
def log_call(call): def log_call(call):

View File

@ -23,6 +23,15 @@ class TestHoneywell(unittest.TestCase):
CONF_PASSWORD: 'pass', CONF_PASSWORD: 'pass',
'region': 'us', 'region': 'us',
} }
bad_pass_config = {
CONF_USERNAME: 'user',
'region': 'us',
}
bad_region_config = {
CONF_USERNAME: 'user',
CONF_PASSWORD: 'pass',
'region': 'un',
}
hass = mock.MagicMock() hass = mock.MagicMock()
add_devices = 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[0].devices_by_id.values.return_value = devices_1
locations[1].devices_by_id.values.return_value = devices_2 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) result = honeywell.setup_platform(hass, config, add_devices)
self.assertTrue(result) self.assertTrue(result)
mock_sc.assert_called_once_with('user', 'pass') mock_sc.assert_called_once_with('user', 'pass')