From 1e31af77de90c9b3e89cb5e6f7c15334b05b3c46 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 5 May 2018 11:41:55 -0400 Subject: [PATCH 01/17] Version bump to 0.69.0b0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 30c73546cf7..7462710ea23 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 69 -PATCH_VERSION = '0.dev0' +PATCH_VERSION = '0b0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 5c88e897af38c69fc188ef2dfbfd62372f522088 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 7 May 2018 10:00:54 -0400 Subject: [PATCH 02/17] Update netdisco to 1.4.1 --- homeassistant/components/discovery.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 07eb5aaab82..65d0a1c76f3 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -21,7 +21,7 @@ from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.discovery import async_load_platform, async_discover import homeassistant.util.dt as dt_util -REQUIREMENTS = ['netdisco==1.4.0'] +REQUIREMENTS = ['netdisco==1.4.1'] DOMAIN = 'discovery' diff --git a/requirements_all.txt b/requirements_all.txt index e74070cb98c..735f39a8013 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -553,7 +553,7 @@ nad_receiver==0.0.9 nanoleaf==0.4.1 # homeassistant.components.discovery -netdisco==1.4.0 +netdisco==1.4.1 # homeassistant.components.sensor.neurio_energy neurio==0.3.1 From ab621808bd3ddeae5055545c906ebf593dbf6789 Mon Sep 17 00:00:00 2001 From: Justin Loutsenhizer Date: Sun, 6 May 2018 13:18:26 -0400 Subject: [PATCH 03/17] Add missing 'sensor' to ABODE_PLATFORMS (#14313) This fixes missing light, humidity, temperature sensors from abode component. --- homeassistant/components/abode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/abode.py b/homeassistant/components/abode.py index 2f56bb7c2b5..6d5feb87dc2 100644 --- a/homeassistant/components/abode.py +++ b/homeassistant/components/abode.py @@ -81,7 +81,7 @@ TRIGGER_SCHEMA = vol.Schema({ ABODE_PLATFORMS = [ 'alarm_control_panel', 'binary_sensor', 'lock', 'switch', 'cover', - 'camera', 'light' + 'camera', 'light', 'sensor' ] From c48986a4674466d437cc7a1f672ac465c0189ce6 Mon Sep 17 00:00:00 2001 From: cdce8p <30130371+cdce8p@users.noreply.github.com> Date: Mon, 7 May 2018 02:55:38 +0200 Subject: [PATCH 04/17] Add debounce to move_cover (#14314) * Add debounce to move_cover * Fix spelling mistake --- .../components/homekit/type_covers.py | 4 ++- .../homekit/type_security_systems.py | 2 +- tests/components/homekit/test_type_covers.py | 34 ++++++++++++++----- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/homekit/type_covers.py b/homeassistant/components/homekit/type_covers.py index b30109f711d..3de87cf63e8 100644 --- a/homeassistant/components/homekit/type_covers.py +++ b/homeassistant/components/homekit/type_covers.py @@ -11,7 +11,7 @@ from homeassistant.const import ( ATTR_SUPPORTED_FEATURES) from . import TYPES -from .accessories import HomeAccessory +from .accessories import HomeAccessory, debounce from .const import ( SERV_WINDOW_COVERING, CHAR_CURRENT_POSITION, CHAR_TARGET_POSITION, CHAR_POSITION_STATE, @@ -80,6 +80,7 @@ class WindowCovering(HomeAccessory): self.char_target_position = serv_cover.configure_char( CHAR_TARGET_POSITION, value=0, setter_callback=self.move_cover) + @debounce def move_cover(self, value): """Move cover to value if call came from HomeKit.""" _LOGGER.debug('%s: Set position to %d', self.entity_id, value) @@ -122,6 +123,7 @@ class WindowCoveringBasic(HomeAccessory): self.char_position_state = serv_cover.configure_char( CHAR_POSITION_STATE, value=2) + @debounce def move_cover(self, value): """Move cover to value if call came from HomeKit.""" _LOGGER.debug('%s: Set position to %d', self.entity_id, value) diff --git a/homeassistant/components/homekit/type_security_systems.py b/homeassistant/components/homekit/type_security_systems.py index e32860d1fef..ab16f921e99 100644 --- a/homeassistant/components/homekit/type_security_systems.py +++ b/homeassistant/components/homekit/type_security_systems.py @@ -67,7 +67,7 @@ class SecuritySystem(HomeAccessory): _LOGGER.debug('%s: Updated current state to %s (%d)', self.entity_id, hass_state, current_security_state) - # SecuritySystemTargetSTate does not support triggered + # SecuritySystemTargetState does not support triggered if not self.flag_target_state and \ hass_state != STATE_ALARM_TRIGGERED: self.char_target_state.set_value(current_security_state) diff --git a/tests/components/homekit/test_type_covers.py b/tests/components/homekit/test_type_covers.py index 2dcb48a4d4c..313d58e78fd 100644 --- a/tests/components/homekit/test_type_covers.py +++ b/tests/components/homekit/test_type_covers.py @@ -4,19 +4,35 @@ import unittest from homeassistant.core import callback from homeassistant.components.cover import ( ATTR_POSITION, ATTR_CURRENT_POSITION, SUPPORT_STOP) -from homeassistant.components.homekit.type_covers import ( - GarageDoorOpener, WindowCovering, WindowCoveringBasic) from homeassistant.const import ( STATE_CLOSED, STATE_UNAVAILABLE, STATE_UNKNOWN, STATE_OPEN, ATTR_SERVICE, ATTR_SERVICE_DATA, EVENT_CALL_SERVICE, ATTR_SUPPORTED_FEATURES) from tests.common import get_test_home_assistant +from tests.components.homekit.test_accessories import patch_debounce -class TestHomekitSensors(unittest.TestCase): +class TestHomekitCovers(unittest.TestCase): """Test class for all accessory types regarding covers.""" + @classmethod + def setUpClass(cls): + """Setup Light class import and debounce patcher.""" + cls.patcher = patch_debounce() + cls.patcher.start() + _import = __import__('homeassistant.components.homekit.type_covers', + fromlist=['GarageDoorOpener', 'WindowCovering,', + 'WindowCoveringBasic']) + cls.garage_cls = _import.GarageDoorOpener + cls.window_cls = _import.WindowCovering + cls.window_basic_cls = _import.WindowCoveringBasic + + @classmethod + def tearDownClass(cls): + """Stop debounce patcher.""" + cls.patcher.stop() + def setUp(self): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() @@ -37,7 +53,7 @@ class TestHomekitSensors(unittest.TestCase): """Test if accessory and HA are updated accordingly.""" garage_door = 'cover.garage_door' - acc = GarageDoorOpener(self.hass, 'Cover', garage_door, 2, config=None) + acc = self.garage_cls(self.hass, 'Cover', garage_door, 2, config=None) acc.run() self.assertEqual(acc.aid, 2) @@ -95,7 +111,7 @@ class TestHomekitSensors(unittest.TestCase): """Test if accessory and HA are updated accordingly.""" window_cover = 'cover.window' - acc = WindowCovering(self.hass, 'Cover', window_cover, 2, config=None) + acc = self.window_cls(self.hass, 'Cover', window_cover, 2, config=None) acc.run() self.assertEqual(acc.aid, 2) @@ -146,8 +162,8 @@ class TestHomekitSensors(unittest.TestCase): self.hass.states.set(window_cover, STATE_UNKNOWN, {ATTR_SUPPORTED_FEATURES: 0}) - acc = WindowCoveringBasic(self.hass, 'Cover', window_cover, 2, - config=None) + acc = self.window_basic_cls(self.hass, 'Cover', window_cover, 2, + config=None) acc.run() self.assertEqual(acc.aid, 2) @@ -214,8 +230,8 @@ class TestHomekitSensors(unittest.TestCase): self.hass.states.set(window_cover, STATE_UNKNOWN, {ATTR_SUPPORTED_FEATURES: SUPPORT_STOP}) - acc = WindowCoveringBasic(self.hass, 'Cover', window_cover, 2, - config=None) + acc = self.window_basic_cls(self.hass, 'Cover', window_cover, 2, + config=None) acc.run() # Set from HomeKit From c4ec2e3434e3981476de6581bb578cf88dd01693 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 6 May 2018 20:54:56 -0400 Subject: [PATCH 05/17] Fix module names for custom components (#14317) * Fix module names for custom components * Also set __package__ correctly * bla * Remove print --- homeassistant/loader.py | 23 +++++++++++++++---- tests/test_loader.py | 18 +++++++++++++-- .../test_package/__init__.py | 2 +- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 322870952f2..b6dabb1d883 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -73,13 +73,15 @@ def get_component(hass, comp_or_platform): # Try custom component module = _load_module(hass.config.path(PATH_CUSTOM_COMPONENTS), - comp_or_platform) + PATH_CUSTOM_COMPONENTS, comp_or_platform) if module is None: try: module = importlib.import_module( '{}.{}'.format(PACKAGE_COMPONENTS, comp_or_platform)) + _LOGGER.debug('Loaded %s (built-in)', comp_or_platform) except ImportError: + _LOGGER.warning('Unable to find %s', comp_or_platform) module = None cache = hass.data.get(DATA_KEY) @@ -102,18 +104,20 @@ def _find_spec(path, name): return None -def _load_module(path, name): +def _load_module(path, base_module, name): """Load a module based on a folder and a name.""" + mod_name = "{}.{}".format(base_module, name) spec = _find_spec([path], name) # Special handling if loading platforms and the folder is a namespace # (namespace is a folder without __init__.py) if spec is None and '.' in name: - parent_spec = _find_spec([path], name.split('.')[0]) + mod_parent_name = name.split('.')[0] + parent_spec = _find_spec([path], mod_parent_name) if (parent_spec is None or parent_spec.submodule_search_locations is None): return None - spec = _find_spec(parent_spec.submodule_search_locations, name) + spec = _find_spec(parent_spec.submodule_search_locations, mod_name) # Not found if spec is None: @@ -123,8 +127,19 @@ def _load_module(path, name): if spec.loader is None: return None + _LOGGER.debug('Loaded %s (%s)', name, base_module) + module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) + # A hack, I know. Don't currently know how to work around it. + if not module.__name__.startswith(base_module): + module.__name__ = "{}.{}".format(base_module, name) + + if not module.__package__: + module.__package__ = base_module + elif not module.__package__.startswith(base_module): + module.__package__ = "{}.{}".format(base_module, name) + return module diff --git a/tests/test_loader.py b/tests/test_loader.py index 646526e94ea..e8a79c6501f 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -30,8 +30,7 @@ class TestLoader(unittest.TestCase): comp = object() loader.set_component(self.hass, 'switch.test_set', comp) - self.assertEqual(comp, - loader.get_component(self.hass, 'switch.test_set')) + assert loader.get_component(self.hass, 'switch.test_set') is comp def test_get_component(self): """Test if get_component works.""" @@ -106,3 +105,18 @@ def test_helpers_wrapper(hass): yield from hass.async_block_till_done() assert result == ['hello'] + + +async def test_custom_component_name(hass): + """Test the name attribte of custom components.""" + comp = loader.get_component(hass, 'test_standalone') + assert comp.__name__ == 'custom_components.test_standalone' + assert comp.__package__ == 'custom_components' + + comp = loader.get_component(hass, 'test_package') + assert comp.__name__ == 'custom_components.test_package' + assert comp.__package__ == 'custom_components.test_package' + + comp = loader.get_component(hass, 'light.test') + assert comp.__name__ == 'custom_components.light.test' + assert comp.__package__ == 'custom_components.light' diff --git a/tests/testing_config/custom_components/test_package/__init__.py b/tests/testing_config/custom_components/test_package/__init__.py index 528f056948b..ee669c6c9b5 100644 --- a/tests/testing_config/custom_components/test_package/__init__.py +++ b/tests/testing_config/custom_components/test_package/__init__.py @@ -2,6 +2,6 @@ DOMAIN = 'test_package' -def setup(hass, config): +async def async_setup(hass, config): """Mock a successful setup.""" return True From 6a74fa344d693b12d9839b229f178dd87cf8e2af Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 7 May 2018 05:25:48 -0400 Subject: [PATCH 06/17] Revert custom component loading logic (#14327) * Revert custom component loading logic * Lint * Fix tests * Guard for infinite inserts into sys.path --- homeassistant/loader.py | 114 +++++++----------- tests/components/notify/test_file.py | 46 ++++--- tests/test_loader.py | 4 + .../image_processing/test.py | 6 +- .../custom_components/light/test.py | 5 +- .../custom_components/switch/test.py | 5 +- .../test_package/__init__.py | 3 + .../custom_components/test_package/const.py | 2 + 8 files changed, 84 insertions(+), 101 deletions(-) create mode 100644 tests/testing_config/custom_components/test_package/const.py diff --git a/homeassistant/loader.py b/homeassistant/loader.py index b6dabb1d883..e94fb2d6833 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -31,12 +31,6 @@ PREPARED = False DEPENDENCY_BLACKLIST = set(('config',)) -# List of available components -AVAILABLE_COMPONENTS = [] # type: List[str] - -# Dict of loaded components mapped name => module -_COMPONENT_CACHE = {} # type: Dict[str, ModuleType] - _LOGGER = logging.getLogger(__name__) @@ -64,85 +58,63 @@ def get_platform(hass, domain: str, platform: str) -> Optional[ModuleType]: return get_component(hass, PLATFORM_FORMAT.format(domain, platform)) -def get_component(hass, comp_or_platform): - """Load a module from either custom component or built-in.""" +def get_component(hass, comp_or_platform) -> Optional[ModuleType]: + """Try to load specified component. + + Looks in config dir first, then built-in components. + Only returns it if also found to be valid. + Async friendly. + """ try: return hass.data[DATA_KEY][comp_or_platform] except KeyError: pass - # Try custom component - module = _load_module(hass.config.path(PATH_CUSTOM_COMPONENTS), - PATH_CUSTOM_COMPONENTS, comp_or_platform) - - if module is None: - try: - module = importlib.import_module( - '{}.{}'.format(PACKAGE_COMPONENTS, comp_or_platform)) - _LOGGER.debug('Loaded %s (built-in)', comp_or_platform) - except ImportError: - _LOGGER.warning('Unable to find %s', comp_or_platform) - module = None - cache = hass.data.get(DATA_KEY) if cache is None: + # Only insert if it's not there (happens during tests) + if sys.path[0] != hass.config.config_dir: + sys.path.insert(0, hass.config.config_dir) cache = hass.data[DATA_KEY] = {} - cache[comp_or_platform] = module - return module + # First check custom, then built-in + potential_paths = ['custom_components.{}'.format(comp_or_platform), + 'homeassistant.components.{}'.format(comp_or_platform)] - -def _find_spec(path, name): - for finder in sys.meta_path: + for path in potential_paths: try: - spec = finder.find_spec(name, path=path) - if spec is not None: - return spec - except AttributeError: - # Not all finders have the find_spec method - pass + module = importlib.import_module(path) + + # In Python 3 you can import files from directories that do not + # contain the file __init__.py. A directory is a valid module if + # it contains a file with the .py extension. In this case Python + # will succeed in importing the directory as a module and call it + # a namespace. We do not care about namespaces. + # This prevents that when only + # custom_components/switch/some_platform.py exists, + # the import custom_components.switch would succeed. + if module.__spec__.origin == 'namespace': + continue + + _LOGGER.info("Loaded %s from %s", comp_or_platform, path) + + cache[comp_or_platform] = module + + return module + + except ImportError as err: + # This error happens if for example custom_components/switch + # exists and we try to load switch.demo. + if str(err) != "No module named '{}'".format(path): + _LOGGER.exception( + ("Error loading %s. Make sure all " + "dependencies are installed"), path) + + _LOGGER.error("Unable to find component %s", comp_or_platform) + return None -def _load_module(path, base_module, name): - """Load a module based on a folder and a name.""" - mod_name = "{}.{}".format(base_module, name) - spec = _find_spec([path], name) - - # Special handling if loading platforms and the folder is a namespace - # (namespace is a folder without __init__.py) - if spec is None and '.' in name: - mod_parent_name = name.split('.')[0] - parent_spec = _find_spec([path], mod_parent_name) - if (parent_spec is None or - parent_spec.submodule_search_locations is None): - return None - spec = _find_spec(parent_spec.submodule_search_locations, mod_name) - - # Not found - if spec is None: - return None - - # This is a namespace - if spec.loader is None: - return None - - _LOGGER.debug('Loaded %s (%s)', name, base_module) - - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - # A hack, I know. Don't currently know how to work around it. - if not module.__name__.startswith(base_module): - module.__name__ = "{}.{}".format(base_module, name) - - if not module.__package__: - module.__package__ = base_module - elif not module.__package__.startswith(base_module): - module.__package__ = "{}.{}".format(base_module, name) - - return module - - class Components: """Helper to load components.""" diff --git a/tests/components/notify/test_file.py b/tests/components/notify/test_file.py index 42b9eb9d82d..c5064fca851 100644 --- a/tests/components/notify/test_file.py +++ b/tests/components/notify/test_file.py @@ -35,28 +35,30 @@ class TestNotifyFile(unittest.TestCase): assert setup_component(self.hass, notify.DOMAIN, config) assert not handle_config[notify.DOMAIN] - def _test_notify_file(self, timestamp, mock_utcnow, mock_stat): + def _test_notify_file(self, timestamp): """Test the notify file output.""" - mock_utcnow.return_value = dt_util.as_utc(dt_util.now()) - mock_stat.return_value.st_size = 0 + filename = 'mock_file' + message = 'one, two, testing, testing' + with assert_setup_component(1) as handle_config: + self.assertTrue(setup_component(self.hass, notify.DOMAIN, { + 'notify': { + 'name': 'test', + 'platform': 'file', + 'filename': filename, + 'timestamp': timestamp, + } + })) + assert handle_config[notify.DOMAIN] m_open = mock_open() with patch( 'homeassistant.components.notify.file.open', m_open, create=True - ): - filename = 'mock_file' - message = 'one, two, testing, testing' - with assert_setup_component(1) as handle_config: - self.assertTrue(setup_component(self.hass, notify.DOMAIN, { - 'notify': { - 'name': 'test', - 'platform': 'file', - 'filename': filename, - 'timestamp': timestamp, - } - })) - assert handle_config[notify.DOMAIN] + ), patch('homeassistant.components.notify.file.os.stat') as mock_st, \ + patch('homeassistant.util.dt.utcnow', + return_value=dt_util.utcnow()): + + mock_st.return_value.st_size = 0 title = '{} notifications (Log started: {})\n{}\n'.format( ATTR_TITLE_DEFAULT, dt_util.utcnow().isoformat(), @@ -82,14 +84,10 @@ class TestNotifyFile(unittest.TestCase): dt_util.utcnow().isoformat(), message))] ) - @patch('homeassistant.components.notify.file.os.stat') - @patch('homeassistant.util.dt.utcnow') - def test_notify_file(self, mock_utcnow, mock_stat): + def test_notify_file(self): """Test the notify file output without timestamp.""" - self._test_notify_file(False, mock_utcnow, mock_stat) + self._test_notify_file(False) - @patch('homeassistant.components.notify.file.os.stat') - @patch('homeassistant.util.dt.utcnow') - def test_notify_file_timestamp(self, mock_utcnow, mock_stat): + def test_notify_file_timestamp(self): """Test the notify file output with timestamp.""" - self._test_notify_file(True, mock_utcnow, mock_stat) + self._test_notify_file(True) diff --git a/tests/test_loader.py b/tests/test_loader.py index e8a79c6501f..c97e94a7ce1 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -120,3 +120,7 @@ async def test_custom_component_name(hass): comp = loader.get_component(hass, 'light.test') assert comp.__name__ == 'custom_components.light.test' assert comp.__package__ == 'custom_components.light' + + # Test custom components is mounted + from custom_components.test_package import TEST + assert TEST == 5 diff --git a/tests/testing_config/custom_components/image_processing/test.py b/tests/testing_config/custom_components/image_processing/test.py index 29d362699f5..b50050ed68e 100644 --- a/tests/testing_config/custom_components/image_processing/test.py +++ b/tests/testing_config/custom_components/image_processing/test.py @@ -3,9 +3,11 @@ from homeassistant.components.image_processing import ImageProcessingEntity -def setup_platform(hass, config, add_devices, discovery_info=None): +async def async_setup_platform(hass, config, async_add_devices_callback, + discovery_info=None): """Set up the test image_processing platform.""" - add_devices([TestImageProcessing('camera.demo_camera', "Test")]) + async_add_devices_callback([ + TestImageProcessing('camera.demo_camera', "Test")]) class TestImageProcessing(ImageProcessingEntity): diff --git a/tests/testing_config/custom_components/light/test.py b/tests/testing_config/custom_components/light/test.py index 71625dfdf93..fbf79f9e770 100644 --- a/tests/testing_config/custom_components/light/test.py +++ b/tests/testing_config/custom_components/light/test.py @@ -21,6 +21,7 @@ def init(empty=False): ] -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +async def async_setup_platform(hass, config, async_add_devices_callback, + discovery_info=None): """Return mock devices.""" - add_devices_callback(DEVICES) + async_add_devices_callback(DEVICES) diff --git a/tests/testing_config/custom_components/switch/test.py b/tests/testing_config/custom_components/switch/test.py index 2819f2f2951..79126b7b52a 100644 --- a/tests/testing_config/custom_components/switch/test.py +++ b/tests/testing_config/custom_components/switch/test.py @@ -21,6 +21,7 @@ def init(empty=False): ] -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +async def async_setup_platform(hass, config, async_add_devices_callback, + discovery_info=None): """Find and return test switches.""" - add_devices_callback(DEVICES) + async_add_devices_callback(DEVICES) diff --git a/tests/testing_config/custom_components/test_package/__init__.py b/tests/testing_config/custom_components/test_package/__init__.py index ee669c6c9b5..85e78a7f9d6 100644 --- a/tests/testing_config/custom_components/test_package/__init__.py +++ b/tests/testing_config/custom_components/test_package/__init__.py @@ -1,4 +1,7 @@ """Provide a mock package component.""" +from .const import TEST # noqa + + DOMAIN = 'test_package' diff --git a/tests/testing_config/custom_components/test_package/const.py b/tests/testing_config/custom_components/test_package/const.py new file mode 100644 index 00000000000..7e13e04cb47 --- /dev/null +++ b/tests/testing_config/custom_components/test_package/const.py @@ -0,0 +1,2 @@ +"""Constants for test_package custom component.""" +TEST = 5 From 3e5d76efb277501a9fc8b14647fe7fa351f4c073 Mon Sep 17 00:00:00 2001 From: Javier Gonel Date: Mon, 7 May 2018 16:52:33 +0300 Subject: [PATCH 07/17] fix(hbmqtt): partial packets breaking hbmqtt (#14329) This issue was fixed in hbmqtt/issues#95 that was released in hbmqtt 0.9.2 --- homeassistant/components/mqtt/server.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/mqtt/server.py b/homeassistant/components/mqtt/server.py index db251ab4180..8a012928792 100644 --- a/homeassistant/components/mqtt/server.py +++ b/homeassistant/components/mqtt/server.py @@ -13,7 +13,7 @@ import voluptuous as vol from homeassistant.const import EVENT_HOMEASSISTANT_STOP import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['hbmqtt==0.9.1'] +REQUIREMENTS = ['hbmqtt==0.9.2'] DEPENDENCIES = ['http'] # None allows custom config to be created through generate_config diff --git a/requirements_all.txt b/requirements_all.txt index 735f39a8013..6ddf0f81f61 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -371,7 +371,7 @@ ha-philipsjs==0.0.3 haversine==0.4.5 # homeassistant.components.mqtt.server -hbmqtt==0.9.1 +hbmqtt==0.9.2 # homeassistant.components.climate.heatmiser heatmiserV3==0.9.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 939e4314718..976f4d87280 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -75,7 +75,7 @@ ha-ffmpeg==1.9 haversine==0.4.5 # homeassistant.components.mqtt.server -hbmqtt==0.9.1 +hbmqtt==0.9.2 # homeassistant.components.binary_sensor.workday holidays==0.9.5 From a4e1615127e5e4fde91a07ca855475aab556552a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 7 May 2018 10:05:34 -0400 Subject: [PATCH 08/17] Version bump to 0.69.0b1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 7462710ea23..a1c19d41efe 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 69 -PATCH_VERSION = '0b0' +PATCH_VERSION = '0b1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From b1eb35ee119c07db2e85ebf44ad5599413d104e1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 7 May 2018 13:12:12 -0400 Subject: [PATCH 09/17] Ignore more loading errors (#14331) --- homeassistant/loader.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index e94fb2d6833..67647a323c9 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -105,7 +105,16 @@ def get_component(hass, comp_or_platform) -> Optional[ModuleType]: except ImportError as err: # This error happens if for example custom_components/switch # exists and we try to load switch.demo. - if str(err) != "No module named '{}'".format(path): + # Ignore errors for custom_components, custom_components.switch + # and custom_components.switch.demo. + white_listed_errors = [] + parts = [] + for part in path.split('.'): + parts.append(part) + white_listed_errors.append( + "No module named '{}'".format('.'.join(parts))) + + if str(err) not in white_listed_errors: _LOGGER.exception( ("Error loading %s. Make sure all " "dependencies are installed"), path) From 8d24541ffe3d20bf4b714656a3505f0958aeeff5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 7 May 2018 13:12:54 -0400 Subject: [PATCH 10/17] Version bump to 0.69.0b2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index a1c19d41efe..7d54bf6356d 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 69 -PATCH_VERSION = '0b1' +PATCH_VERSION = '0b2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 01ec4a7afdf616d2ff37a266c48e33804b16bab6 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 8 May 2018 20:48:46 -0400 Subject: [PATCH 11/17] Bump frontend to 20180509.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index b4eb6df07e1..0d267077991 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -25,7 +25,7 @@ from homeassistant.core import callback from homeassistant.helpers.translation import async_get_translations from homeassistant.loader import bind_hass -REQUIREMENTS = ['home-assistant-frontend==20180505.0'] +REQUIREMENTS = ['home-assistant-frontend==20180509.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log'] diff --git a/requirements_all.txt b/requirements_all.txt index 6ddf0f81f61..6bcd267e456 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -386,7 +386,7 @@ hipnotify==1.0.8 holidays==0.9.5 # homeassistant.components.frontend -home-assistant-frontend==20180505.0 +home-assistant-frontend==20180509.0 # homeassistant.components.homekit_controller # homekit==0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 976f4d87280..a25f36a8195 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -81,7 +81,7 @@ hbmqtt==0.9.2 holidays==0.9.5 # homeassistant.components.frontend -home-assistant-frontend==20180505.0 +home-assistant-frontend==20180509.0 # homeassistant.components.influxdb # homeassistant.components.sensor.influxdb From 2d0e3c14021d51ce7c780720d61ec303718dce90 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Wed, 9 May 2018 02:54:38 +0200 Subject: [PATCH 12/17] Ignore NaN values for influxdb (#14347) * Ignore NaN values for influxdb * Catch TypeError --- homeassistant/components/influxdb.py | 10 +++++++--- tests/components/test_influxdb.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/influxdb.py b/homeassistant/components/influxdb.py index 1f7f9f6262f..6d54324542a 100644 --- a/homeassistant/components/influxdb.py +++ b/homeassistant/components/influxdb.py @@ -9,6 +9,7 @@ import re import queue import threading import time +import math import requests.exceptions import voluptuous as vol @@ -220,9 +221,12 @@ def setup(hass, config): json['fields'][key] = float( RE_DECIMAL.sub('', new_value)) - # Infinity is not a valid float in InfluxDB - if (key, float("inf")) in json['fields'].items(): - del json['fields'][key] + # Infinity and NaN are not valid floats in InfluxDB + try: + if not math.isfinite(json['fields'][key]): + del json['fields'][key] + except (KeyError, TypeError): + pass json['tags'].update(tags) diff --git a/tests/components/test_influxdb.py b/tests/components/test_influxdb.py index c909a8488be..e2323aca855 100644 --- a/tests/components/test_influxdb.py +++ b/tests/components/test_influxdb.py @@ -217,7 +217,7 @@ class TestInfluxDB(unittest.TestCase): """Test the event listener for missing units.""" self._setup() - attrs = {'bignumstring': "9" * 999} + attrs = {'bignumstring': '9' * 999, 'nonumstring': 'nan'} state = mock.MagicMock( state=8, domain='fake', entity_id='fake.entity-id', object_id='entity', attributes=attrs) From f406fd57ac52eea2f2fc0ab12e8b9c914b64f741 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 8 May 2018 20:55:35 -0400 Subject: [PATCH 13/17] Version bump to 0.69.0b3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 7d54bf6356d..60bc6a78213 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 69 -PATCH_VERSION = '0b2' +PATCH_VERSION = '0b3' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 3b39ab5b94e0692df742c12478fb4f5e27ec5a56 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 10 May 2018 17:13:00 -0400 Subject: [PATCH 14/17] Remove domain expiry sensor (#14381) --- .../components/sensor/domain_expiry.py | 76 ------------------- requirements_all.txt | 3 - 2 files changed, 79 deletions(-) delete mode 100644 homeassistant/components/sensor/domain_expiry.py diff --git a/homeassistant/components/sensor/domain_expiry.py b/homeassistant/components/sensor/domain_expiry.py deleted file mode 100644 index 9364ce041f2..00000000000 --- a/homeassistant/components/sensor/domain_expiry.py +++ /dev/null @@ -1,76 +0,0 @@ -""" -Counter for the days till domain will expire. - -For more details about this sensor please refer to the documentation at -https://home-assistant.io/components/sensor.domain_expiry/ -""" -import logging -from datetime import datetime, timedelta - -import voluptuous as vol - -import homeassistant.helpers.config_validation as cv -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import (CONF_NAME, CONF_DOMAIN) -from homeassistant.helpers.entity import Entity - -REQUIREMENTS = ['python-whois==0.6.9'] - -_LOGGER = logging.getLogger(__name__) - -DEFAULT_NAME = 'Domain Expiry' - -SCAN_INTERVAL = timedelta(hours=24) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_DOMAIN): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string -}) - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up domain expiry sensor.""" - server_name = config.get(CONF_DOMAIN) - sensor_name = config.get(CONF_NAME) - - add_devices([DomainExpiry(sensor_name, server_name)], True) - - -class DomainExpiry(Entity): - """Implementation of the domain expiry sensor.""" - - def __init__(self, sensor_name, server_name): - """Initialize the sensor.""" - self.server_name = server_name - self._name = sensor_name - self._state = None - - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def unit_of_measurement(self): - """Return the unit this state is expressed in.""" - return 'days' - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - @property - def icon(self): - """Icon to use in the frontend, if any.""" - return 'mdi:earth' - - def update(self): - """Fetch the domain information.""" - import whois - domain = whois.whois(self.server_name) - if isinstance(domain.expiration_date, datetime): - expiry = domain.expiration_date - datetime.today() - self._state = expiry.days - else: - _LOGGER.error("Cannot get expiry date for %s", self.server_name) diff --git a/requirements_all.txt b/requirements_all.txt index 6bcd267e456..0887ff98996 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1041,9 +1041,6 @@ python-velbus==2.0.11 # homeassistant.components.media_player.vlc python-vlc==1.1.2 -# homeassistant.components.sensor.domain_expiry -python-whois==0.6.9 - # homeassistant.components.wink python-wink==1.7.3 From 8c0b45af1e98e84d97d02ff6fe0657b0bb977c87 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 10 May 2018 22:40:04 -0400 Subject: [PATCH 15/17] Version bump to 0.69.0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 60bc6a78213..0f319891649 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 69 -PATCH_VERSION = '0b3' +PATCH_VERSION = '0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 6fedad7890b8f043b49b5ee22f52b059514c43cd Mon Sep 17 00:00:00 2001 From: Lukas Barth Date: Sat, 12 May 2018 10:30:21 +0200 Subject: [PATCH 16/17] Fix waiting for setup that never happens (#14346) --- homeassistant/components/matrix.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/homeassistant/components/matrix.py b/homeassistant/components/matrix.py index 569b012b484..b2805c994e8 100644 --- a/homeassistant/components/matrix.py +++ b/homeassistant/components/matrix.py @@ -114,9 +114,6 @@ class MatrixBot(object): self._listening_rooms = listening_rooms - # Logging in is deferred b/c it does I/O - self._setup_done = False - # We have to fetch the aliases for every room to make sure we don't # join it twice by accident. However, fetching aliases is costly, # so we only do it once per room. @@ -343,9 +340,5 @@ class MatrixBot(object): def handle_send_message(self, service): """Handle the send_message service.""" - if not self._setup_done: - _LOGGER.warning("Could not send message: setup is not done!") - return - self._send_message(service.data[ATTR_MESSAGE], service.data[ATTR_TARGET]) From d17186a8b708170ed4e5727a8c28d1264c8b146b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 12 May 2018 09:34:28 -0400 Subject: [PATCH 17/17] Version bump to 0.69.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 0f319891649..8de8922e4e0 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 69 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3)