From db0dd6af09f92005e0f0eb1dbc021f1f12824970 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 13 May 2019 12:03:52 +0200 Subject: [PATCH] Add custom component mechanism --- esphome/config.py | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/esphome/config.py b/esphome/config.py index d6f5735066..7ad707ee0d 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -7,6 +7,7 @@ import re import os.path # pylint: disable=unused-import, wrong-import-order +import sys from contextlib import contextmanager import voluptuous as vol @@ -17,7 +18,7 @@ from esphome.components.substitutions import CONF_SUBSTITUTIONS from esphome.const import CONF_ESPHOME, CONF_PLATFORM, ESP_PLATFORMS from esphome.core import CORE, EsphomeError # noqa from esphome.helpers import color, indent -from esphome.py_compat import text_type +from esphome.py_compat import text_type, IS_PY2 from esphome.util import safe_print, OrderedDict from typing import List, Optional, Tuple, Union # noqa @@ -106,15 +107,53 @@ class ComponentManifest(object): CORE_COMPONENTS_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'components')) +_UNDEF = object() +CUSTOM_COMPONENTS_PATH = _UNDEF + + +def _mount_config_dir(): + global CUSTOM_COMPONENTS_PATH + if CUSTOM_COMPONENTS_PATH is not _UNDEF: + return + custom_path = os.path.abspath(os.path.join(CORE.config_dir, 'custom_components')) + if not os.path.isdir(custom_path): + CUSTOM_COMPONENTS_PATH = None + return + init_path = os.path.join(custom_path, '__init__.py') + if IS_PY2 and not os.path.isfile(init_path): + _LOGGER.warning("Found 'custom_components' folder, but file __init__.py was not found. " + "ESPHome will automatically create it now....") + with open(init_path, 'w') as f: + f.write('\n') + if CORE.config_dir not in sys.path: + sys.path.insert(0, CORE.config_dir) + CUSTOM_COMPONENTS_PATH = custom_path def _lookup_module(domain, is_platform): if domain in _COMPONENT_CACHE: return _COMPONENT_CACHE[domain] - path = 'esphome.components.{}'.format(domain) + _mount_config_dir() + # First look for custom_components try: - module = importlib.import_module(path) + module = importlib.import_module('custom_components.{}'.format(domain)) + except ImportError as e: + # ImportError when no such module + if 'No module named' not in str(e): + _LOGGER.warn("Unable to import custom component %s:", domain, exc_info=True) + except Exception: # pylint: disable=broad-except + # Other error means component has an issue + _LOGGER.error("Unable to load custom component %s:", domain, exc_info=True) + return None + else: + # Found in custom components + manif = ComponentManifest(module, CUSTOM_COMPONENTS_PATH, is_platform=is_platform) + _COMPONENT_CACHE[domain] = manif + return manif + + try: + module = importlib.import_module('esphome.components.{}'.format(domain)) except ImportError as e: if 'No module named' in str(e): _LOGGER.error("Unable to import component %s:", domain)