diff --git a/.coveragerc b/.coveragerc index b2acc2d5657bb0da0e372ddebea0008056b28deb..a57f5ac99b70da61f06783d003cf229257c3e857 100644 --- a/.coveragerc +++ b/.coveragerc @@ -95,7 +95,6 @@ omit = homeassistant/components/homematic.py homeassistant/components/*/homematic.py - homeassistant/components/pilight.py homeassistant/components/switch/pilight.py homeassistant/components/knx.py diff --git a/homeassistant/components/pilight.py b/homeassistant/components/pilight.py index 3475a6be65a919e2b31341b0ff708d71edc1b3e5..2cfbc0063a1d69e41be712955819e2df649fbc62 100644 --- a/homeassistant/components/pilight.py +++ b/homeassistant/components/pilight.py @@ -14,7 +14,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT, CONF_WHITELIST) -REQUIREMENTS = ['pilight==0.0.2'] +REQUIREMENTS = ['pilight==0.1.1'] _LOGGER = logging.getLogger(__name__) @@ -102,7 +102,7 @@ def setup(hass, config): if not whitelist: hass.bus.fire(EVENT, data) # Check if data matches the defined whitelist - elif all(data[key] in whitelist[key] for key in whitelist): + elif all(str(data[key]) in whitelist[key] for key in whitelist): hass.bus.fire(EVENT, data) pilight_client.set_callback(handle_received_code) diff --git a/requirements_all.txt b/requirements_all.txt index 3d0eeb337d4b3b7fb25f7ce514259d94d6333eee..d17775487fcd107f01db54ff41c6a6ae48980dc4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -294,7 +294,7 @@ pexpect==4.0.1 phue==0.8 # homeassistant.components.pilight -pilight==0.0.2 +pilight==0.1.1 # homeassistant.components.media_player.plex # homeassistant.components.sensor.plex diff --git a/tests/components/test_pilight.py b/tests/components/test_pilight.py new file mode 100644 index 0000000000000000000000000000000000000000..ca491ee838d22c0c5e45c45fd8be7646ba7e15d3 --- /dev/null +++ b/tests/components/test_pilight.py @@ -0,0 +1,298 @@ +"""The tests for the pilight component.""" +import logging +import unittest +from unittest.mock import patch +import socket + +from homeassistant.bootstrap import setup_component +from homeassistant.components import pilight + +from tests.common import get_test_home_assistant, assert_setup_component + +_LOGGER = logging.getLogger(__name__) + + +class PilightDaemonSim: + """Class to fake the interface of the pilight python package. + + Is used in an asyncio loop, thus the mock cannot be accessed to + determine if methods where called?! + This is solved here in a hackish way by printing errors + that can be checked using logging.error mocks. + """ + + callback = None + called = None + + test_message = {"protocol": "kaku_switch", + "uuid": "1-2-3-4", + "message": { + "id": 0, + "unit": 0, + "off": 1}} + + def __init__(self, host, port): + """Init pilight client, ignore parameters.""" + pass + + def send_code(self, call): # pylint: disable=no-self-use + """Called pilight.send service is called.""" + _LOGGER.error('PilightDaemonSim payload: ' + str(call)) + + def start(self): + """Called homeassistant.start is called. + + Also sends one test message after start up + """ + _LOGGER.error('PilightDaemonSim start') + # Fake one code receive after daemon started + if not self.called: + self.callback(self.test_message) + self.called = True + + def stop(self): # pylint: disable=no-self-use + """Called homeassistant.stop is called.""" + _LOGGER.error('PilightDaemonSim stop') + + def set_callback(self, function): + """Callback called on event pilight.pilight_received.""" + self.callback = function + _LOGGER.error('PilightDaemonSim callback: ' + str(function)) + + +class TestPilight(unittest.TestCase): + """Test the Pilight component.""" + + def setUp(self): # pylint: disable=invalid-name + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + @patch('homeassistant.components.pilight._LOGGER.error') + def test_connection_failed_error(self, mock_error): + """Try to connect at 127.0.0.1:5000 with socket error.""" + with assert_setup_component(3): + with patch('pilight.pilight.Client', + side_effect=socket.error) as mock_client: + self.assertFalse(setup_component( + self.hass, pilight.DOMAIN, {pilight.DOMAIN: {}})) + mock_client.assert_called_once_with(host=pilight.DEFAULT_HOST, + port=pilight.DEFAULT_PORT) + self.assertEqual(1, mock_error.call_count) + + @patch('homeassistant.components.pilight._LOGGER.error') + def test_connection_timeout_error(self, mock_error): + """Try to connect at 127.0.0.1:5000 with socket timeout.""" + with assert_setup_component(3): + with patch('pilight.pilight.Client', + side_effect=socket.timeout) as mock_client: + self.assertFalse(setup_component( + self.hass, pilight.DOMAIN, {pilight.DOMAIN: {}})) + mock_client.assert_called_once_with(host=pilight.DEFAULT_HOST, + port=pilight.DEFAULT_PORT) + self.assertEqual(1, mock_error.call_count) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('homeassistant.core._LOGGER.error') + @patch('tests.components.test_pilight._LOGGER.error') + def test_send_code_no_protocol(self, mock_pilight_error, mock_error): + """Try to send data without protocol information, should give error.""" + with assert_setup_component(3): + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, {pilight.DOMAIN: {}})) + + # Call without protocol info, should be ignored with error + self.hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, + service_data={'noprotocol': 'test', + 'value': 42}, + blocking=True) + self.hass.block_till_done() + error_log_call = mock_error.call_args_list[-1] + self.assertTrue( + 'required key not provided @ data[\'protocol\']' in + str(error_log_call)) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('tests.components.test_pilight._LOGGER.error') + def test_send_code(self, mock_pilight_error): + """Try to send proper data.""" + with assert_setup_component(3): + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, {pilight.DOMAIN: {}})) + + # Call with protocol info, should not give error + service_data = {'protocol': 'test', + 'value': 42} + self.hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, + service_data=service_data, + blocking=True) + self.hass.block_till_done() + error_log_call = mock_pilight_error.call_args_list[-1] + service_data['protocol'] = [service_data['protocol']] + self.assertTrue(str(service_data) in str(error_log_call)) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('homeassistant.components.pilight._LOGGER.error') + def test_send_code_fail(self, mock_pilight_error): + """Check IOError exception error message.""" + with assert_setup_component(3): + with patch('pilight.pilight.Client.send_code', + side_effect=IOError): + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, {pilight.DOMAIN: {}})) + + # Call with protocol info, should not give error + service_data = {'protocol': 'test', + 'value': 42} + self.hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, + service_data=service_data, + blocking=True) + self.hass.block_till_done() + error_log_call = mock_pilight_error.call_args_list[-1] + self.assertTrue('Pilight send failed' in str(error_log_call)) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('tests.components.test_pilight._LOGGER.error') + def test_start_stop(self, mock_pilight_error): + """Check correct startup and stop of pilight daemon.""" + with assert_setup_component(3): + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, {pilight.DOMAIN: {}})) + + # Test startup + self.hass.start() + self.hass.block_till_done() + error_log_call = mock_pilight_error.call_args_list[-2] + self.assertTrue( + 'PilightDaemonSim callback' in str(error_log_call)) + error_log_call = mock_pilight_error.call_args_list[-1] + self.assertTrue( + 'PilightDaemonSim start' in str(error_log_call)) + + # Test stop + self.hass.stop() + error_log_call = mock_pilight_error.call_args_list[-1] + self.assertTrue( + 'PilightDaemonSim stop' in str(error_log_call)) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('homeassistant.core._LOGGER.info') + def test_receive_code(self, mock_info): + """Check if code receiving via pilight daemon works.""" + with assert_setup_component(3): + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, {pilight.DOMAIN: {}})) + + # Test startup + self.hass.start() + self.hass.block_till_done() + + expected_message = dict( + {'protocol': PilightDaemonSim.test_message['protocol'], + 'uuid': PilightDaemonSim.test_message['uuid']}, + **PilightDaemonSim.test_message['message']) + error_log_call = mock_info.call_args_list[-1] + + # Check if all message parts are put on event bus + for key, value in expected_message.items(): + self.assertTrue(str(key) in str(error_log_call)) + self.assertTrue(str(value) in str(error_log_call)) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('homeassistant.core._LOGGER.info') + def test_whitelist_exact_match(self, mock_info): + """Check whitelist filter with matched data.""" + with assert_setup_component(3): + whitelist = { + 'protocol': [PilightDaemonSim.test_message['protocol']], + 'uuid': [PilightDaemonSim.test_message['uuid']], + 'id': [PilightDaemonSim.test_message['message']['id']], + 'unit': [PilightDaemonSim.test_message['message']['unit']]} + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, + {pilight.DOMAIN: {"whitelist": whitelist}})) + + self.hass.start() + self.hass.block_till_done() + + expected_message = dict( + {'protocol': PilightDaemonSim.test_message['protocol'], + 'uuid': PilightDaemonSim.test_message['uuid']}, + **PilightDaemonSim.test_message['message']) + info_log_call = mock_info.call_args_list[-1] + + # Check if all message parts are put on event bus + for key, value in expected_message.items(): + self.assertTrue(str(key) in str(info_log_call)) + self.assertTrue(str(value) in str(info_log_call)) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('homeassistant.core._LOGGER.info') + def test_whitelist_partial_match(self, mock_info): + """Check whitelist filter with partially matched data, should work.""" + with assert_setup_component(3): + whitelist = { + 'protocol': [PilightDaemonSim.test_message['protocol']], + 'id': [PilightDaemonSim.test_message['message']['id']]} + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, + {pilight.DOMAIN: {"whitelist": whitelist}})) + + self.hass.start() + self.hass.block_till_done() + + expected_message = dict( + {'protocol': PilightDaemonSim.test_message['protocol'], + 'uuid': PilightDaemonSim.test_message['uuid']}, + **PilightDaemonSim.test_message['message']) + info_log_call = mock_info.call_args_list[-1] + + # Check if all message parts are put on event bus + for key, value in expected_message.items(): + self.assertTrue(str(key) in str(info_log_call)) + self.assertTrue(str(value) in str(info_log_call)) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('homeassistant.core._LOGGER.info') + def test_whitelist_or_match(self, mock_info): + """Check whitelist filter with several subsection, should work.""" + with assert_setup_component(3): + whitelist = { + 'protocol': [PilightDaemonSim.test_message['protocol'], + 'other_protocoll'], + 'id': [PilightDaemonSim.test_message['message']['id']]} + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, + {pilight.DOMAIN: {"whitelist": whitelist}})) + + self.hass.start() + self.hass.block_till_done() + + expected_message = dict( + {'protocol': PilightDaemonSim.test_message['protocol'], + 'uuid': PilightDaemonSim.test_message['uuid']}, + **PilightDaemonSim.test_message['message']) + info_log_call = mock_info.call_args_list[-1] + + # Check if all message parts are put on event bus + for key, value in expected_message.items(): + self.assertTrue(str(key) in str(info_log_call)) + self.assertTrue(str(value) in str(info_log_call)) + + @patch('pilight.pilight.Client', PilightDaemonSim) + @patch('homeassistant.core._LOGGER.info') + def test_whitelist_no_match(self, mock_info): + """Check whitelist filter with unmatched data, should not work.""" + with assert_setup_component(3): + whitelist = { + 'protocol': ['wrong_protocoll'], + 'id': [PilightDaemonSim.test_message['message']['id']]} + self.assertTrue(setup_component( + self.hass, pilight.DOMAIN, + {pilight.DOMAIN: {"whitelist": whitelist}})) + + self.hass.start() + self.hass.block_till_done() + + info_log_call = mock_info.call_args_list[-1] + + self.assertFalse('Event pilight_received' in info_log_call)