diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 70a6ce0c58..9f92207b19 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -254,6 +254,7 @@ def setup_binary_sensor_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings) if CONF_INVALID_COOLDOWN in conf: cg.add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN])) + yield cg.register_component(trigger, conf) yield automation.build_automation(trigger, [], conf) for conf in config.get(CONF_ON_STATE, []): diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index ffddf83acc..f3bc91440e 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -63,6 +63,7 @@ def register_fan(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_fan(var)) + yield cg.register_component(var, config) yield setup_fan_core_(var, config) diff --git a/esphome/components/template/cover/__init__.py b/esphome/components/template/cover/__init__.py index 33b3b62870..808318ac81 100644 --- a/esphome/components/template/cover/__init__.py +++ b/esphome/components/template/cover/__init__.py @@ -7,7 +7,7 @@ from esphome.const import CONF_ASSUMED_STATE, CONF_CLOSE_ACTION, CONF_CURRENT_OP CONF_STATE, CONF_STOP_ACTION from .. import template_ns -TemplateCover = template_ns.class_('TemplateCover', cover.Cover) +TemplateCover = template_ns.class_('TemplateCover', cover.Cover, cg.Component) TemplateCoverRestoreMode = template_ns.enum('TemplateCoverRestoreMode') RESTORE_MODES = { diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index 82dc750486..a81255a254 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -285,6 +285,7 @@ def setup_time_core_(time_var, config): days_of_week = conf.get(CONF_DAYS_OF_WEEK, [x for x in range(1, 8)]) cg.add(trigger.add_days_of_week(days_of_week)) + yield cg.register_component(trigger, conf) yield automation.build_automation(trigger, [], conf) diff --git a/esphome/components/uart/switch/__init__.py b/esphome/components/uart/switch/__init__.py index 35e7877cc3..dae63a2add 100644 --- a/esphome/components/uart/switch/__init__.py +++ b/esphome/components/uart/switch/__init__.py @@ -8,7 +8,7 @@ from .. import uart_ns DEPENDENCIES = ['uart'] -UARTSwitch = uart_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevice) +UARTSwitch = uart_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevice, cg.Component) def validate_data(value): diff --git a/esphome/config.py b/esphome/config.py index ad86811f19..ac22db37ad 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -322,6 +322,7 @@ def iter_ids(config, path=None): def do_id_pass(result): # type: (Config) -> None from esphome.cpp_generator import MockObjClass + from esphome.cpp_types import Component declare_ids = [] # type: List[Tuple[core.ID, ConfigPath]] searching_ids = [] # type: List[Tuple[core.ID, ConfigPath]] @@ -340,6 +341,8 @@ def do_id_pass(result): # type: (Config) -> None # Resolve default ids after manual IDs for id, _ in declare_ids: id.resolve([v[0].id for v in declare_ids]) + if isinstance(id.type, MockObjClass) and id.type.inherits_from(Component): + CORE.component_ids.add(id.id) # Check searched IDs for id, path in searching_ids: diff --git a/esphome/core.py b/esphome/core.py index f2bc4fdf2d..7aaf6b2c70 100644 --- a/esphome/core.py +++ b/esphome/core.py @@ -505,6 +505,8 @@ class EsphomeCore(object): self.active_coroutines = {} # type: Dict[int, Any] # A set of strings of names of loaded integrations, used to find namespace ID conflicts self.loaded_integrations = set() + # A set of component IDs to track what Component subclasses are declared + self.component_ids = set() def reset(self): self.dashboard = False @@ -525,6 +527,7 @@ class EsphomeCore(object): self.defines = set() self.active_coroutines = {} self.loaded_integrations = set() + self.component_ids = set() @property def address(self): # type: () -> str @@ -626,6 +629,12 @@ class EsphomeCore(object): _LOGGER.warning(u"Please file a bug report with your configuration.") if self.active_coroutines: raise EsphomeError() + if self.component_ids: + comps = u', '.join(u"'{}'".format(x) for x in self.component_ids) + _LOGGER.warning(u"Components %s were never registered. Please create a bug report", + comps) + _LOGGER.warning(u"with your configuration.") + raise EsphomeError() self.active_coroutines.clear() def add(self, expression): diff --git a/esphome/core_config.py b/esphome/core_config.py index 5230f723af..a2663b8db8 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -18,9 +18,11 @@ from esphome.pins import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS _LOGGER = logging.getLogger(__name__) BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout'] -StartupTrigger = cg.esphome_ns.StartupTrigger -ShutdownTrigger = cg.esphome_ns.ShutdownTrigger -LoopTrigger = cg.esphome_ns.LoopTrigger +StartupTrigger = cg.esphome_ns.class_('StartupTrigger', cg.Component, automation.Trigger.template()) +ShutdownTrigger = cg.esphome_ns.class_('ShutdownTrigger', cg.Component, + automation.Trigger.template()) +LoopTrigger = cg.esphome_ns.class_('LoopTrigger', cg.Component, + automation.Trigger.template()) VERSION_REGEX = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$') diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 8e310ac44b..fd79feec1c 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -1,8 +1,9 @@ from esphome.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_SETUP_PRIORITY, \ CONF_UPDATE_INTERVAL, CONF_TYPE_ID -from esphome.core import coroutine, ID +from esphome.core import coroutine, ID, CORE from esphome.cpp_generator import RawExpression, add, get_variable from esphome.cpp_types import App, GPIOPin +from esphome.py_compat import text_type @coroutine @@ -34,6 +35,12 @@ def register_component(var, config): :param var: The variable representing the component. :param config: The configuration for the component. """ + id_ = text_type(var.base) + if id_ not in CORE.component_ids: + raise ValueError(u"Component ID {} was not declared to inherit from Component, " + u"or was registered twice. Please create a bug report with your " + u"configuration.".format(id_)) + CORE.component_ids.remove(id_) if CONF_SETUP_PRIORITY in config: add(var.set_setup_priority(config[CONF_SETUP_PRIORITY])) if CONF_UPDATE_INTERVAL in config: