diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 64780a340..94a5b7284 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,11 +4,10 @@ **Related issue (if applicable):** fixes **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs# -**Pull request in [esphome-core](https://github.com/esphome/esphome-core) with C++ framework changes (if applicable):** esphome/esphome-core# ## Checklist: - [ ] The code change is tested and works locally. - [ ] Tests have been added to verify that the new code works (under `tests/` folder). If user exposed functionality or configuration variables are added/changed: - - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs). + - [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs). diff --git a/esphome/__main__.py b/esphome/__main__.py index c00d6c00e..4b01b5425 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -195,8 +195,13 @@ def clean_mqtt(config, args): return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id) -def setup_log(debug=False): - log_level = logging.DEBUG if debug else logging.INFO +def setup_log(debug=False, quiet=False): + if debug: + log_level = logging.DEBUG + elif quiet: + log_level = logging.CRITICAL + else: + log_level = logging.INFO logging.basicConfig(level=log_level) fmt = "%(levelname)s %(message)s" colorfmt = "%(log_color)s{}%(reset)s".format(fmt) @@ -236,6 +241,13 @@ def command_config(args, config): return 0 +def command_vscode(args): + from esphome import vscode + + CORE.config_path = args.configuration + vscode.read_config() + + def command_compile(args, config): exit_code = write_cpp(config) if exit_code != 0: @@ -321,7 +333,8 @@ def command_dashboard(args): PRE_CONFIG_ACTIONS = { 'wizard': command_wizard, 'version': command_version, - 'dashboard': command_dashboard + 'dashboard': command_dashboard, + 'vscode': command_vscode, } POST_CONFIG_ACTIONS = { @@ -340,8 +353,9 @@ def parse_args(argv): parser = argparse.ArgumentParser(prog='esphome') parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.", action='store_true') - parser.add_argument('--dashboard', help="Internal flag to set if the command is run from the " - "dashboard.", action='store_true') + parser.add_argument('-q', '--quiet', help="Disable all esphome logs.", + action='store_true') + parser.add_argument('--dashboard', help=argparse.SUPPRESS, action='store_true') parser.add_argument('configuration', help='Your YAML configuration file.') subparsers = parser.add_subparsers(help='Commands', dest='command') @@ -404,12 +418,13 @@ def parse_args(argv): dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.", action='store_true') dashboard.add_argument("--hassio", - help="Internal flag used to tell esphome is started as a Hass.io " - "add-on.", + help=argparse.SUPPRESS, action="store_true") dashboard.add_argument("--socket", help="Make the dashboard serve under a unix socket", type=str) + subparsers.add_parser('vscode', help=argparse.SUPPRESS) + return parser.parse_args(argv[1:]) @@ -417,7 +432,7 @@ def run_esphome(argv): args = parse_args(argv) CORE.dashboard = args.dashboard - setup_log(args.verbose) + setup_log(args.verbose, args.quiet) if args.command in PRE_CONFIG_ACTIONS: try: return PRE_CONFIG_ACTIONS[args.command](args) diff --git a/esphome/automation.py b/esphome/automation.py index cabd42a45..0f1f3a0c3 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -1,15 +1,9 @@ -import copy - +import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ABOVE, CONF_ACTION_ID, CONF_AND, CONF_AUTOMATION_ID, CONF_BELOW, \ - CONF_CONDITION, CONF_CONDITION_ID, CONF_DELAY, CONF_ELSE, CONF_ID, CONF_IF, CONF_LAMBDA, \ - CONF_OR, CONF_RANGE, CONF_THEN, CONF_TRIGGER_ID, CONF_WAIT_UNTIL, CONF_WHILE +from esphome.const import CONF_AUTOMATION_ID, CONF_CONDITION, CONF_ELSE, CONF_ID, CONF_THEN, \ + CONF_TRIGGER_ID, CONF_TYPE_ID from esphome.core import coroutine -from esphome.cpp_generator import Pvariable, TemplateArguments, add, get_variable, \ - process_lambda, templatable -from esphome.cpp_types import Action, App, Component, PollingComponent, Trigger, bool_, \ - esphome_ns, float_, uint32, void -from esphome.util import ServiceRegistry +from esphome.util import Registry def maybe_simple_id(*validators): @@ -18,97 +12,49 @@ def maybe_simple_id(*validators): def validate(value): if isinstance(value, dict): return validator(value) - return validator({CONF_ID: value}) + with cv.remove_prepend_path([CONF_ID]): + return validator({CONF_ID: value}) return validate -def validate_recursive_condition(value): - is_list = isinstance(value, list) - value = cv.ensure_list()(value)[:] - for i, item in enumerate(value): - path = [i] if is_list else [] - item = copy.deepcopy(item) - if not isinstance(item, dict): - raise cv.Invalid(u"Condition must consist of key-value mapping! Got {}".format(item), - path) - key = next((x for x in item if x != CONF_CONDITION_ID), None) - if key is None: - raise cv.Invalid(u"Key missing from action! Got {}".format(item), path) - if key not in CONDITION_REGISTRY: - raise cv.Invalid(u"Unable to find condition with the name '{}', is the " - u"component loaded?".format(key), path + [key]) - item.setdefault(CONF_CONDITION_ID, None) - key2 = next((x for x in item if x not in (CONF_CONDITION_ID, key)), None) - if key2 is not None: - raise cv.Invalid(u"Cannot have two conditions in one item. Key '{}' overrides '{}'! " - u"Did you forget to indent the block inside the condition?" - u"".format(key, key2), path) - validator = CONDITION_REGISTRY[key][0] - try: - condition = validator(item[key] or {}) - except cv.Invalid as err: - err.prepend(path) - raise err - value[i] = { - CONF_CONDITION_ID: cv.declare_variable_id(Condition)(item[CONF_CONDITION_ID]), - key: condition, - } - return value +def register_action(name, action_type, schema): + return ACTION_REGISTRY.register(name, action_type, schema) -def validate_recursive_action(value): - is_list = isinstance(value, list) - if not is_list: - value = [value] - for i, item in enumerate(value): - path = [i] if is_list else [] - item = copy.deepcopy(item) - if not isinstance(item, dict): - raise cv.Invalid(u"Action must consist of key-value mapping! Got {}".format(item), - path) - key = next((x for x in item if x != CONF_ACTION_ID), None) - if key is None: - raise cv.Invalid(u"Key missing from action! Got {}".format(item), path) - if key not in ACTION_REGISTRY: - raise cv.Invalid(u"Unable to find action with the name '{}', is the component loaded?" - u"".format(key), path + [key]) - item.setdefault(CONF_ACTION_ID, None) - key2 = next((x for x in item if x not in (CONF_ACTION_ID, key)), None) - if key2 is not None: - raise cv.Invalid(u"Cannot have two actions in one item. Key '{}' overrides '{}'! " - u"Did you forget to indent the block inside the action?" - u"".format(key, key2), path) - validator = ACTION_REGISTRY[key][0] - try: - action = validator(item[key] or {}) - except cv.Invalid as err: - err.prepend(path) - raise err - value[i] = { - CONF_ACTION_ID: cv.declare_variable_id(Action)(item[CONF_ACTION_ID]), - key: action, - } - return value +def register_condition(name, condition_type, schema): + return CONDITION_REGISTRY.register(name, condition_type, schema) -ACTION_REGISTRY = ServiceRegistry() -CONDITION_REGISTRY = ServiceRegistry() +Action = cg.esphome_ns.class_('Action') +Trigger = cg.esphome_ns.class_('Trigger') +ACTION_REGISTRY = Registry() +Condition = cg.esphome_ns.class_('Condition') +CONDITION_REGISTRY = Registry() +validate_action = cv.validate_registry_entry('action', ACTION_REGISTRY) +validate_action_list = cv.validate_registry('action', ACTION_REGISTRY) +validate_condition = cv.validate_registry_entry('condition', CONDITION_REGISTRY) +validate_condition_list = cv.validate_registry('condition', CONDITION_REGISTRY) -# pylint: disable=invalid-name -DelayAction = esphome_ns.class_('DelayAction', Action, Component) -LambdaAction = esphome_ns.class_('LambdaAction', Action) -IfAction = esphome_ns.class_('IfAction', Action) -WhileAction = esphome_ns.class_('WhileAction', Action) -WaitUntilAction = esphome_ns.class_('WaitUntilAction', Action, Component) -UpdateComponentAction = esphome_ns.class_('UpdateComponentAction', Action) -Automation = esphome_ns.class_('Automation') -Condition = esphome_ns.class_('Condition') -AndCondition = esphome_ns.class_('AndCondition', Condition) -OrCondition = esphome_ns.class_('OrCondition', Condition) -RangeCondition = esphome_ns.class_('RangeCondition', Condition) -LambdaCondition = esphome_ns.class_('LambdaCondition', Condition) +def validate_potentially_and_condition(value): + if isinstance(value, list): + with cv.remove_prepend_path(['and']): + return validate_condition({ + 'and': value + }) + return validate_condition(value) + + +DelayAction = cg.esphome_ns.class_('DelayAction', Action, cg.Component) +LambdaAction = cg.esphome_ns.class_('LambdaAction', Action) +IfAction = cg.esphome_ns.class_('IfAction', Action) +WhileAction = cg.esphome_ns.class_('WhileAction', Action) +WaitUntilAction = cg.esphome_ns.class_('WaitUntilAction', Action, cg.Component) +UpdateComponentAction = cg.esphome_ns.class_('UpdateComponentAction', Action) +Automation = cg.esphome_ns.class_('Automation') + +LambdaCondition = cg.esphome_ns.class_('LambdaCondition', Condition) def validate_automation(extra_schema=None, extra_validators=None, single=False): @@ -120,24 +66,26 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): def validator_(value): if isinstance(value, list): + # List of items, there are two possible options here, either a sequence of + # actions (no then:) or a list of automations. try: # First try as a sequence of actions - return [schema({CONF_THEN: value})] + # If that succeeds, return immediately + with cv.remove_prepend_path([CONF_THEN]): + return [schema({CONF_THEN: value})] except cv.Invalid as err: - if err.path and err.path[0] == CONF_THEN: - err.path.pop(0) - # Next try as a sequence of automations try: return cv.Schema([schema])(value) except cv.Invalid as err2: - if 'Unable to find action' in str(err): + if u'Unable to find action' in str(err): raise err2 - raise cv.MultipleInvalid([err, err2]) + raise cv.MultipleInvalid([err, err2]) elif isinstance(value, dict): if CONF_THEN in value: return [schema(value)] - return [schema({CONF_THEN: value})] + with cv.remove_prepend_path([CONF_THEN]): + return [schema({CONF_THEN: value})] # This should only happen with invalid configs, but let's have a nice error message. return [schema(value)] @@ -155,175 +103,119 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): AUTOMATION_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(Trigger), - cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(Automation), - cv.Required(CONF_THEN): validate_recursive_action, + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger), + cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation), + cv.Required(CONF_THEN): validate_action_list, }) -AND_CONDITION_SCHEMA = validate_recursive_condition +AndCondition = cg.esphome_ns.class_('AndCondition', Condition) +OrCondition = cg.esphome_ns.class_('OrCondition', Condition) +NotCondition = cg.esphome_ns.class_('NotCondition', Condition) -@CONDITION_REGISTRY.register(CONF_AND, AND_CONDITION_SCHEMA) +@register_condition('and', AndCondition, validate_condition_list) def and_condition_to_code(config, condition_id, template_arg, args): - conditions = yield build_conditions(config, template_arg, args) - rhs = AndCondition.new(template_arg, conditions) - type = AndCondition.template(template_arg) - yield Pvariable(condition_id, rhs, type=type) + conditions = yield build_condition_list(config, template_arg, args) + yield cg.new_Pvariable(condition_id, template_arg, conditions) -OR_CONDITION_SCHEMA = validate_recursive_condition - - -@CONDITION_REGISTRY.register(CONF_OR, OR_CONDITION_SCHEMA) +@register_condition('or', OrCondition, validate_condition_list) def or_condition_to_code(config, condition_id, template_arg, args): - conditions = yield build_conditions(config, template_arg, args) - rhs = OrCondition.new(template_arg, conditions) - type = OrCondition.template(template_arg) - yield Pvariable(condition_id, rhs, type=type) + conditions = yield build_condition_list(config, template_arg, args) + yield cg.new_Pvariable(condition_id, template_arg, conditions) -RANGE_CONDITION_SCHEMA = cv.All(cv.Schema({ - cv.Optional(CONF_ABOVE): cv.templatable(cv.float_), - cv.Optional(CONF_BELOW): cv.templatable(cv.float_), -}), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)) +@register_condition('not', NotCondition, validate_condition) +def not_condition_to_code(config, condition_id, template_arg, args): + condition = yield build_condition(config, template_arg, args) + yield cg.new_Pvariable(condition_id, template_arg, condition) -@CONDITION_REGISTRY.register(CONF_RANGE, RANGE_CONDITION_SCHEMA) -def range_condition_to_code(config, condition_id, template_arg, args): - conditions = yield build_conditions(config, template_arg, args) - rhs = RangeCondition.new(template_arg, conditions) - type = RangeCondition.template(template_arg) - condition = Pvariable(condition_id, rhs, type=type) - if CONF_ABOVE in config: - template_ = yield templatable(config[CONF_ABOVE], args, float_) - condition.set_min(template_) - if CONF_BELOW in config: - template_ = yield templatable(config[CONF_BELOW], args, float_) - condition.set_max(template_) - yield condition +@register_condition('lambda', LambdaCondition, cv.lambda_) +def lambda_condition_to_code(config, condition_id, template_arg, args): + lambda_ = yield cg.process_lambda(config, args, return_type=bool) + yield cg.new_Pvariable(condition_id, template_arg, lambda_) -DELAY_ACTION_SCHEMA = cv.templatable(cv.positive_time_period_milliseconds) - - -@ACTION_REGISTRY.register(CONF_DELAY, DELAY_ACTION_SCHEMA) +@register_action('delay', DelayAction, cv.templatable(cv.positive_time_period_milliseconds)) def delay_action_to_code(config, action_id, template_arg, args): - rhs = App.register_component(DelayAction.new(template_arg)) - type = DelayAction.template(template_arg) - action = Pvariable(action_id, rhs, type=type) - template_ = yield templatable(config, args, uint32) - add(action.set_delay(template_)) - yield action + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_component(var, {}) + template_ = yield cg.templatable(config, args, cg.uint32) + cg.add(var.set_delay(template_)) + yield var -IF_ACTION_SCHEMA = cv.All({ - cv.Required(CONF_CONDITION): validate_recursive_condition, - cv.Optional(CONF_THEN): validate_recursive_action, - cv.Optional(CONF_ELSE): validate_recursive_action, -}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE)) - - -@ACTION_REGISTRY.register(CONF_IF, IF_ACTION_SCHEMA) +@register_action('if', IfAction, cv.All({ + cv.Required(CONF_CONDITION): validate_potentially_and_condition, + cv.Optional(CONF_THEN): validate_action_list, + cv.Optional(CONF_ELSE): validate_action_list, +}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE))) def if_action_to_code(config, action_id, template_arg, args): - conditions = yield build_conditions(config[CONF_CONDITION], template_arg, args) - rhs = IfAction.new(template_arg, conditions) - type = IfAction.template(template_arg) - action = Pvariable(action_id, rhs, type=type) + conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) + var = cg.new_Pvariable(action_id, template_arg, conditions) if CONF_THEN in config: - actions = yield build_actions(config[CONF_THEN], template_arg, args) - add(action.add_then(actions)) + actions = yield build_action_list(config[CONF_THEN], template_arg, args) + cg.add(var.add_then(actions)) if CONF_ELSE in config: - actions = yield build_actions(config[CONF_ELSE], template_arg, args) - add(action.add_else(actions)) - yield action + actions = yield build_action_list(config[CONF_ELSE], template_arg, args) + cg.add(var.add_else(actions)) + yield var -WHILE_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_CONDITION): validate_recursive_condition, - cv.Required(CONF_THEN): validate_recursive_action, -}) - - -@ACTION_REGISTRY.register(CONF_WHILE, WHILE_ACTION_SCHEMA) +@register_action('while', WhileAction, cv.Schema({ + cv.Required(CONF_CONDITION): validate_potentially_and_condition, + cv.Required(CONF_THEN): validate_action_list, +})) def while_action_to_code(config, action_id, template_arg, args): - conditions = yield build_conditions(config[CONF_CONDITION], template_arg, args) - rhs = WhileAction.new(template_arg, conditions) - type = WhileAction.template(template_arg) - action = Pvariable(action_id, rhs, type=type) - actions = yield build_actions(config[CONF_THEN], template_arg, args) - add(action.add_then(actions)) - yield action + conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) + var = cg.new_Pvariable(action_id, template_arg, conditions) + actions = yield build_action_list(config[CONF_THEN], template_arg, args) + cg.add(var.add_then(actions)) + yield var def validate_wait_until(value): schema = cv.Schema({ - cv.Required(CONF_CONDITION): validate_recursive_condition + cv.Required(CONF_CONDITION): validate_potentially_and_condition, }) if isinstance(value, dict) and CONF_CONDITION in value: return schema(value) return validate_wait_until({CONF_CONDITION: value}) -WAIT_UNTIL_ACTION_SCHEMA = validate_wait_until - - -@ACTION_REGISTRY.register(CONF_WAIT_UNTIL, WAIT_UNTIL_ACTION_SCHEMA) +@register_action('wait_until', WaitUntilAction, validate_wait_until) def wait_until_action_to_code(config, action_id, template_arg, args): - conditions = yield build_conditions(config[CONF_CONDITION], template_arg, args) - rhs = WaitUntilAction.new(template_arg, conditions) - type = WaitUntilAction.template(template_arg) - action = Pvariable(action_id, rhs, type=type) - add(App.register_component(action)) - yield action + conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) + var = cg.new_Pvariable(action_id, template_arg, conditions) + yield cg.register_component(var, {}) + yield var -LAMBDA_ACTION_SCHEMA = cv.lambda_ - - -@ACTION_REGISTRY.register(CONF_LAMBDA, LAMBDA_ACTION_SCHEMA) +@register_action('lambda', LambdaAction, cv.lambda_) def lambda_action_to_code(config, action_id, template_arg, args): - lambda_ = yield process_lambda(config, args, return_type=void) - rhs = LambdaAction.new(template_arg, lambda_) - type = LambdaAction.template(template_arg) - yield Pvariable(action_id, rhs, type=type) + lambda_ = yield cg.process_lambda(config, args, return_type=cg.void) + yield cg.new_Pvariable(action_id, template_arg, lambda_) -LAMBDA_CONDITION_SCHEMA = cv.lambda_ - - -@CONDITION_REGISTRY.register(CONF_LAMBDA, LAMBDA_CONDITION_SCHEMA) -def lambda_condition_to_code(config, condition_id, template_arg, args): - lambda_ = yield process_lambda(config, args, return_type=bool_) - rhs = LambdaCondition.new(template_arg, lambda_) - type = LambdaCondition.template(template_arg) - yield Pvariable(condition_id, rhs, type=type) - - -CONF_COMPONENT_UPDATE = 'component.update' -COMPONENT_UPDATE_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(PollingComponent), -}) - - -@ACTION_REGISTRY.register(CONF_COMPONENT_UPDATE, COMPONENT_UPDATE_ACTION_SCHEMA) +@register_action('component.update', UpdateComponentAction, maybe_simple_id({ + cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), +})) def component_update_action_to_code(config, action_id, template_arg, args): - var = yield get_variable(config[CONF_ID]) - rhs = UpdateComponentAction.new(template_arg, var) - type = UpdateComponentAction.template(template_arg) - yield Pvariable(action_id, rhs, type=type) + comp = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, comp) @coroutine def build_action(full_config, template_arg, args): - action_id = full_config[CONF_ACTION_ID] - key, config = next((k, v) for k, v in full_config.items() if k in ACTION_REGISTRY) - - builder = coroutine(ACTION_REGISTRY[key][1]) + registry_entry, config = cg.extract_registry_entry_config(ACTION_REGISTRY, full_config) + action_id = full_config[CONF_TYPE_ID] + builder = registry_entry.coroutine_fun yield builder(config, action_id, template_arg, args) @coroutine -def build_actions(config, templ, arg_type): +def build_action_list(config, templ, arg_type): actions = [] for conf in config: action = yield build_action(conf, templ, arg_type) @@ -333,15 +225,14 @@ def build_actions(config, templ, arg_type): @coroutine def build_condition(full_config, template_arg, args): - action_id = full_config[CONF_CONDITION_ID] - key, config = next((k, v) for k, v in full_config.items() if k in CONDITION_REGISTRY) - - builder = coroutine(CONDITION_REGISTRY[key][1]) + registry_entry, config = cg.extract_registry_entry_config(CONDITION_REGISTRY, full_config) + action_id = full_config[CONF_TYPE_ID] + builder = registry_entry.coroutine_fun yield builder(config, action_id, template_arg, args) @coroutine -def build_conditions(config, templ, args): +def build_condition_list(config, templ, args): conditions = [] for conf in config: condition = yield build_condition(conf, templ, args) @@ -352,10 +243,8 @@ def build_conditions(config, templ, args): @coroutine def build_automation(trigger, args, config): arg_types = [arg[0] for arg in args] - templ = TemplateArguments(*arg_types) - type = Automation.template(templ) - rhs = type.new(trigger) - obj = Pvariable(config[CONF_AUTOMATION_ID], rhs, type=type) - actions = yield build_actions(config[CONF_THEN], templ, args) - add(obj.add_actions(actions)) + templ = cg.TemplateArguments(*arg_types) + obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger) + actions = yield build_action_list(config[CONF_THEN], templ, args) + cg.add(obj.add_actions(actions)) yield obj diff --git a/esphome/codegen.py b/esphome/codegen.py index eb9738d5a..73e3f9da9 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -17,10 +17,10 @@ from esphome.cpp_generator import ( # noqa MockObjClass) from esphome.cpp_helpers import ( # noqa gpio_pin_expression, register_component, build_registry_entry, - build_registry_list) + build_registry_list, extract_registry_entry_config) from esphome.cpp_types import ( # noqa global_ns, void, nullptr, float_, bool_, std_ns, std_string, std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN, - esphome_ns, App, Nameable, Trigger, Action, Component, ComponentPtr, + esphome_ns, App, Nameable, Component, ComponentPtr, PollingComponent, Application, optional, arduino_json_ns, JsonObject, JsonObjectRef, JsonObjectConstRef, Controller, GPIOPin) diff --git a/esphome/components/a4988/a4988.h b/esphome/components/a4988/a4988.h index 8c56f2710..10fb5e001 100644 --- a/esphome/components/a4988/a4988.h +++ b/esphome/components/a4988/a4988.h @@ -9,7 +9,8 @@ namespace a4988 { class A4988 : public stepper::Stepper, public Component { public: - A4988(GPIOPin *step_pin, GPIOPin *dir_pin) : step_pin_(step_pin), dir_pin_(dir_pin) {} + void set_step_pin(GPIOPin *step_pin) { step_pin_ = step_pin; } + void set_dir_pin(GPIOPin *dir_pin) { dir_pin_ = dir_pin; } void set_sleep_pin(GPIOPin *sleep_pin) { this->sleep_pin_ = sleep_pin; } void setup() override; void dump_config() override; diff --git a/esphome/components/a4988/stepper.py b/esphome/components/a4988/stepper.py index 6f6c9ae55..29696dbd5 100644 --- a/esphome/components/a4988/stepper.py +++ b/esphome/components/a4988/stepper.py @@ -9,7 +9,7 @@ a4988_ns = cg.esphome_ns.namespace('a4988') A4988 = a4988_ns.class_('A4988', stepper.Stepper, cg.Component) CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_variable_id(A4988), + cv.Required(CONF_ID): cv.declare_id(A4988), cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema, @@ -17,12 +17,15 @@ CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({ def to_code(config): - step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN]) - dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN]) - var = cg.new_Pvariable(config[CONF_ID], step_pin, dir_pin) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield stepper.register_stepper(var, config) + step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN]) + cg.add(var.set_step_pin(step_pin)) + dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN]) + cg.add(var.set_dir_pin(dir_pin)) + if CONF_SLEEP_PIN in config: sleep_pin = yield cg.gpio_pin_expression(config[CONF_SLEEP_PIN]) cg.add(var.set_sleep_pin(sleep_pin)) diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index 6dc23a3e2..849a1f31e 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -6,9 +6,6 @@ namespace adc { static const char *TAG = "adc"; -ADCSensor::ADCSensor(const std::string &name, uint8_t pin, uint32_t update_interval) - : PollingSensorComponent(name, update_interval), pin_(pin) {} - #ifdef ARDUINO_ARCH_ESP32 void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; } #endif diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 1790ea24e..72afe5c1e 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -7,11 +7,8 @@ namespace esphome { namespace adc { -class ADCSensor : public sensor::PollingSensorComponent { +class ADCSensor : public sensor::Sensor, public PollingComponent { public: - /// Construct the ADCSensor with the provided pin and update interval in ms. - explicit ADCSensor(const std::string &name, uint8_t pin, uint32_t update_interval); - #ifdef ARDUINO_ARCH_ESP32 /// Set the attenuation for this pin. Only available on the ESP32. void set_attenuation(adc_attenuation_t attenuation); @@ -26,6 +23,7 @@ class ADCSensor : public sensor::PollingSensorComponent { void dump_config() override; /// `HARDWARE_LATE` setup priority. float get_setup_priority() const override; + void set_pin(uint8_t pin) { this->pin_ = pin; } #ifdef ARDUINO_ARCH_ESP8266 std::string unique_id() override; diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 973016b00..c369ea5be 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -2,8 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_NAME, CONF_PIN, CONF_UPDATE_INTERVAL, \ - CONF_ICON, ICON_FLASH, CONF_UNIT_OF_MEASUREMENT, UNIT_VOLT, CONF_ACCURACY_DECIMALS +from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT ATTENUATION_MODES = { '0db': cg.global_ns.ADC_0db, @@ -23,30 +22,24 @@ def validate_adc_pin(value): adc_ns = cg.esphome_ns.namespace('adc') ADCSensor = adc_ns.class_('ADCSensor', sensor.PollingSensorComponent) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(ADCSensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({ + cv.GenerateID(): cv.declare_id(ADCSensor), cv.Required(CONF_PIN): validate_adc_pin, - cv.Optional(CONF_ATTENUATION): cv.All(cv.only_on_esp32, cv.one_of(*ATTENUATION_MODES, - lower=True)), - - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, - cv.Optional(CONF_ICON, default=ICON_FLASH): sensor.icon, - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_VOLT): sensor.unit_of_measurement, - cv.Optional(CONF_ACCURACY_DECIMALS, default=2): sensor.accuracy_decimals, -}).extend(cv.COMPONENT_SCHEMA)) + cv.SplitDefault(CONF_ATTENUATION, esp32='0db'): + cv.All(cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)), +}).extend(cv.polling_component_schema('60s')) def to_code(config): - pin = config[CONF_PIN] - if pin == 'VCC': - pin = 0 + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield sensor.register_sensor(var, config) + if config[CONF_PIN] == 'VCC': cg.add_define('USE_ADC_SENSOR_VCC') cg.add_global(cg.global_ns.ADC_MODE(cg.global_ns.ADC_VCC)) - rhs = ADCSensor.new(config[CONF_NAME], pin, config[CONF_UPDATE_INTERVAL]) - adc = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(adc, config) - yield sensor.register_sensor(adc, config) + else: + cg.add(var.set_pin(config[CONF_PIN])) if CONF_ATTENUATION in config: - cg.add(adc.set_attenuation(ATTENUATION_MODES[config[CONF_ATTENUATION]])) + cg.add(var.set_attenuation(config[CONF_ATTENUATION])) diff --git a/esphome/components/ads1115/__init__.py b/esphome/components/ads1115/__init__.py index 354ea0d9d..e34bab858 100644 --- a/esphome/components/ads1115/__init__.py +++ b/esphome/components/ads1115/__init__.py @@ -11,7 +11,7 @@ ads1115_ns = cg.esphome_ns.namespace('ads1115') ADS1115Component = ads1115_ns.class_('ADS1115Component', cg.Component, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(ADS1115Component), + cv.GenerateID(): cv.declare_id(ADS1115Component), }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(None)) diff --git a/esphome/components/ads1115/ads1115.h b/esphome/components/ads1115/ads1115.h index af66e9c87..863bb247a 100644 --- a/esphome/components/ads1115/ads1115.h +++ b/esphome/components/ads1115/ads1115.h @@ -48,9 +48,6 @@ class ADS1115Component : public Component, public i2c::I2CDevice { /// Internal holder class that is in instance of Sensor so that the hub can create individual sensors. class ADS1115Sensor : public sensor::Sensor { public: - ADS1115Sensor(const std::string &name, uint32_t update_interval) - : sensor::Sensor(name), update_interval_(update_interval) {} - void set_multiplexer(ADS1115Multiplexer multiplexer); void set_gain(ADS1115Gain gain); diff --git a/esphome/components/ads1115/sensor.py b/esphome/components/ads1115/sensor.py index e8c96ba7a..4a1d109c2 100644 --- a/esphome/components/ads1115/sensor.py +++ b/esphome/components/ads1115/sensor.py @@ -2,8 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor from esphome.components.ads1115 import ADS1115Component -from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, CONF_UPDATE_INTERVAL, \ - ICON_FLASH, UNIT_VOLT, CONF_ID, CONF_NAME +from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID from esphome.py_compat import string_types from . import ads1115_ns @@ -38,27 +37,26 @@ def validate_gain(value): elif not isinstance(value, string_types): raise cv.Invalid('invalid gain "{}"'.format(value)) - return cv.one_of(*GAIN)(value) + return cv.enum(GAIN)(value) ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor) - CONF_ADS1115_ID = 'ads1115_id' -CONFIG_SCHEMA = cv.nameable(sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({ - cv.GenerateID(): cv.declare_variable_id(ADS1115Sensor), - cv.GenerateID(CONF_ADS1115_ID): cv.use_variable_id(ADS1115Component), - cv.Required(CONF_MULTIPLEXER): cv.one_of(*MUX, upper=True, space='_'), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({ + cv.GenerateID(): cv.declare_id(ADS1115Sensor), + cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component), + cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space='_'), cv.Required(CONF_GAIN): validate_gain, - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -})) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - hub = yield cg.get_variable(config[CONF_ADS1115_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) - cg.add(var.set_multiplexer(MUX[config[CONF_MULTIPLEXER]])) - cg.add(var.set_gain(GAIN[config[CONF_GAIN]])) + var = cg.new_Pvariable(config[CONF_ID]) yield sensor.register_sensor(var, config) + cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) + cg.add(var.set_gain(config[CONF_GAIN])) + + hub = yield cg.get_variable(config[CONF_ADS1115_ID]) cg.add(hub.register_sensor(var)) diff --git a/esphome/components/apds9960/__init__.py b/esphome/components/apds9960/__init__.py index 8090a4e92..4725c1603 100644 --- a/esphome/components/apds9960/__init__.py +++ b/esphome/components/apds9960/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c -from esphome.const import CONF_ID, CONF_UPDATE_INTERVAL +from esphome.const import CONF_ID DEPENDENCIES = ['i2c'] AUTO_LOAD = ['sensor', 'binary_sensor'] @@ -13,12 +13,11 @@ apds9960_nds = cg.esphome_ns.namespace('apds9960') APDS9960 = apds9960_nds.class_('APDS9960', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(APDS9960), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x39)) + cv.GenerateID(): cv.declare_id(APDS9960), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/apds9960/apds9960.h b/esphome/components/apds9960/apds9960.h index 88df6b9c9..ae44b5da0 100644 --- a/esphome/components/apds9960/apds9960.h +++ b/esphome/components/apds9960/apds9960.h @@ -10,7 +10,6 @@ namespace apds9960 { class APDS9960 : public PollingComponent, public i2c::I2CDevice { public: - APDS9960(uint32_t update_interval) : PollingComponent(update_interval) {} void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/apds9960/binary_sensor.py b/esphome/components/apds9960/binary_sensor.py index eae76c69b..440451090 100644 --- a/esphome/components/apds9960/binary_sensor.py +++ b/esphome/components/apds9960/binary_sensor.py @@ -13,11 +13,11 @@ DIRECTIONS = { 'RIGHT': 'set_right_direction', } -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True), - cv.GenerateID(CONF_APDS9960_ID): cv.use_variable_id(APDS9960), + cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), cv.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING): binary_sensor.device_class, -})) +}) def to_code(config): diff --git a/esphome/components/apds9960/sensor.py b/esphome/components/apds9960/sensor.py index 8d1bd1428..58087cbe8 100644 --- a/esphome/components/apds9960/sensor.py +++ b/esphome/components/apds9960/sensor.py @@ -1,8 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, CONF_ACCURACY_DECIMALS, CONF_ICON, \ - UNIT_PERCENT, ICON_LIGHTBULB +from esphome.const import CONF_TYPE, UNIT_PERCENT, ICON_LIGHTBULB from . import APDS9960, CONF_APDS9960_ID DEPENDENCIES = ['apds9960'] @@ -15,14 +14,10 @@ TYPES = { 'PROXIMITY': 'set_proximity', } -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1).extend({ cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True), - cv.GenerateID(CONF_APDS9960_ID): cv.use_variable_id(APDS9960), - - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PERCENT): sensor.unit_of_measurement, - cv.Optional(CONF_ACCURACY_DECIMALS, default=1): sensor.accuracy_decimals, - cv.Optional(CONF_ICON, default=ICON_LIGHTBULB): sensor.icon, -})) + cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), +}) def to_code(config): diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 1ae788306..97465f1f7 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -1,20 +1,21 @@ - -from esphome import automation -from esphome.automation import ACTION_REGISTRY, CONDITION_REGISTRY, Condition -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.automation import Condition from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \ CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID from esphome.core import CORE, coroutine_with_priority +DEPENDENCIES = ['network'] + api_ns = cg.esphome_ns.namespace('api') APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller) -HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', cg.Action) +HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action) KeyValuePair = api_ns.class_('KeyValuePair') TemplatableKeyValuePair = api_ns.class_('TemplatableKeyValuePair') APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition) -UserService = api_ns.class_('UserService', cg.Trigger) +UserService = api_ns.class_('UserService', automation.Trigger) ServiceTypeArgument = api_ns.class_('ServiceTypeArgument') ServiceArgType = api_ns.enum('ServiceArgType') SERVICE_ARG_TYPES = { @@ -30,14 +31,13 @@ SERVICE_ARG_NATIVE_TYPES = { 'string': cg.std_string, } - CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(APIServer), + cv.GenerateID(): cv.declare_id(APIServer), cv.Optional(CONF_PORT, default=6053): cv.port, cv.Optional(CONF_PASSWORD, default=''): cv.string_strict, cv.Optional(CONF_REBOOT_TIMEOUT, default='5min'): cv.positive_time_period_milliseconds, cv.Optional(CONF_SERVICES): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(UserService), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserService), cv.Required(CONF_SERVICE): cv.valid_name, cv.Optional(CONF_VARIABLES, default={}): cv.Schema({ cv.validate_id_name: cv.one_of(*SERVICE_ARG_TYPES, lower=True), @@ -48,13 +48,12 @@ CONFIG_SCHEMA = cv.Schema({ @coroutine_with_priority(40.0) def to_code(config): - rhs = APIServer.new() - api = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(api, config) + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) - cg.add(api.set_port(config[CONF_PORT])) - cg.add(api.set_password(config[CONF_PASSWORD])) - cg.add(api.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) + cg.add(var.set_port(config[CONF_PORT])) + cg.add(var.set_password(config[CONF_PASSWORD])) + cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) for conf in config.get(CONF_SERVICES, []): template_args = [] @@ -65,10 +64,9 @@ def to_code(config): template_args.append(native) func_args.append((native, name)) service_type_args.append(ServiceTypeArgument(name, SERVICE_ARG_TYPES[var_])) - func = api.make_user_service_trigger.template(*template_args) - rhs = func(conf[CONF_SERVICE], service_type_args) - type_ = UserService.template(*template_args) - trigger = cg.Pvariable(conf[CONF_TRIGGER_ID], rhs, type=type_) + templ = cg.TemplateArguments(*template_args) + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ, + conf[CONF_SERVICE], service_type_args) yield automation.build_automation(trigger, func_args, conf) cg.add_define('USE_API') @@ -78,9 +76,8 @@ def to_code(config): cg.add_library('ESPAsyncTCP', '1.2.0') -CONF_HOMEASSISTANT_SERVICE = 'homeassistant.service' HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_variable_id(APIServer), + cv.GenerateID(): cv.use_id(APIServer), cv.Required(CONF_SERVICE): cv.string, cv.Optional(CONF_DATA): cv.Schema({ cv.string: cv.string, @@ -94,34 +91,27 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({ }) -@ACTION_REGISTRY.register(CONF_HOMEASSISTANT_SERVICE, HOMEASSISTANT_SERVICE_ACTION_SCHEMA) +@automation.register_action('homeassistant.service', HomeAssistantServiceCallAction, + HOMEASSISTANT_SERVICE_ACTION_SCHEMA) def homeassistant_service_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = HomeAssistantServiceCallAction.template(template_arg) - rhs = type.new(var) - act = cg.Pvariable(action_id, rhs, type=type) - cg.add(act.set_service(config[CONF_SERVICE])) + serv = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, serv) + cg.add(var.set_service(config[CONF_SERVICE])) if CONF_DATA in config: datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA].items()] - cg.add(act.set_data(datas)) + cg.add(var.set_data(datas)) if CONF_DATA_TEMPLATE in config: datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA_TEMPLATE].items()] - cg.add(act.set_data_template(datas)) + cg.add(var.set_data_template(datas)) if CONF_VARIABLES in config: datas = [] for key, value in config[CONF_VARIABLES].items(): value_ = yield cg.process_lambda(value, []) datas.append(TemplatableKeyValuePair(key, value_)) - cg.add(act.set_variables(datas)) - yield act + cg.add(var.set_variables(datas)) + yield var -CONF_API_CONNECTED = 'api.connected' -API_CONNECTED_CONDITION_SCHEMA = cv.Schema({}) - - -@CONDITION_REGISTRY.register(CONF_API_CONNECTED, API_CONNECTED_CONDITION_SCHEMA) +@automation.register_condition('api.connected', APIConnectedCondition, {}) def api_connected_to_code(config, condition_id, template_arg, args): - rhs = APIConnectedCondition.new(template_arg) - type = APIConnectedCondition.template(template_arg) - yield cg.Pvariable(condition_id, rhs, type=type) + yield cg.new_Pvariable(condition_id, template_arg) diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 6c0662f15..2e9b9f684 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -999,7 +999,7 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin bool success = this->send_buffer(APIMessageType::SUBSCRIBE_LOGS_RESPONSE); if (!success) { - auto buffer = this->get_buffer(); + buffer = this->get_buffer(); // bool send_failed = 4; buffer.encode_bool(4, true); return this->send_buffer(APIMessageType::SUBSCRIBE_LOGS_RESPONSE); diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 0d25535b1..be75d8726 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -226,10 +226,7 @@ template class HomeAssistantServiceCallAction : public Actionresp_.set_data_template(data_template); } void set_variables(const std::vector &variables) { this->resp_.set_variables(variables); } - void play(Ts... x) override { - this->parent_->send_service_call(this->resp_); - this->play_next(x...); - } + void play(Ts... x) override { this->parent_->send_service_call(this->resp_); } protected: APIServer *parent_; diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index 6c24d3ea4..1bdabaec3 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -138,11 +138,8 @@ void BangBangClimate::set_away_config(const BangBangClimateTargetTempConfig &awa this->supports_away_ = true; this->away_config_ = away_config; } -BangBangClimate::BangBangClimate(const std::string &name) - : climate::Climate(name), - idle_trigger_(new Trigger<>()), - cool_trigger_(new Trigger<>()), - heat_trigger_(new Trigger<>()) {} +BangBangClimate::BangBangClimate() + : idle_trigger_(new Trigger<>()), cool_trigger_(new Trigger<>()), heat_trigger_(new Trigger<>()) {} void BangBangClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; } Trigger<> *BangBangClimate::get_idle_trigger() const { return this->idle_trigger_; } Trigger<> *BangBangClimate::get_cool_trigger() const { return this->cool_trigger_; } diff --git a/esphome/components/bang_bang/bang_bang_climate.h b/esphome/components/bang_bang/bang_bang_climate.h index ad07bf0e4..716655d20 100644 --- a/esphome/components/bang_bang/bang_bang_climate.h +++ b/esphome/components/bang_bang/bang_bang_climate.h @@ -19,7 +19,7 @@ struct BangBangClimateTargetTempConfig { class BangBangClimate : public climate::Climate, public Component { public: - BangBangClimate(const std::string &name); + BangBangClimate(); void setup() override; void set_sensor(sensor::Sensor *sensor); diff --git a/esphome/components/bang_bang/climate.py b/esphome/components/bang_bang/climate.py index ce20d0dbb..7837749ba 100644 --- a/esphome/components/bang_bang/climate.py +++ b/esphome/components/bang_bang/climate.py @@ -4,15 +4,15 @@ from esphome import automation from esphome.components import climate, sensor from esphome.const import CONF_AWAY_CONFIG, CONF_COOL_ACTION, \ CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION, \ - CONF_ID, CONF_IDLE_ACTION, CONF_NAME, CONF_SENSOR + CONF_ID, CONF_IDLE_ACTION, CONF_SENSOR bang_bang_ns = cg.esphome_ns.namespace('bang_bang') BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.ClimateDevice) BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig') -CONFIG_SCHEMA = cv.nameable(climate.CLIMATE_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(BangBangClimate), - cv.Required(CONF_SENSOR): cv.use_variable_id(sensor.Sensor), +CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(BangBangClimate), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True), @@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.nameable(climate.CLIMATE_SCHEMA.extend({ def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield climate.register_climate(var, config) diff --git a/esphome/components/bh1750/bh1750.cpp b/esphome/components/bh1750/bh1750.cpp index 63b6c2d38..9cd152e1e 100644 --- a/esphome/components/bh1750/bh1750.cpp +++ b/esphome/components/bh1750/bh1750.cpp @@ -8,9 +8,6 @@ static const char *TAG = "bh1750.sensor"; static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001; -BH1750Sensor::BH1750Sensor(const std::string &name, uint32_t update_interval) - : PollingSensorComponent(name, update_interval) {} - void BH1750Sensor::setup() { ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str()); if (!this->write_bytes(BH1750_COMMAND_POWER_ON, nullptr, 0)) { diff --git a/esphome/components/bh1750/bh1750.h b/esphome/components/bh1750/bh1750.h index 276b808b1..8df0bda02 100644 --- a/esphome/components/bh1750/bh1750.h +++ b/esphome/components/bh1750/bh1750.h @@ -15,10 +15,8 @@ enum BH1750Resolution { }; /// This class implements support for the i2c-based BH1750 ambient light sensor. -class BH1750Sensor : public sensor::PollingSensorComponent, public i2c::I2CDevice { +class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { public: - BH1750Sensor(const std::string &name, uint32_t update_interval); - /** Set the resolution of this sensor. * * Possible values are: diff --git a/esphome/components/bh1750/sensor.py b/esphome/components/bh1750/sensor.py index ecab63ff7..27ee3d1b8 100644 --- a/esphome/components/bh1750/sensor.py +++ b/esphome/components/bh1750/sensor.py @@ -1,8 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_NAME, CONF_RESOLUTION, CONF_UPDATE_INTERVAL, UNIT_LUX, \ - ICON_BRIGHTNESS_5 +from esphome.const import CONF_ID, CONF_RESOLUTION, UNIT_LUX, ICON_BRIGHTNESS_5 DEPENDENCIES = ['i2c'] @@ -16,17 +15,16 @@ BH1750_RESOLUTIONS = { BH1750Sensor = bh1750_ns.class_('BH1750Sensor', sensor.PollingSensorComponent, i2c.I2CDevice) -CONFIG_SCHEMA = cv.nameable(sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ - cv.GenerateID(): cv.declare_variable_id(BH1750Sensor), - cv.Optional(CONF_RESOLUTION, default=0.0): cv.one_of(*BH1750_RESOLUTIONS, float=True), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x23))) +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ + cv.GenerateID(): cv.declare_id(BH1750Sensor), + cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(BH1750_RESOLUTIONS, float=True), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x23)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) yield i2c.register_i2c_device(var, config) - cg.add(var.set_resolution(BH1750_RESOLUTIONS[config[CONF_RESOLUTION]])) + cg.add(var.set_resolution(config[CONF_RESOLUTION])) diff --git a/esphome/components/binary/fan/__init__.py b/esphome/components/binary/fan/__init__.py index 082e24630..6ba04ce35 100644 --- a/esphome/components/binary/fan/__init__.py +++ b/esphome/components/binary/fan/__init__.py @@ -1,25 +1,27 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import fan, output -from esphome.const import CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, \ - CONF_OUTPUT_ID +from esphome.const import CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_OUTPUT_ID from .. import binary_ns BinaryFan = binary_ns.class_('BinaryFan', cg.Component) -CONFIG_SCHEMA = cv.nameable(fan.FAN_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(BinaryFan), - cv.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput), - cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(output.BinaryOutput), -}).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = fan.FAN_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan), + cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), + cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - output_ = yield cg.get_variable(config[CONF_OUTPUT]) - state = yield fan.create_fan_state(config) - var = cg.new_Pvariable(config[CONF_OUTPUT_ID], state, output_) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) yield cg.register_component(var, config) + fan_ = yield fan.create_fan_state(config) + cg.add(var.set_fan(fan_)) + output_ = yield cg.get_variable(config[CONF_OUTPUT]) + cg.add(var.set_output(output_)) + if CONF_OSCILLATION_OUTPUT in config: oscillation_output = yield cg.get_variable(config[CONF_OSCILLATION_OUTPUT]) cg.add(var.set_oscillation(oscillation_output)) diff --git a/esphome/components/binary/fan/binary_fan.h b/esphome/components/binary/fan/binary_fan.h index 897352304..980d2629f 100644 --- a/esphome/components/binary/fan/binary_fan.h +++ b/esphome/components/binary/fan/binary_fan.h @@ -9,7 +9,8 @@ namespace binary { class BinaryFan : public Component { public: - BinaryFan(fan::FanState *fan, output::BinaryOutput *output) : fan_(fan), output_(output) {} + void set_fan(fan::FanState *fan) { fan_ = fan; } + void set_output(output::BinaryOutput *output) { output_ = output; } void setup() override; void loop() override; void dump_config() override; diff --git a/esphome/components/binary/light/__init__.py b/esphome/components/binary/light/__init__.py index 846fe24b8..6167ae239 100644 --- a/esphome/components/binary/light/__init__.py +++ b/esphome/components/binary/light/__init__.py @@ -6,13 +6,15 @@ from .. import binary_ns BinaryLightOutput = binary_ns.class_('BinaryLightOutput', light.LightOutput) -CONFIG_SCHEMA = cv.nameable(light.BINARY_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(BinaryLightOutput), - cv.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput), -})) +CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryLightOutput), + cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), +}) def to_code(config): - out = yield cg.get_variable(config[CONF_OUTPUT]) - var = cg.new_Pvariable(config[CONF_OUTPUT_ID], out) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) yield light.register_light(var, config) + + out = yield cg.get_variable(config[CONF_OUTPUT]) + cg.add(var.set_output(out)) diff --git a/esphome/components/binary/light/binary_light_output.h b/esphome/components/binary/light/binary_light_output.h index 1d55d0662..731973bda 100644 --- a/esphome/components/binary/light/binary_light_output.h +++ b/esphome/components/binary/light/binary_light_output.h @@ -9,7 +9,7 @@ namespace binary { class BinaryLightOutput : public light::LightOutput { public: - BinaryLightOutput(output::BinaryOutput *output) : output_(output) {} + void set_output(output::BinaryOutput *output) { output_ = output; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); traits.set_supports_brightness(false); diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 380160c55..6d0f75682 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -1,16 +1,16 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation, core -from esphome.automation import CONDITION_REGISTRY, Condition, maybe_simple_id +from esphome.automation import Condition, maybe_simple_id from esphome.components import mqtt from esphome.const import CONF_DEVICE_CLASS, CONF_FILTERS, \ CONF_ID, CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERTED, \ CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_ON_CLICK, \ CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_ON_STATE, \ - CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, CONF_FOR, CONF_VALUE, CONF_NAME, CONF_MQTT_ID + CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, CONF_FOR, CONF_NAME, CONF_MQTT_ID from esphome.core import CORE, coroutine from esphome.py_compat import string_types -from esphome.util import ServiceRegistry +from esphome.util import Registry DEVICE_CLASSES = [ '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', @@ -26,15 +26,15 @@ BinarySensor = binary_sensor_ns.class_('BinarySensor', cg.Nameable) BinarySensorPtr = BinarySensor.operator('ptr') # Triggers -PressTrigger = binary_sensor_ns.class_('PressTrigger', cg.Trigger.template()) -ReleaseTrigger = binary_sensor_ns.class_('ReleaseTrigger', cg.Trigger.template()) -ClickTrigger = binary_sensor_ns.class_('ClickTrigger', cg.Trigger.template()) -DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', cg.Trigger.template()) -MultiClickTrigger = binary_sensor_ns.class_('MultiClickTrigger', cg.Trigger.template(), +PressTrigger = binary_sensor_ns.class_('PressTrigger', automation.Trigger.template()) +ReleaseTrigger = binary_sensor_ns.class_('ReleaseTrigger', automation.Trigger.template()) +ClickTrigger = binary_sensor_ns.class_('ClickTrigger', automation.Trigger.template()) +DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', automation.Trigger.template()) +MultiClickTrigger = binary_sensor_ns.class_('MultiClickTrigger', automation.Trigger.template(), cg.Component) MultiClickTriggerEvent = binary_sensor_ns.struct('MultiClickTriggerEvent') -StateTrigger = binary_sensor_ns.class_('StateTrigger', cg.Trigger.template(bool)) -BinarySensorPublishAction = binary_sensor_ns.class_('BinarySensorPublishAction', cg.Action) +StateTrigger = binary_sensor_ns.class_('StateTrigger', automation.Trigger.template(bool)) +BinarySensorPublishAction = binary_sensor_ns.class_('BinarySensorPublishAction', automation.Action) # Condition BinarySensorCondition = binary_sensor_ns.class_('BinarySensorCondition', Condition) @@ -46,55 +46,34 @@ DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, cg.Compon InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter) LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter) -FILTER_REGISTRY = ServiceRegistry() -validate_filters = cv.validate_registry('filter', FILTER_REGISTRY, [CONF_ID]) +FILTER_REGISTRY = Registry() +validate_filters = cv.validate_registry('filter', FILTER_REGISTRY) -@FILTER_REGISTRY.register('invert', - cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(InvertFilter) - })) -def invert_filter_to_code(config): - rhs = InvertFilter.new() - var = cg.Pvariable(config[CONF_ID], rhs) +@FILTER_REGISTRY.register('invert', InvertFilter, {}) +def invert_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id) + + +@FILTER_REGISTRY.register('delayed_on', DelayedOnFilter, + cv.positive_time_period_milliseconds) +def delayed_on_filter_to_code(config, filter_id): + var = cg.new_Pvariable(filter_id, config) + yield cg.register_component(var, {}) yield var -@FILTER_REGISTRY.register('delayed_on', - cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DelayedOnFilter), - cv.Required(CONF_VALUE): cv.positive_time_period_milliseconds, - }).extend(cv.COMPONENT_SCHEMA))) -def delayed_on_filter_to_code(config): - rhs = DelayedOnFilter.new(config[CONF_VALUE]) - var = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(var, config) +@FILTER_REGISTRY.register('delayed_off', DelayedOffFilter, cv.positive_time_period_milliseconds) +def delayed_off_filter_to_code(config, filter_id): + var = cg.new_Pvariable(filter_id, config) + yield cg.register_component(var, {}) yield var -@FILTER_REGISTRY.register('delayed_off', - cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DelayedOffFilter), - cv.Required(CONF_VALUE): cv.positive_time_period_milliseconds, - }).extend(cv.COMPONENT_SCHEMA))) -def delayed_off_filter_to_code(config): - rhs = DelayedOffFilter.new(config[CONF_VALUE]) - var = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(var, config) - yield var - - -@FILTER_REGISTRY.register('lambda', - cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(LambdaFilter), - cv.Required(CONF_VALUE): cv.lambda_, - }))) -def lambda_filter_to_code(config): - lambda_ = yield cg.process_lambda(config[CONF_VALUE], [(bool, 'x')], - return_type=cg.optional.template(bool)) - rhs = LambdaFilter.new(lambda_) - var = cg.Pvariable(config[CONF_ID], rhs) - yield var +@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.lambda_) +def lambda_filter_to_code(config, filter_id): + lambda_ = yield cg.process_lambda(config, [(bool, 'x')], return_type=cg.optional.template(bool)) + yield cg.new_Pvariable(filter_id, lambda_) MULTI_CLICK_TIMING_SCHEMA = cv.Schema({ @@ -193,35 +172,35 @@ def validate_multi_click_timing(value): device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space='_') BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(BinarySensor), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTBinarySensorComponent), + cv.GenerateID(): cv.declare_id(BinarySensor), + cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTBinarySensorComponent), cv.Optional(CONF_DEVICE_CLASS): device_class, cv.Optional(CONF_FILTERS): validate_filters, cv.Optional(CONF_ON_PRESS): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PressTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger), }), cv.Optional(CONF_ON_RELEASE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ReleaseTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger), }), cv.Optional(CONF_ON_CLICK): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ClickTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger), cv.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds, cv.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds, }), cv.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(DoubleClickTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger), cv.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds, cv.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds, }), cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MultiClickTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger), cv.Required(CONF_TIMING): cv.All([parse_multi_click_timing_str], validate_multi_click_timing), cv.Optional(CONF_INVALID_COOLDOWN, default='1s'): cv.positive_time_period_milliseconds, }), cv.Optional(CONF_ON_STATE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(StateTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), }), cv.Optional(CONF_INVERTED): cv.invalid( @@ -234,6 +213,7 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ @coroutine def setup_binary_sensor_core_(var, config): + cg.add(var.set_name(config[CONF_NAME])) if CONF_INTERNAL in config: cg.add(var.set_internal(CONF_INTERNAL)) if CONF_DEVICE_CLASS in config: @@ -300,26 +280,24 @@ def new_binary_sensor(config): yield var -BINARY_SENSOR_IS_ON_OFF_CONDITION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(BinarySensor), +BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id({ + cv.Required(CONF_ID): cv.use_id(BinarySensor), cv.Optional(CONF_FOR): cv.positive_time_period_milliseconds, }) -@CONDITION_REGISTRY.register('binary_sensor.is_on', BINARY_SENSOR_IS_ON_OFF_CONDITION_SCHEMA) +@automation.register_condition('binary_sensor.is_on', BinarySensorCondition, + BINARY_SENSOR_CONDITION_SCHEMA) def binary_sensor_is_on_to_code(config, condition_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = BinarySensorCondition.template(template_arg) - rhs = type.new(var, True, config.get(CONF_FOR)) - yield cg.Pvariable(condition_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(condition_id, template_arg, paren, True, config.get(CONF_FOR)) -@CONDITION_REGISTRY.register('binary_sensor.is_off', BINARY_SENSOR_IS_ON_OFF_CONDITION_SCHEMA) +@automation.register_condition('binary_sensor.is_off', BinarySensorCondition, + BINARY_SENSOR_CONDITION_SCHEMA) def binary_sensor_is_off_to_code(config, condition_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = BinarySensorCondition.template(template_arg) - rhs = type.new(var, False, config.get(CONF_FOR)) - yield cg.Pvariable(condition_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(condition_id, template_arg, paren, False, config.get(CONF_FOR)) def to_code(config): diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index 2fdbb1ec4..47db74848 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -150,7 +150,6 @@ template class BinarySensorPublishAction : public Action void play(Ts... x) override { auto val = this->state_.value(x...); this->sensor_->publish_state(val); - this->play_next(x...); } protected: diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 3dd7004c0..51c7a57ff 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -24,12 +24,12 @@ namespace binary_sensor { */ class BinarySensor : public Nameable { public: + explicit BinarySensor(); /** Construct a binary sensor with the specified name * * @param name Name of this binary sensor. */ explicit BinarySensor(const std::string &name); - explicit BinarySensor(); /** Add a callback to be notified of state changes. * diff --git a/esphome/components/ble_presence/binary_sensor.py b/esphome/components/ble_presence/binary_sensor.py index c4cbd89f3..beab5448b 100644 --- a/esphome/components/ble_presence/binary_sensor.py +++ b/esphome/components/ble_presence/binary_sensor.py @@ -11,10 +11,10 @@ ble_presence_ns = cg.esphome_ns.namespace('ble_presence') BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor, cg.Component, ESPBTDeviceListener) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(BLEPresenceDevice), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(BLEPresenceDevice), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, -}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)) +}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index 7fb34649c..22e1c82f6 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -3,8 +3,7 @@ import esphome.config_validation as cv from esphome.components import sensor from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ ESP_BLE_DEVICE_SCHEMA -from esphome.const import CONF_MAC_ADDRESS, CONF_NAME, CONF_ID, CONF_UNIT_OF_MEASUREMENT, \ - CONF_ICON, CONF_ACCURACY_DECIMALS, UNIT_DECIBEL, ICON_SIGNAL +from esphome.const import CONF_MAC_ADDRESS, CONF_NAME, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL DEPENDENCIES = ['esp32_ble_tracker'] @@ -12,14 +11,10 @@ ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi') BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component, ESPBTDeviceListener) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(BLERSSISensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ + cv.GenerateID(): cv.declare_id(BLERSSISensor), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_DECIBEL): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_SIGNAL): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals -}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)) +}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/bme280/bme280.h b/esphome/components/bme280/bme280.h index 69ad9a741..82724d688 100644 --- a/esphome/components/bme280/bme280.h +++ b/esphome/components/bme280/bme280.h @@ -60,8 +60,6 @@ enum BME280IIRFilter { /// This class implements support for the BME280 Temperature+Pressure+Humidity i2c sensor. class BME280Component : public PollingComponent, public i2c::I2CDevice { public: - BME280Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; } void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } diff --git a/esphome/components/bme280/sensor.py b/esphome/components/bme280/sensor.py index 65b751386..651752102 100644 --- a/esphome/components/bme280/sensor.py +++ b/esphome/components/bme280/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, \ - CONF_PRESSURE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, ICON_THERMOMETER, \ + CONF_PRESSURE, CONF_TEMPERATURE, ICON_THERMOMETER, \ UNIT_CELSIUS, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -30,29 +30,28 @@ IIR_FILTER_OPTIONS = { BME280Component = bme280_ns.class_('BME280Component', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(BME280Component), - cv.Optional(CONF_TEMPERATURE): cv.nameable( + cv.GenerateID(): cv.declare_id(BME280Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), - })), - cv.Optional(CONF_PRESSURE): cv.nameable( + cv.enum(OVERSAMPLING_OPTIONS, upper=True), + }), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), - })), - cv.Optional(CONF_HUMIDITY): cv.nameable( + cv.enum(OVERSAMPLING_OPTIONS, upper=True), + }), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), - })), - cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.one_of(*IIR_FILTER_OPTIONS, upper=True), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x77)) + cv.enum(OVERSAMPLING_OPTIONS, upper=True), + }), + cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) @@ -60,18 +59,18 @@ def to_code(config): conf = config[CONF_TEMPERATURE] sens = yield sensor.new_sensor(conf) cg.add(var.set_temperature_sensor(sens)) - cg.add(var.set_temperature_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]])) + cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING])) if CONF_PRESSURE in config: conf = config[CONF_PRESSURE] sens = yield sensor.new_sensor(conf) cg.add(var.set_pressure_sensor(sens)) - cg.add(var.set_pressure_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]])) + cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING])) if CONF_HUMIDITY in config: conf = config[CONF_HUMIDITY] sens = yield sensor.new_sensor(conf) cg.add(var.set_humidity_sensor(sens)) - cg.add(var.set_humidity_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]])) + cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING])) - cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]])) + cg.add(var.set_iir_filter(config[CONF_IIR_FILTER])) diff --git a/esphome/components/bme680/bme680.h b/esphome/components/bme680/bme680.h index e69fb1dad..0671cd990 100644 --- a/esphome/components/bme680/bme680.h +++ b/esphome/components/bme680/bme680.h @@ -68,8 +68,6 @@ struct BME680CalibrationData { class BME680Component : public PollingComponent, public i2c::I2CDevice { public: - BME680Component(uint32_t update_interval) : PollingComponent(update_interval) {} - /// Set the temperature oversampling value. Defaults to 16X. void set_temperature_oversampling(BME680Oversampling temperature_oversampling); /// Set the pressure oversampling value. Defaults to 16X. diff --git a/esphome/components/bme680/sensor.py b/esphome/components/bme680/sensor.py index 826e9a5bf..64973fb91 100644 --- a/esphome/components/bme680/sensor.py +++ b/esphome/components/bme680/sensor.py @@ -4,7 +4,7 @@ from esphome import core from esphome.components import i2c, sensor from esphome.const import CONF_DURATION, CONF_GAS_RESISTANCE, CONF_HEATER, \ CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, CONF_PRESSURE, \ - CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, UNIT_OHM, ICON_GAS_CYLINDER, UNIT_CELSIUS, \ + CONF_TEMPERATURE, UNIT_OHM, ICON_GAS_CYLINDER, UNIT_CELSIUS, \ ICON_THERMOMETER, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -35,36 +35,35 @@ IIR_FILTER_OPTIONS = { BME680Component = bme680_ns.class_('BME680Component', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(BME680Component), - cv.Optional(CONF_TEMPERATURE): cv.nameable( + cv.GenerateID(): cv.declare_id(BME680Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), - })), - cv.Optional(CONF_PRESSURE): cv.nameable( + cv.enum(OVERSAMPLING_OPTIONS, upper=True), + }), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), - })), - cv.Optional(CONF_HUMIDITY): cv.nameable( + cv.enum(OVERSAMPLING_OPTIONS, upper=True), + }), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), - })), - cv.Optional(CONF_GAS_RESISTANCE): cv.nameable( - sensor.sensor_schema(UNIT_OHM, ICON_GAS_CYLINDER, 1)), - cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.one_of(*IIR_FILTER_OPTIONS, upper=True), + cv.enum(OVERSAMPLING_OPTIONS, upper=True), + }), + cv.Optional(CONF_GAS_RESISTANCE): + sensor.sensor_schema(UNIT_OHM, ICON_GAS_CYLINDER, 1), + cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), cv.Optional(CONF_HEATER): cv.Any(None, cv.All(cv.Schema({ - cv.Optional(CONF_TEMPERATURE, default=320): cv.All(cv.Coerce(int), cv.Range(200, 400)), + cv.Optional(CONF_TEMPERATURE, default=320): cv.int_range(min=200, max=400), cv.Optional(CONF_DURATION, default='150ms'): cv.All( cv.positive_time_period_milliseconds, cv.Range(max=core.TimePeriod(milliseconds=4032))) - }, cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION)))), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x76)) + }), cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION))), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x76)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) @@ -72,19 +71,19 @@ def to_code(config): conf = config[CONF_TEMPERATURE] sens = yield sensor.new_sensor(conf) cg.add(var.set_temperature_sensor(sens)) - cg.add(var.set_temperature_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]])) + cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING])) if CONF_PRESSURE in config: conf = config[CONF_PRESSURE] sens = yield sensor.new_sensor(conf) cg.add(var.set_pressure_sensor(sens)) - cg.add(var.set_pressure_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]])) + cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING])) if CONF_HUMIDITY in config: conf = config[CONF_HUMIDITY] sens = yield sensor.new_sensor(conf) cg.add(var.set_humidity_sensor(sens)) - cg.add(var.set_humidity_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]])) + cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING])) if CONF_GAS_RESISTANCE in config: conf = config[CONF_GAS_RESISTANCE] diff --git a/esphome/components/bmp085/bmp085.h b/esphome/components/bmp085/bmp085.h index 9013dad09..d84b4d43e 100644 --- a/esphome/components/bmp085/bmp085.h +++ b/esphome/components/bmp085/bmp085.h @@ -9,8 +9,6 @@ namespace bmp085 { class BMP085Component : public PollingComponent, public i2c::I2CDevice { public: - BMP085Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } void set_pressure(sensor::Sensor *pressure) { pressure_ = pressure; } diff --git a/esphome/components/bmp085/sensor.py b/esphome/components/bmp085/sensor.py index 54e326272..558c6978b 100644 --- a/esphome/components/bmp085/sensor.py +++ b/esphome/components/bmp085/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL, UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL + UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL DEPENDENCIES = ['i2c'] @@ -10,17 +10,14 @@ bmp085_ns = cg.esphome_ns.namespace('bmp085') BMP085Component = bmp085_ns.class_('BMP085Component', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(BMP085Component), - cv.Optional(CONF_TEMPERATURE): - cv.nameable(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1)), - cv.Optional(CONF_PRESSURE): - cv.nameable(sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x77)) + cv.GenerateID(): cv.declare_id(BMP085Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/bmp280/bmp280.cpp b/esphome/components/bmp280/bmp280.cpp index 20b034a99..aed9f3e51 100644 --- a/esphome/components/bmp280/bmp280.cpp +++ b/esphome/components/bmp280/bmp280.cpp @@ -233,7 +233,6 @@ uint16_t BMP280Component::read_u16_le_(uint8_t a_register) { return (data >> 8) | (data << 8); } int16_t BMP280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); } -BMP280Component::BMP280Component(uint32_t update_interval) : PollingComponent(update_interval) {} } // namespace bmp280 } // namespace esphome diff --git a/esphome/components/bmp280/bmp280.h b/esphome/components/bmp280/bmp280.h index 300943db8..f8646fb54 100644 --- a/esphome/components/bmp280/bmp280.h +++ b/esphome/components/bmp280/bmp280.h @@ -53,7 +53,6 @@ enum BMP280IIRFilter { /// This class implements support for the BMP280 Temperature+Pressure i2c sensor. class BMP280Component : public PollingComponent, public i2c::I2CDevice { public: - BMP280Component(uint32_t update_interval); void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; } diff --git a/esphome/components/bmp280/sensor.py b/esphome/components/bmp280/sensor.py index 6e73aa8eb..63c965533 100644 --- a/esphome/components/bmp280/sensor.py +++ b/esphome/components/bmp280/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL, UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL, \ + UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL, \ CONF_IIR_FILTER, CONF_OVERSAMPLING DEPENDENCIES = ['i2c'] @@ -30,24 +30,19 @@ IIR_FILTER_OPTIONS = { BMP280Component = bmp280_ns.class_('BMP280Component', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(BMP280Component), - cv.Optional(CONF_TEMPERATURE): cv.nameable( - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), - })), - cv.Optional(CONF_PRESSURE): cv.nameable( - sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.one_of(*OVERSAMPLING_OPTIONS, upper=True), - })), - cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.one_of(*IIR_FILTER_OPTIONS, upper=True), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x77)) + cv.GenerateID(): cv.declare_id(BMP280Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ + cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), + }), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ + cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), + }), + cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) @@ -55,10 +50,10 @@ def to_code(config): conf = config[CONF_TEMPERATURE] sens = yield sensor.new_sensor(conf) cg.add(var.set_temperature_sensor(sens)) - cg.add(var.set_temperature_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]])) + cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING])) if CONF_PRESSURE in config: conf = config[CONF_PRESSURE] sens = yield sensor.new_sensor(conf) cg.add(var.set_pressure_sensor(sens)) - cg.add(var.set_pressure_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]])) + cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING])) diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index 3f5a9ca28..976ce38b7 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -1,13 +1,15 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.automation import ACTION_REGISTRY +from esphome import automation from esphome.components import mqtt from esphome.const import CONF_AWAY, CONF_ID, CONF_INTERNAL, CONF_MAX_TEMPERATURE, \ CONF_MIN_TEMPERATURE, CONF_MODE, CONF_TARGET_TEMPERATURE, \ CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_LOW, CONF_TEMPERATURE_STEP, CONF_VISUAL, \ - CONF_MQTT_ID + CONF_MQTT_ID, CONF_NAME from esphome.core import CORE, coroutine +IS_PLATFORM_COMPONENT = True + climate_ns = cg.esphome_ns.namespace('climate') ClimateDevice = climate_ns.class_('Climate', cg.Nameable) @@ -23,14 +25,14 @@ CLIMATE_MODES = { 'HEAT': ClimateMode.CLIMATE_MODE_HEAT, } -validate_climate_mode = cv.one_of(*CLIMATE_MODES, upper=True) +validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True) # Actions -ControlAction = climate_ns.class_('ControlAction', cg.Action) +ControlAction = climate_ns.class_('ControlAction', automation.Action) CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(ClimateDevice), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTClimateComponent), + cv.GenerateID(): cv.declare_id(ClimateDevice), + cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTClimateComponent), cv.Optional(CONF_VISUAL, default={}): cv.Schema({ cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature, cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature, @@ -42,6 +44,7 @@ CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ @coroutine def setup_climate_core_(var, config): + cg.add(var.set_name(config[CONF_NAME])) if CONF_INTERNAL in config: cg.add(var.set_internal(config[CONF_INTERNAL])) visual = config[CONF_VISUAL] @@ -66,7 +69,7 @@ def register_climate(var, config): CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(ClimateDevice), + cv.Required(CONF_ID): cv.use_id(ClimateDevice), cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode), cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature), cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature), @@ -75,29 +78,26 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema({ }) -@ACTION_REGISTRY.register('climate.control', CLIMATE_CONTROL_ACTION_SCHEMA) +@automation.register_action('climate.control', ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA) def climate_control_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ControlAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) if CONF_MODE in config: - template_ = yield cg.templatable(config[CONF_MODE], args, ClimateMode, - to_exp=CLIMATE_MODES) - cg.add(action.set_mode(template_)) + template_ = yield cg.templatable(config[CONF_MODE], args, ClimateMode) + cg.add(var.set_mode(template_)) if CONF_TARGET_TEMPERATURE in config: template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float) - cg.add(action.set_target_temperature(template_)) + cg.add(var.set_target_temperature(template_)) if CONF_TARGET_TEMPERATURE_LOW in config: template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE_LOW], args, float) - cg.add(action.set_target_temperature_low(template_)) + cg.add(var.set_target_temperature_low(template_)) if CONF_TARGET_TEMPERATURE_HIGH in config: template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE_HIGH], args, float) - cg.add(action.set_target_temperature_high(template_)) + cg.add(var.set_target_temperature_high(template_)) if CONF_AWAY in config: template_ = yield cg.templatable(config[CONF_AWAY], args, bool) - cg.add(action.set_away(template_)) - yield action + cg.add(var.set_away(template_)) + yield var def to_code(config): diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 0eba29da5..102d700d5 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -114,10 +114,10 @@ struct ClimateDeviceRestoreState { */ class Climate : public Nameable { public: - /// Construct a climate device with a name. - Climate(const std::string &name); /// Construct a climate device with empty name (will be set later). Climate(); + /// Construct a climate device with a name. + Climate(const std::string &name); /// The active mode of the climate device. ClimateMode mode{CLIMATE_MODE_OFF}; diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 6abe57170..041be0a2b 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -1,9 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.automation import ACTION_REGISTRY, maybe_simple_id, Condition +from esphome import automation +from esphome.automation import maybe_simple_id, Condition from esphome.components import mqtt from esphome.const import CONF_ID, CONF_INTERNAL, CONF_DEVICE_CLASS, CONF_STATE, \ - CONF_POSITION, CONF_TILT, CONF_STOP, CONF_MQTT_ID + CONF_POSITION, CONF_TILT, CONF_STOP, CONF_MQTT_ID, CONF_NAME from esphome.core import CORE, coroutine IS_PLATFORM_COMPONENT = True @@ -24,7 +25,7 @@ COVER_STATES = { 'OPEN': COVER_OPEN, 'CLOSED': COVER_CLOSED, } -validate_cover_state = cv.one_of(*COVER_STATES, upper=True) +validate_cover_state = cv.enum(COVER_STATES, upper=True) CoverOperation = cover_ns.enum('CoverOperation') COVER_OPERATIONS = { @@ -32,20 +33,20 @@ COVER_OPERATIONS = { 'OPENING': CoverOperation.COVER_OPERATION_OPENING, 'CLOSING': CoverOperation.COVER_OPERATION_CLOSING, } -validate_cover_operation = cv.one_of(*COVER_OPERATIONS, upper=True) +validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True) # Actions -OpenAction = cover_ns.class_('OpenAction', cg.Action) -CloseAction = cover_ns.class_('CloseAction', cg.Action) -StopAction = cover_ns.class_('StopAction', cg.Action) -ControlAction = cover_ns.class_('ControlAction', cg.Action) -CoverPublishAction = cover_ns.class_('CoverPublishAction', cg.Action) +OpenAction = cover_ns.class_('OpenAction', automation.Action) +CloseAction = cover_ns.class_('CloseAction', automation.Action) +StopAction = cover_ns.class_('StopAction', automation.Action) +ControlAction = cover_ns.class_('ControlAction', automation.Action) +CoverPublishAction = cover_ns.class_('CoverPublishAction', automation.Action) CoverIsOpenCondition = cover_ns.class_('CoverIsOpenCondition', Condition) CoverIsClosedCondition = cover_ns.class_('CoverIsClosedCondition', Condition) COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(Cover), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTCoverComponent), + cv.GenerateID(): cv.declare_id(Cover), + cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTCoverComponent), cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True), # TODO: MQTT topic options }) @@ -53,6 +54,7 @@ COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ @coroutine def setup_cover_core_(var, config): + cg.add(var.set_name(config[CONF_NAME])) if CONF_INTERNAL in config: cg.add(var.set_internal(config[CONF_INTERNAL])) if CONF_DEVICE_CLASS in config: @@ -72,63 +74,54 @@ def register_cover(var, config): COVER_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(Cover), + cv.Required(CONF_ID): cv.use_id(Cover), }) -@ACTION_REGISTRY.register('cover.open', COVER_ACTION_SCHEMA) +@automation.register_action('cover.open', OpenAction, COVER_ACTION_SCHEMA) def cover_open_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = OpenAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('cover.close', COVER_ACTION_SCHEMA) +@automation.register_action('cover.close', CloseAction, COVER_ACTION_SCHEMA) def cover_close_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = CloseAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('cover.stop', COVER_ACTION_SCHEMA) +@automation.register_action('cover.stop', StopAction, COVER_ACTION_SCHEMA) def cover_stop_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = StopAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) COVER_CONTROL_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(Cover), + cv.Required(CONF_ID): cv.use_id(Cover), cv.Optional(CONF_STOP): cv.templatable(cv.boolean), - cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(cv.one_of(*COVER_STATES)), + cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(validate_cover_state), cv.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.percentage), cv.Optional(CONF_TILT): cv.templatable(cv.percentage), }) -@ACTION_REGISTRY.register('cover.control', COVER_CONTROL_ACTION_SCHEMA) +@automation.register_action('cover.control', ControlAction, COVER_CONTROL_ACTION_SCHEMA) def cover_control_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = StopAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) if CONF_STOP in config: template_ = yield cg.templatable(config[CONF_STOP], args, bool) - cg.add(action.set_stop(template_)) + cg.add(var.set_stop(template_)) if CONF_STATE in config: - template_ = yield cg.templatable(config[CONF_STATE], args, float, - to_exp=COVER_STATES) - cg.add(action.set_position(template_)) + template_ = yield cg.templatable(config[CONF_STATE], args, float) + cg.add(var.set_position(template_)) if CONF_POSITION in config: template_ = yield cg.templatable(config[CONF_POSITION], args, float) - cg.add(action.set_position(template_)) + cg.add(var.set_position(template_)) if CONF_TILT in config: template_ = yield cg.templatable(config[CONF_TILT], args, float) - cg.add(action.set_tilt(template_)) - yield action + cg.add(var.set_tilt(template_)) + yield var def to_code(config): diff --git a/esphome/components/cover/automation.h b/esphome/components/cover/automation.h index 296760263..a8eb0cdf9 100644 --- a/esphome/components/cover/automation.h +++ b/esphome/components/cover/automation.h @@ -11,10 +11,7 @@ template class OpenAction : public Action { public: explicit OpenAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { - this->cover_->open(); - this->play_next(x...); - } + void play(Ts... x) override { this->cover_->open(); } protected: Cover *cover_; @@ -24,10 +21,7 @@ template class CloseAction : public Action { public: explicit CloseAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { - this->cover_->close(); - this->play_next(x...); - } + void play(Ts... x) override { this->cover_->close(); } protected: Cover *cover_; @@ -37,10 +31,7 @@ template class StopAction : public Action { public: explicit StopAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { - this->cover_->stop(); - this->play_next(x...); - } + void play(Ts... x) override { this->cover_->stop(); } protected: Cover *cover_; @@ -59,7 +50,6 @@ template class ControlAction : public Action { if (this->tilt_.has_value()) call.set_tilt(this->tilt_.value(x...)); call.perform(); - this->play_next(x...); } TEMPLATABLE_VALUE(bool, stop) @@ -81,7 +71,6 @@ template class CoverPublishAction : public Action { if (this->current_operation_.has_value()) this->cover_->current_operation = this->current_operation_.value(x...); this->cover_->publish_state(); - this->play_next(x...); } TEMPLATABLE_VALUE(float, position) diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index bace869ba..12011e1b4 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -105,8 +105,8 @@ const char *cover_operation_to_str(CoverOperation op); */ class Cover : public Nameable { public: - explicit Cover(const std::string &name); explicit Cover(); + explicit Cover(const std::string &name); /// The current operation of the cover (idle, opening, closing). CoverOperation current_operation{COVER_OPERATION_IDLE}; diff --git a/esphome/components/cse7766/cse7766.h b/esphome/components/cse7766/cse7766.h index c7ed4d4e5..6cacfee07 100644 --- a/esphome/components/cse7766/cse7766.h +++ b/esphome/components/cse7766/cse7766.h @@ -9,8 +9,6 @@ namespace cse7766 { class CSE7766Component : public PollingComponent, public uart::UARTDevice { public: - CSE7766Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; } void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } diff --git a/esphome/components/cse7766/sensor.py b/esphome/components/cse7766/sensor.py index 6de12266c..a415d6768 100644 --- a/esphome/components/cse7766/sensor.py +++ b/esphome/components/cse7766/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_UPDATE_INTERVAL, CONF_VOLTAGE, \ +from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT DEPENDENCIES = ['uart'] @@ -10,18 +10,16 @@ cse7766_ns = cg.esphome_ns.namespace('cse7766') CSE7766Component = cse7766_ns.class_('CSE7766Component', cg.PollingComponent, uart.UARTDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CSE7766Component), + cv.GenerateID(): cv.declare_id(CSE7766Component), - cv.Optional(CONF_VOLTAGE): cv.nameable(sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1)), - cv.Optional(CONF_CURRENT): cv.nameable( - sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2)), - cv.Optional(CONF_POWER): cv.nameable(sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), +}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield uart.register_uart_device(var, config) diff --git a/esphome/components/custom/binary_sensor/__init__.py b/esphome/components/custom/binary_sensor/__init__.py index afcb577f3..b08988cc5 100644 --- a/esphome/components/custom/binary_sensor/__init__.py +++ b/esphome/components/custom/binary_sensor/__init__.py @@ -1,16 +1,15 @@ -from esphome.components import binary_sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA, CONF_NAME +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA from .. import custom_ns CustomBinarySensorConstructor = custom_ns.class_('CustomBinarySensorConstructor') CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CustomBinarySensorConstructor), + cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor), cv.Required(CONF_LAMBDA): cv.lambda_, - cv.Required(CONF_BINARY_SENSORS): - cv.ensure_list(cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA)), + cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(binary_sensor.BINARY_SENSOR_SCHEMA), }) @@ -22,5 +21,4 @@ def to_code(config): custom = cg.variable(config[CONF_ID], rhs) for i, conf in enumerate(config[CONF_BINARY_SENSORS]): rhs = custom.Pget_binary_sensor(i) - cg.add(rhs.set_name(conf[CONF_NAME])) yield binary_sensor.register_binary_sensor(rhs, conf) diff --git a/esphome/components/custom/output/__init__.py b/esphome/components/custom/output/__init__.py index 0b60df6ff..3266cbda9 100644 --- a/esphome/components/custom/output/__init__.py +++ b/esphome/components/custom/output/__init__.py @@ -8,22 +8,22 @@ CustomBinaryOutputConstructor = custom_ns.class_('CustomBinaryOutputConstructor' CustomFloatOutputConstructor = custom_ns.class_('CustomFloatOutputConstructor') BINARY_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CustomBinaryOutputConstructor), + cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor), cv.Required(CONF_LAMBDA): cv.lambda_, cv.Required(CONF_TYPE): 'binary', cv.Required(CONF_OUTPUTS): cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(output.BinaryOutput), + cv.GenerateID(): cv.declare_id(output.BinaryOutput), })), }) FLOAT_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CustomFloatOutputConstructor), + cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor), cv.Required(CONF_LAMBDA): cv.lambda_, cv.Required(CONF_TYPE): 'float', cv.Required(CONF_OUTPUTS): cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(output.FloatOutput), + cv.GenerateID(): cv.declare_id(output.FloatOutput), })), }) diff --git a/esphome/components/custom/sensor/__init__.py b/esphome/components/custom/sensor/__init__.py index 7ed12c0ea..6503cc0c6 100644 --- a/esphome/components/custom/sensor/__init__.py +++ b/esphome/components/custom/sensor/__init__.py @@ -1,17 +1,15 @@ -from esphome.components import sensor -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_SENSORS from .. import custom_ns CustomSensorConstructor = custom_ns.class_('CustomSensorConstructor') CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CustomSensorConstructor), + cv.GenerateID(): cv.declare_id(CustomSensorConstructor), cv.Required(CONF_LAMBDA): cv.lambda_, - cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(sensor.Sensor), - })), + cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA), }) diff --git a/esphome/components/custom/switch/__init__.py b/esphome/components/custom/switch/__init__.py index 3a7c7499b..b0da14b4f 100644 --- a/esphome/components/custom/switch/__init__.py +++ b/esphome/components/custom/switch/__init__.py @@ -8,11 +8,11 @@ from .. import custom_ns CustomSwitchConstructor = custom_ns.class_('CustomSwitchConstructor') CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CustomSwitchConstructor), + cv.GenerateID(): cv.declare_id(CustomSwitchConstructor), cv.Required(CONF_LAMBDA): cv.lambda_, cv.Required(CONF_SWITCHES): cv.ensure_list(switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(switch.Switch), + cv.GenerateID(): cv.declare_id(switch.Switch), })), }) diff --git a/esphome/components/custom/text_sensor/__init__.py b/esphome/components/custom/text_sensor/__init__.py index 601392fac..40b8be8d7 100644 --- a/esphome/components/custom/text_sensor/__init__.py +++ b/esphome/components/custom/text_sensor/__init__.py @@ -7,11 +7,11 @@ from .. import custom_ns CustomTextSensorConstructor = custom_ns.class_('CustomTextSensorConstructor') CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CustomTextSensorConstructor), + cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor), cv.Required(CONF_LAMBDA): cv.lambda_, cv.Required(CONF_TEXT_SENSORS): cv.ensure_list(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(text_sensor.TextSensor), + cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), })), }) diff --git a/esphome/components/custom_component/__init__.py b/esphome/components/custom_component/__init__.py index 0bc2eea2c..0dcbb163c 100644 --- a/esphome/components/custom_component/__init__.py +++ b/esphome/components/custom_component/__init__.py @@ -7,10 +7,10 @@ CustomComponentConstructor = custom_component_ns.class_('CustomComponentConstruc MULTI_CONF = True CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CustomComponentConstructor), + cv.GenerateID(): cv.declare_id(CustomComponentConstructor), cv.Required(CONF_LAMBDA): cv.lambda_, cv.Optional(CONF_COMPONENTS): cv.ensure_list(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(cg.Component) + cv.GenerateID(): cv.declare_id(cg.Component) }).extend(cv.COMPONENT_SCHEMA)), }) diff --git a/esphome/components/cwww/cwww_light_output.h b/esphome/components/cwww/cwww_light_output.h index db042a9a0..4497d051e 100644 --- a/esphome/components/cwww/cwww_light_output.h +++ b/esphome/components/cwww/cwww_light_output.h @@ -9,12 +9,10 @@ namespace cwww { class CWWWLightOutput : public light::LightOutput { public: - CWWWLightOutput(output::FloatOutput *cold_white, output::FloatOutput *warm_white, float cold_white_temperature, - float warm_white_temperature) - : cold_white_(cold_white), - warm_white_(warm_white), - cold_white_temperature_(cold_white_temperature), - warm_white_temperature_(warm_white_temperature) {} + void set_cold_white(output::FloatOutput *cold_white) { cold_white_ = cold_white; } + void set_warm_white(output::FloatOutput *warm_white) { warm_white_ = warm_white; } + void set_cold_white_temperature(float cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; } + void set_warm_white_temperature(float warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); traits.set_supports_brightness(true); diff --git a/esphome/components/cwww/light.py b/esphome/components/cwww/light.py index 5c3a0e01f..5d2b4ab2c 100644 --- a/esphome/components/cwww/light.py +++ b/esphome/components/cwww/light.py @@ -7,19 +7,22 @@ from esphome.const import CONF_OUTPUT_ID, CONF_COLD_WHITE, CONF_WARM_WHITE, \ cwww_ns = cg.esphome_ns.namespace('cwww') CWWWLightOutput = cwww_ns.class_('CWWWLightOutput', light.LightOutput) -CONFIG_SCHEMA = cv.nameable(light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(CWWWLightOutput), - cv.Required(CONF_COLD_WHITE): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_WARM_WHITE): cv.use_variable_id(output.FloatOutput), +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CWWWLightOutput), + cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, -})) +}) def to_code(config): - cwhite = yield cg.get_variable(config[CONF_COLD_WHITE]) - wwhite = yield cg.get_variable(config[CONF_WARM_WHITE]) - var = cg.new_Pvariable(config[CONF_OUTPUT_ID], cwhite, wwhite, - config[CONF_COLD_WHITE_COLOR_TEMPERATURE], - config[CONF_WARM_WHITE_COLOR_TEMPERATURE]) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) yield light.register_light(var, config) + cwhite = yield cg.get_variable(config[CONF_COLD_WHITE]) + cg.add(var.set_cold_white(cwhite)) + cg.add(var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE])) + + wwhite = yield cg.get_variable(config[CONF_WARM_WHITE]) + cg.add(var.set_warm_white(wwhite)) + cg.add(var.set_warm_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE])) diff --git a/esphome/components/dallas/__init__.py b/esphome/components/dallas/__init__.py index e523e59a4..85ab4300e 100644 --- a/esphome/components/dallas/__init__.py +++ b/esphome/components/dallas/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.const import CONF_ID, CONF_PIN, CONF_UPDATE_INTERVAL +from esphome.const import CONF_ID, CONF_PIN MULTI_CONF = True AUTO_LOAD = ['sensor'] @@ -12,15 +12,14 @@ DallasComponent = dallas_ns.class_('DallasComponent', cg.PollingComponent) ESPOneWire = dallas_ns.class_('ESPOneWire') CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DallasComponent), - cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_variable_id(ESPOneWire), + cv.GenerateID(): cv.declare_id(DallasComponent), + cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire), cv.Required(CONF_PIN): pins.gpio_input_pin_schema, - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA) +}).extend(cv.polling_component_schema('60s')) def to_code(config): pin = yield cg.gpio_pin_expression(config[CONF_PIN]) one_wire = cg.new_Pvariable(config[CONF_ONE_WIRE_ID], pin) - var = cg.new_Pvariable(config[CONF_ID], one_wire, config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID], one_wire) yield cg.register_component(var, config) diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index ce3a22e75..1d3e693ff 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -95,15 +95,13 @@ void DallasComponent::dump_config() { } } -DallasTemperatureSensor *DallasComponent::get_sensor_by_address(const std::string &name, uint64_t address, - uint8_t resolution) { - auto s = new DallasTemperatureSensor(name, address, resolution, this); +DallasTemperatureSensor *DallasComponent::get_sensor_by_address(uint64_t address, uint8_t resolution) { + auto s = new DallasTemperatureSensor(address, resolution, this); this->sensors_.push_back(s); return s; } -DallasTemperatureSensor *DallasComponent::get_sensor_by_index(const std::string &name, uint8_t index, - uint8_t resolution) { - auto s = this->get_sensor_by_address(name, 0, resolution); +DallasTemperatureSensor *DallasComponent::get_sensor_by_index(uint8_t index, uint8_t resolution) { + auto s = this->get_sensor_by_address(0, resolution); s->set_index(index); return s; } @@ -148,12 +146,10 @@ void DallasComponent::update() { }); } } -DallasComponent::DallasComponent(ESPOneWire *one_wire, uint32_t update_interval) - : PollingComponent(update_interval), one_wire_(one_wire) {} +DallasComponent::DallasComponent(ESPOneWire *one_wire) : one_wire_(one_wire) {} -DallasTemperatureSensor::DallasTemperatureSensor(const std::string &name, uint64_t address, uint8_t resolution, - DallasComponent *parent) - : sensor::Sensor(name), parent_(parent) { +DallasTemperatureSensor::DallasTemperatureSensor(uint64_t address, uint8_t resolution, DallasComponent *parent) + : parent_(parent) { this->set_address(address); this->set_resolution(resolution); } diff --git a/esphome/components/dallas/dallas_component.h b/esphome/components/dallas/dallas_component.h index db7cb1159..d32aec175 100644 --- a/esphome/components/dallas/dallas_component.h +++ b/esphome/components/dallas/dallas_component.h @@ -11,10 +11,10 @@ class DallasTemperatureSensor; class DallasComponent : public PollingComponent { public: - explicit DallasComponent(ESPOneWire *one_wire, uint32_t update_interval); + explicit DallasComponent(ESPOneWire *one_wire); - DallasTemperatureSensor *get_sensor_by_address(const std::string &name, uint64_t address, uint8_t resolution); - DallasTemperatureSensor *get_sensor_by_index(const std::string &name, uint8_t index, uint8_t resolution); + DallasTemperatureSensor *get_sensor_by_address(uint64_t address, uint8_t resolution); + DallasTemperatureSensor *get_sensor_by_index(uint8_t index, uint8_t resolution); void setup() override; void dump_config() override; @@ -33,7 +33,7 @@ class DallasComponent : public PollingComponent { /// Internal class that helps us create multiple sensors for one Dallas hub. class DallasTemperatureSensor : public sensor::Sensor { public: - DallasTemperatureSensor(const std::string &name, uint64_t address, uint8_t resolution, DallasComponent *parent); + DallasTemperatureSensor(uint64_t address, uint8_t resolution, DallasComponent *parent); /// Helper to get a pointer to the address as uint8_t. uint8_t *get_address8(); diff --git a/esphome/components/dallas/sensor.py b/esphome/components/dallas/sensor.py index 1f25698c1..2236f919f 100644 --- a/esphome/components/dallas/sensor.py +++ b/esphome/components/dallas/sensor.py @@ -1,24 +1,19 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_NAME, \ - CONF_RESOLUTION, CONF_UNIT_OF_MEASUREMENT, UNIT_CELSIUS, CONF_ICON, ICON_THERMOMETER, \ - CONF_ACCURACY_DECIMALS, CONF_ID +from esphome.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, UNIT_CELSIUS, \ + ICON_THERMOMETER, CONF_ID from . import DallasComponent, dallas_ns DallasTemperatureSensor = dallas_ns.class_('DallasTemperatureSensor', sensor.Sensor) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(DallasTemperatureSensor), - cv.GenerateID(CONF_DALLAS_ID): cv.use_variable_id(DallasComponent), +CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ + cv.GenerateID(): cv.declare_id(DallasTemperatureSensor), + cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent), cv.Optional(CONF_ADDRESS): cv.hex_int, cv.Optional(CONF_INDEX): cv.positive_int, cv.Optional(CONF_RESOLUTION, default=12): cv.All(cv.int_, cv.Range(min=9, max=12)), - - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_CELSIUS): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_THERMOMETER): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=1): sensor.accuracy_decimals, }), cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX)) @@ -26,9 +21,8 @@ def to_code(config): hub = yield cg.get_variable(config[CONF_DALLAS_ID]) if CONF_ADDRESS in config: address = config[CONF_ADDRESS] - rhs = hub.Pget_sensor_by_address(config[CONF_NAME], address, config.get(CONF_RESOLUTION)) + rhs = hub.Pget_sensor_by_address(address, config.get(CONF_RESOLUTION)) else: - rhs = hub.Pget_sensor_by_index(config[CONF_NAME], config[CONF_INDEX], - config.get(CONF_RESOLUTION)) + rhs = hub.Pget_sensor_by_index(config[CONF_INDEX], config.get(CONF_RESOLUTION)) var = cg.Pvariable(config[CONF_ID], rhs) yield sensor.register_sensor(var, config) diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index 681317f26..a40dadb5c 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -7,11 +7,10 @@ DEPENDENCIES = ['logger'] debug_ns = cg.esphome_ns.namespace('debug') DebugComponent = debug_ns.class_('DebugComponent', cg.Component) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DebugComponent), + cv.GenerateID(): cv.declare_id(DebugComponent), }).extend(cv.COMPONENT_SCHEMA) def to_code(config): - rhs = DebugComponent.new() - var = cg.Pvariable(config[CONF_ID], rhs) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index b5f42528b..93ef04d19 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -1,7 +1,7 @@ -from esphome import pins -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.automation import ACTION_REGISTRY, maybe_simple_id +import esphome.config_validation as cv +from esphome import pins, automation +from esphome.automation import maybe_simple_id from esphome.const import CONF_ID, CONF_MODE, CONF_NUMBER, CONF_PINS, CONF_RUN_CYCLES, \ CONF_RUN_DURATION, CONF_SLEEP_DURATION, CONF_WAKEUP_PIN @@ -16,8 +16,8 @@ def validate_pin_number(value): deep_sleep_ns = cg.esphome_ns.namespace('deep_sleep') DeepSleepComponent = deep_sleep_ns.class_('DeepSleepComponent', cg.Component) -EnterDeepSleepAction = deep_sleep_ns.class_('EnterDeepSleepAction', cg.Action) -PreventDeepSleepAction = deep_sleep_ns.class_('PreventDeepSleepAction', cg.Action) +EnterDeepSleepAction = deep_sleep_ns.class_('EnterDeepSleepAction', automation.Action) +PreventDeepSleepAction = deep_sleep_ns.class_('PreventDeepSleepAction', automation.Action) WakeupPinMode = deep_sleep_ns.enum('WakeupPinMode') WAKEUP_PIN_MODES = { @@ -37,17 +37,17 @@ CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode' CONF_ESP32_EXT1_WAKEUP = 'esp32_ext1_wakeup' CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DeepSleepComponent), + cv.GenerateID(): cv.declare_id(DeepSleepComponent), cv.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds, cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, cv.Optional(CONF_WAKEUP_PIN): cv.All(cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number), cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(cv.only_on_esp32, - cv.one_of(*WAKEUP_PIN_MODES), upper=True), + cv.enum(WAKEUP_PIN_MODES), upper=True), cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All(cv.only_on_esp32, cv.Schema({ cv.Required(CONF_PINS): cv.ensure_list(pins.shorthand_input_pin, validate_pin_number), - cv.Required(CONF_MODE): cv.one_of(*EXT1_WAKEUP_MODES, upper=True), + cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True), })), cv.Optional(CONF_RUN_CYCLES): cv.invalid("The run_cycles option has been removed in 1.11.0 as " @@ -66,7 +66,7 @@ def to_code(config): pin = yield cg.gpio_pin_expression(config[CONF_WAKEUP_PIN]) cg.add(var.set_wakeup_pin(pin)) if CONF_WAKEUP_PIN_MODE in config: - cg.add(var.set_wakeup_pin_mode(WAKEUP_PIN_MODES[config[CONF_WAKEUP_PIN_MODE]])) + cg.add(var.set_wakeup_pin_mode(config[CONF_WAKEUP_PIN_MODE])) if CONF_RUN_DURATION in config: cg.add(var.set_run_duration(config[CONF_RUN_DURATION])) @@ -78,7 +78,7 @@ def to_code(config): struct = cg.StructInitializer( Ext1Wakeup, ('mask', mask), - ('wakeup_mode', EXT1_WAKEUP_MODES[conf[CONF_MODE]]) + ('wakeup_mode', conf[CONF_MODE]) ) cg.add(var.set_ext1_wakeup(struct)) @@ -86,21 +86,17 @@ def to_code(config): DEEP_SLEEP_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(DeepSleepComponent), + cv.GenerateID(): cv.use_id(DeepSleepComponent), }) -@ACTION_REGISTRY.register('deep_sleep.enter', DEEP_SLEEP_ACTION_SCHEMA) +@automation.register_action('deep_sleep.enter', EnterDeepSleepAction, DEEP_SLEEP_ACTION_SCHEMA) def deep_sleep_enter_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = EnterDeepSleepAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('deep_sleep.prevent', DEEP_SLEEP_ACTION_SCHEMA) +@automation.register_action('deep_sleep.prevent', PreventDeepSleepAction, DEEP_SLEEP_ACTION_SCHEMA) def deep_sleep_prevent_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = PreventDeepSleepAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index d1af7a814..4372a3f66 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -85,10 +85,7 @@ template class EnterDeepSleepAction : public Action { public: EnterDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {} - void play(Ts... x) override { - this->deep_sleep_->begin_sleep(true); - this->play_next(x...); - } + void play(Ts... x) override { this->deep_sleep_->begin_sleep(true); } protected: DeepSleepComponent *deep_sleep_; @@ -98,10 +95,7 @@ template class PreventDeepSleepAction : public Action { public: PreventDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {} - void play(Ts... x) override { - this->deep_sleep_->prevent_deep_sleep(); - this->play_next(x...); - } + void play(Ts... x) override { this->deep_sleep_->prevent_deep_sleep(); } protected: DeepSleepComponent *deep_sleep_; diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 03c3ddba0..79732bb26 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -7,12 +7,6 @@ namespace dht { static const char *TAG = "dht"; -DHT::DHT(const std::string &temperature_name, const std::string &humidity_name, GPIOPin *pin, uint32_t update_interval) - : PollingComponent(update_interval), - pin_(pin), - temperature_sensor_(new sensor::Sensor(temperature_name)), - humidity_sensor_(new sensor::Sensor(humidity_name)) {} - void DHT::setup() { ESP_LOGCONFIG(TAG, "Setting up DHT..."); this->pin_->digital_write(true); @@ -71,8 +65,6 @@ void DHT::set_dht_model(DHTModel model) { this->model_ = model; this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT; } -sensor::Sensor *DHT::get_temperature_sensor() const { return this->temperature_sensor_; } -sensor::Sensor *DHT::get_humidity_sensor() const { return this->humidity_sensor_; } bool HOT DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { *humidity = NAN; *temperature = NAN; diff --git a/esphome/components/dht/dht.h b/esphome/components/dht/dht.h index 5e3b35988..4ed5d4e02 100644 --- a/esphome/components/dht/dht.h +++ b/esphome/components/dht/dht.h @@ -18,13 +18,6 @@ enum DHTModel { /// Component for reading temperature/humidity measurements from DHT11/DHT22 sensors. class DHT : public PollingComponent { public: - /** Construct a DHTComponent. - * - * @param pin The pin which DHT sensor is connected to. - * @param update_interval The interval in ms the sensor should be checked. - */ - DHT(const std::string &temperature_name, const std::string &humidity_name, GPIOPin *pin, uint32_t update_interval); - /** Manually select the DHT model. * * Valid values are: @@ -40,10 +33,10 @@ class DHT : public PollingComponent { */ void set_dht_model(DHTModel model); - // ========== INTERNAL METHODS ========== - // (In most use cases you won't need these) - sensor::Sensor *get_temperature_sensor() const; - sensor::Sensor *get_humidity_sensor() const; + void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_model(DHTModel model) { model_ = model; } + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } + void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } /// Set up the pins and check connection. void setup() override; @@ -59,8 +52,8 @@ class DHT : public PollingComponent { GPIOPin *pin_; DHTModel model_{DHT_MODEL_AUTO_DETECT}; bool is_auto_detect_{false}; - sensor::Sensor *temperature_sensor_; - sensor::Sensor *humidity_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; }; } // namespace dht diff --git a/esphome/components/dht/sensor.py b/esphome/components/dht/sensor.py index 4975c7a05..e1e18bb7f 100644 --- a/esphome/components/dht/sensor.py +++ b/esphome/components/dht/sensor.py @@ -1,11 +1,10 @@ -from esphome.components import sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_NAME, \ - CONF_PIN, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, CONF_ACCURACY_DECIMALS, CONF_ICON, \ - ICON_THERMOMETER, CONF_UNIT_OF_MEASUREMENT, UNIT_CELSIUS, ICON_WATER_PERCENT, UNIT_PERCENT +import esphome.config_validation as cv +from esphome import pins +from esphome.components import sensor +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_PIN, CONF_TEMPERATURE, \ + CONF_UPDATE_INTERVAL, ICON_THERMOMETER, UNIT_CELSIUS, ICON_WATER_PERCENT, UNIT_PERCENT from esphome.cpp_helpers import gpio_pin_expression -from esphome.pins import gpio_input_pullup_pin_schema dht_ns = cg.esphome_ns.namespace('dht') DHTModel = dht_ns.enum('DHTModel') @@ -20,31 +19,27 @@ DHT_MODELS = { DHT = dht_ns.class_('DHT', cg.PollingComponent) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DHT), - cv.Required(CONF_PIN): gpio_input_pullup_pin_schema, - cv.Required(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_ACCURACY_DECIMALS, default=1): sensor.accuracy_decimals, - cv.Optional(CONF_ICON, default=ICON_THERMOMETER): sensor.icon, - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_CELSIUS): sensor.unit_of_measurement, - })), - cv.Required(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals, - cv.Optional(CONF_ICON, default=ICON_WATER_PERCENT): sensor.icon, - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PERCENT): sensor.unit_of_measurement, - })), - cv.Optional(CONF_MODEL, default='auto detect'): cv.one_of(*DHT_MODELS, upper=True, space='_'), + cv.GenerateID(): cv.declare_id(DHT), + cv.Required(CONF_PIN): pins.gpio_input_pin_schema, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), + cv.Optional(CONF_MODEL, default='auto detect'): cv.enum(DHT_MODELS, upper=True, space='_'), cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - pin = yield gpio_pin_expression(config[CONF_PIN]) - rhs = DHT.new(config[CONF_TEMPERATURE][CONF_NAME], - config[CONF_HUMIDITY][CONF_NAME], - pin, config[CONF_UPDATE_INTERVAL]) - dht = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(dht, config) - yield sensor.register_sensor(dht.Pget_temperature_sensor(), config[CONF_TEMPERATURE]) - yield sensor.register_sensor(dht.Pget_humidity_sensor(), config[CONF_HUMIDITY]) + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) - cg.add(dht.set_dht_model(DHT_MODELS[config[CONF_MODEL]])) + pin = yield gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) + + if CONF_TEMPERATURE in config: + sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature_sensor(sens)) + if CONF_HUMIDITY in config: + sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) + cg.add(var.set_humidity_sensor(sens)) + + cg.add(var.set_dht_model(config[CONF_MODEL])) diff --git a/esphome/components/dht12/dht12.h b/esphome/components/dht12/dht12.h index 1974c0ba8..ae4d4fd60 100644 --- a/esphome/components/dht12/dht12.h +++ b/esphome/components/dht12/dht12.h @@ -9,8 +9,6 @@ namespace dht12 { class DHT12Component : public PollingComponent, public i2c::I2CDevice { public: - DHT12Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/dht12/sensor.py b/esphome/components/dht12/sensor.py index 932f6ffa6..7d86e8c83 100644 --- a/esphome/components/dht12/sensor.py +++ b/esphome/components/dht12/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL, UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT + UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -10,17 +10,14 @@ dht12_ns = cg.esphome_ns.namespace('dht12') DHT12Component = dht12_ns.class_('DHT12Component', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DHT12Component), - cv.Optional(CONF_TEMPERATURE): - cv.nameable(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1)), - cv.Optional(CONF_HUMIDITY): - cv.nameable(sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x5C)) + cv.GenerateID(): cv.declare_id(DHT12Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/display/__init__.py b/esphome/components/display/__init__.py index 2592a3a48..28f8cdd32 100644 --- a/esphome/components/display/__init__.py +++ b/esphome/components/display/__init__.py @@ -1,8 +1,8 @@ # coding=utf-8 import esphome.codegen as cg import esphome.config_validation as cv -from esphome import core -from esphome.automation import ACTION_REGISTRY, maybe_simple_id +from esphome import core, automation +from esphome.automation import maybe_simple_id from esphome.const import CONF_ID, CONF_LAMBDA, CONF_PAGES, CONF_ROTATION, CONF_UPDATE_INTERVAL from esphome.core import coroutine @@ -13,9 +13,9 @@ DisplayBuffer = display_ns.class_('DisplayBuffer') DisplayPage = display_ns.class_('DisplayPage') DisplayPagePtr = DisplayPage.operator('ptr') DisplayBufferRef = DisplayBuffer.operator('ref') -DisplayPageShowAction = display_ns.class_('DisplayPageShowAction', cg.Action) -DisplayPageShowNextAction = display_ns.class_('DisplayPageShowNextAction', cg.Action) -DisplayPageShowPrevAction = display_ns.class_('DisplayPageShowPrevAction', cg.Action) +DisplayPageShowAction = display_ns.class_('DisplayPageShowAction', automation.Action) +DisplayPageShowNextAction = display_ns.class_('DisplayPageShowNextAction', automation.Action) +DisplayPageShowPrevAction = display_ns.class_('DisplayPageShowPrevAction', automation.Action) DISPLAY_ROTATIONS = { 0: display_ns.DISPLAY_ROTATION_0_DEGREES, @@ -29,11 +29,7 @@ def validate_rotation(value): value = cv.string(value) if value.endswith(u"°"): value = value[:-1] - try: - value = int(value) - except ValueError: - raise cv.Invalid(u"Expected integer for rotation") - return cv.one_of(*DISPLAY_ROTATIONS)(value) + return cv.enum(DISPLAY_ROTATIONS, int=True)(value) BASIC_DISPLAY_SCHEMA = cv.Schema({ @@ -44,7 +40,7 @@ BASIC_DISPLAY_SCHEMA = cv.Schema({ FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend({ cv.Optional(CONF_ROTATION): validate_rotation, cv.Optional(CONF_PAGES): cv.All(cv.ensure_list({ - cv.GenerateID(): cv.declare_variable_id(DisplayPage), + cv.GenerateID(): cv.declare_id(DisplayPage), cv.Required(CONF_LAMBDA): cv.lambda_, }), cv.Length(min=1)), }) @@ -71,37 +67,35 @@ def register_display(var, config): yield setup_display_core_(var, config) -@ACTION_REGISTRY.register('display.page.show', maybe_simple_id({ - cv.Required(CONF_ID): cv.templatable(cv.use_variable_id(DisplayPage)), +@automation.register_action('display.page.show', DisplayPageShowAction, maybe_simple_id({ + cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayPage)), })) def display_page_show_to_code(config, action_id, template_arg, args): - type = DisplayPageShowAction.template(template_arg) - action = cg.Pvariable(action_id, type.new(), type=type) + var = cg.new_Pvariable(action_id, template_arg) if isinstance(config[CONF_ID], core.Lambda): template_ = yield cg.templatable(config[CONF_ID], args, DisplayPagePtr) - cg.add(action.set_page(template_)) + cg.add(var.set_page(template_)) else: - var = yield cg.get_variable(config[CONF_ID]) - cg.add(action.set_page(var)) - yield action + paren = yield cg.get_variable(config[CONF_ID]) + cg.add(var.set_page(paren)) + yield var -@ACTION_REGISTRY.register('display.page.show_next', maybe_simple_id({ - cv.Required(CONF_ID): cv.templatable(cv.use_variable_id(DisplayBuffer)), +@automation.register_action('display.page.show_next', DisplayPageShowNextAction, maybe_simple_id({ + cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)), })) def display_page_show_next_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = DisplayPageShowNextAction.template(template_arg) - yield cg.Pvariable(action_id, type.new(var), type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('display.page.show_previous', maybe_simple_id({ - cv.Required(CONF_ID): cv.templatable(cv.use_variable_id(DisplayBuffer)), -})) +@automation.register_action('display.page.show_previous', DisplayPageShowPrevAction, + maybe_simple_id({ + cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)), + })) def display_page_show_previous_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = DisplayPageShowPrevAction.template(template_arg) - yield cg.Pvariable(action_id, type.new(var), type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) def to_code(config): diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 90dfba43e..57b95eee2 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -396,17 +396,13 @@ template class DisplayPageShowAction : public Action { if (page != nullptr) { page->show(); } - this->play_next(x...); } }; template class DisplayPageShowNextAction : public Action { public: DisplayPageShowNextAction(DisplayBuffer *buffer) : buffer_(buffer) {} - void play(Ts... x) override { - this->buffer_->show_next_page(); - this->play_next(x...); - } + void play(Ts... x) override { this->buffer_->show_next_page(); } protected: DisplayBuffer *buffer_; @@ -415,10 +411,7 @@ template class DisplayPageShowNextAction : public Action template class DisplayPageShowPrevAction : public Action { public: DisplayPageShowPrevAction(DisplayBuffer *buffer) : buffer_(buffer) {} - void play(Ts... x) override { - this->buffer_->show_prev_page(); - this->play_next(x...); - } + void play(Ts... x) override { this->buffer_->show_prev_page(); } protected: DisplayBuffer *buffer_; diff --git a/esphome/components/duty_cycle/duty_cycle_sensor.h b/esphome/components/duty_cycle/duty_cycle_sensor.h index 3657f97c3..2205bec72 100644 --- a/esphome/components/duty_cycle/duty_cycle_sensor.h +++ b/esphome/components/duty_cycle/duty_cycle_sensor.h @@ -17,10 +17,9 @@ struct DutyCycleSensorStore { static void gpio_intr(DutyCycleSensorStore *arg); }; -class DutyCycleSensor : public sensor::PollingSensorComponent { +class DutyCycleSensor : public sensor::Sensor, public PollingComponent { public: - DutyCycleSensor(const std::string &name, uint32_t update_interval, GPIOPin *pin) - : PollingSensorComponent(name, update_interval), pin_(pin) {} + void set_pin(GPIOPin *pin) { pin_ = pin; } void setup() override; float get_setup_priority() const override; diff --git a/esphome/components/duty_cycle/sensor.py b/esphome/components/duty_cycle/sensor.py index 30adf6693..d60ff0d8b 100644 --- a/esphome/components/duty_cycle/sensor.py +++ b/esphome/components/duty_cycle/sensor.py @@ -1,23 +1,23 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -import esphome.config_validation as cv -import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_NAME, CONF_PIN, CONF_UPDATE_INTERVAL, UNIT_PERCENT, \ - ICON_PERCENT +from esphome.const import CONF_ID, CONF_PIN, UNIT_PERCENT, ICON_PERCENT duty_cycle_ns = cg.esphome_ns.namespace('duty_cycle') DutyCycleSensor = duty_cycle_ns.class_('DutyCycleSensor', sensor.PollingSensorComponent) -CONFIG_SCHEMA = cv.nameable(sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1).extend({ - cv.GenerateID(): cv.declare_variable_id(DutyCycleSensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1).extend({ + cv.GenerateID(): cv.declare_id(DutyCycleSensor), cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - pin = yield cg.gpio_pin_expression(config[CONF_PIN]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL], pin) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) + + pin = yield cg.gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) diff --git a/esphome/components/endstop/cover.py b/esphome/components/endstop/cover.py index d59711004..0d65cc107 100644 --- a/esphome/components/endstop/cover.py +++ b/esphome/components/endstop/cover.py @@ -1,32 +1,32 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome import automation from esphome.components import binary_sensor, cover -import esphome.config_validation as cv -import esphome.codegen as cg from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, \ - CONF_CLOSE_ENDSTOP, CONF_ID, CONF_NAME, CONF_OPEN_ACTION, CONF_OPEN_DURATION, \ + CONF_CLOSE_ENDSTOP, CONF_ID, CONF_OPEN_ACTION, CONF_OPEN_DURATION, \ CONF_OPEN_ENDSTOP, CONF_STOP_ACTION, CONF_MAX_DURATION endstop_ns = cg.esphome_ns.namespace('endstop') EndstopCover = endstop_ns.class_('EndstopCover', cover.Cover, cg.Component) -CONFIG_SCHEMA = cv.nameable(cover.COVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(EndstopCover), +CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(EndstopCover), cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), - cv.Required(CONF_OPEN_ENDSTOP): cv.use_variable_id(binary_sensor.BinarySensor), + cv.Required(CONF_OPEN_ENDSTOP): cv.use_id(binary_sensor.BinarySensor), cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds, cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), - cv.Required(CONF_CLOSE_ENDSTOP): cv.use_variable_id(binary_sensor.BinarySensor), + cv.Required(CONF_CLOSE_ENDSTOP): cv.use_id(binary_sensor.BinarySensor), cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield cover.register_cover(var, config) diff --git a/esphome/components/endstop/endstop_cover.h b/esphome/components/endstop/endstop_cover.h index c75ff9d4c..f8d274623 100644 --- a/esphome/components/endstop/endstop_cover.h +++ b/esphome/components/endstop/endstop_cover.h @@ -10,8 +10,6 @@ namespace endstop { class EndstopCover : public cover::Cover, public Component { public: - EndstopCover(const std::string &name) : cover::Cover(name) {} - void setup() override; void loop() override; void dump_config() override; diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index 310b7c22b..2f02e71fe 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -12,7 +12,7 @@ CONF_MAJOR = 'major' CONF_MINOR = 'minor' CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon), + cv.GenerateID(): cv.declare_id(ESP32BLEBeacon), cv.Required(CONF_TYPE): cv.one_of('IBEACON', upper=True), cv.Required(CONF_UUID): cv.uuid, cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t, diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 193a25082..c77e5cd44 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -11,12 +11,12 @@ ESP32BLETracker = esp32_ble_tracker_ns.class_('ESP32BLETracker', cg.Component) ESPBTDeviceListener = esp32_ble_tracker_ns.class_('ESPBTDeviceListener') CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(ESP32BLETracker), + cv.GenerateID(): cv.declare_id(ESP32BLETracker), cv.Optional(CONF_SCAN_INTERVAL, default='300s'): cv.positive_time_period_seconds, }).extend(cv.COMPONENT_SCHEMA) ESP_BLE_DEVICE_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_variable_id(ESP32BLETracker), + cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_id(ESP32BLETracker), }) diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index 9708b11c3..4e7901571 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -58,7 +58,7 @@ CONF_TEST_PATTERN = 'test_pattern' camera_range_param = cv.All(cv.int_, cv.Range(min=-2, max=2)) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(ESP32Camera), + cv.GenerateID(): cv.declare_id(ESP32Camera), cv.Required(CONF_NAME): cv.string, cv.Required(CONF_DATA_PINS): cv.All([pins.input_pin], cv.Length(min=8, max=8)), cv.Required(CONF_VSYNC_PIN): pins.input_pin, @@ -80,7 +80,7 @@ CONFIG_SCHEMA = cv.Schema({ max=60)), cv.Optional(CONF_IDLE_FRAMERATE, default='0.1 fps'): cv.All(cv.framerate, cv.Range(min=0, max=1)), - cv.Optional(CONF_RESOLUTION, default='640X480'): cv.one_of(*FRAME_SIZES, upper=True), + cv.Optional(CONF_RESOLUTION, default='640X480'): cv.enum(FRAME_SIZES, upper=True), cv.Optional(CONF_JPEG_QUALITY, default=10): cv.All(cv.int_, cv.Range(min=10, max=63)), cv.Optional(CONF_CONTRAST, default=0): camera_range_param, cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, @@ -124,7 +124,7 @@ def to_code(config): cg.add(var.set_idle_update_interval(0)) else: cg.add(var.set_idle_update_interval(1000 / config[CONF_IDLE_FRAMERATE])) - cg.add(var.set_frame_size(FRAME_SIZES[config[CONF_RESOLUTION]])) + cg.add(var.set_frame_size(config[CONF_RESOLUTION])) cg.add_define('USE_ESP32_CAMERA') cg.add_build_flag('-DBOARD_HAS_PSRAM') diff --git a/esphome/components/esp32_hall/sensor.py b/esphome/components/esp32_hall/sensor.py index 62badf3bd..81a90c8c1 100644 --- a/esphome/components/esp32_hall/sensor.py +++ b/esphome/components/esp32_hall/sensor.py @@ -1,22 +1,18 @@ -from esphome.components import sensor -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor from esphome.const import CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, ESP_PLATFORM_ESP32, \ - CONF_UNIT_OF_MEASUREMENT, CONF_ICON, CONF_ACCURACY_DECIMALS, UNIT_MICROTESLA, ICON_MAGNET + UNIT_MICROTESLA, ICON_MAGNET ESP_PLATFORMS = [ESP_PLATFORM_ESP32] esp32_hall_ns = cg.esphome_ns.namespace('esp32_hall') ESP32HallSensor = esp32_hall_ns.class_('ESP32HallSensor', sensor.PollingSensorComponent) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(ESP32HallSensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1).extend({ + cv.GenerateID(): cv.declare_id(ESP32HallSensor), cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, - - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_MICROTESLA): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_MAGNET): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=-1): sensor.accuracy_decimals, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/esp32_touch/__init__.py b/esphome/components/esp32_touch/__init__.py index bc2698a02..3bdc988fc 100644 --- a/esphome/components/esp32_touch/__init__.py +++ b/esphome/components/esp32_touch/__init__.py @@ -44,7 +44,7 @@ VOLTAGE_ATTENUATION = { } CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(ESP32TouchComponent), + cv.GenerateID(): cv.declare_id(ESP32TouchComponent), cv.Optional(CONF_SETUP_MODE, default=False): cv.boolean, cv.Optional(CONF_IIR_FILTER, default='0ms'): cv.positive_time_period_milliseconds, cv.Optional(CONF_SLEEP_DURATION, default='27306us'): diff --git a/esphome/components/esp32_touch/binary_sensor.py b/esphome/components/esp32_touch/binary_sensor.py index 27b93a1df..94748e53e 100644 --- a/esphome/components/esp32_touch/binary_sensor.py +++ b/esphome/components/esp32_touch/binary_sensor.py @@ -34,12 +34,12 @@ def validate_touch_pad(value): ESP32TouchBinarySensor = esp32_touch_ns.class_('ESP32TouchBinarySensor', binary_sensor.BinarySensor) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(ESP32TouchBinarySensor), - cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_variable_id(ESP32TouchComponent), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(ESP32TouchBinarySensor), + cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent), cv.Required(CONF_PIN): validate_touch_pad, cv.Required(CONF_THRESHOLD): cv.uint16_t, -})) +}) def to_code(config): diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.cpp b/esphome/components/esp8266_pwm/esp8266_pwm.cpp index e76e16170..363f4d24b 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.cpp +++ b/esphome/components/esp8266_pwm/esp8266_pwm.cpp @@ -25,6 +25,8 @@ void ESP8266PWM::dump_config() { LOG_FLOAT_OUTPUT(this); } void HOT ESP8266PWM::write_state(float state) { + this->last_output_ = state; + // Also check pin inversion if (this->pin_->is_inverted()) { state = 1.0f - state; diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.h b/esphome/components/esp8266_pwm/esp8266_pwm.h index 4ea611952..b6839985b 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.h +++ b/esphome/components/esp8266_pwm/esp8266_pwm.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/esphal.h" +#include "esphome/core/automation.h" #include "esphome/components/output/float_output.h" namespace esphome { @@ -9,9 +10,13 @@ namespace esp8266_pwm { class ESP8266PWM : public output::FloatOutput, public Component { public: - explicit ESP8266PWM(GPIOPin *pin) : pin_(pin) {} - + void set_pin(GPIOPin *pin) { pin_ = pin; } void set_frequency(float frequency) { this->frequency_ = frequency; } + /// Dynamically update frequency + void update_frequency(float frequency) { + this->set_frequency(frequency); + this->write_state(this->last_output_); + } /// Initialize pin void setup() override; @@ -24,6 +29,22 @@ class ESP8266PWM : public output::FloatOutput, public Component { GPIOPin *pin_; float frequency_{1000.0}; + /// Cache last output level for dynamic frequency updating + float last_output_{0.0}; +}; + +template class SetFrequencyAction : public Action { + public: + SetFrequencyAction(ESP8266PWM *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(float, frequency); + + void play(Ts... x) { + float freq = this->frequency_.value(x...); + this->parent_->update_frequency(freq); + } + + protected: + ESP8266PWM *parent_; }; } // namespace esp8266_pwm diff --git a/esphome/components/esp8266_pwm/output.py b/esphome/components/esp8266_pwm/output.py index bbabf3398..e97349052 100644 --- a/esphome/components/esp8266_pwm/output.py +++ b/esphome/components/esp8266_pwm/output.py @@ -1,4 +1,4 @@ -from esphome import pins +from esphome import pins, automation from esphome.components import output import esphome.config_validation as cv import esphome.codegen as cg @@ -15,18 +15,34 @@ def valid_pwm_pin(value): esp8266_pwm_ns = cg.esphome_ns.namespace('esp8266_pwm') ESP8266PWM = esp8266_pwm_ns.class_('ESP8266PWM', output.FloatOutput, cg.Component) +SetFrequencyAction = esp8266_pwm_ns.class_('SetFrequencyAction', automation.Action) +validate_frequency = cv.All(cv.frequency, cv.Range(min=1.0e-6)) CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_variable_id(ESP8266PWM), + cv.Required(CONF_ID): cv.declare_id(ESP8266PWM), cv.Required(CONF_PIN): cv.All(pins.internal_gpio_output_pin_schema, valid_pwm_pin), - cv.Optional(CONF_FREQUENCY, default='1kHz'): cv.All(cv.frequency, cv.Range(min=1.0e-6)), + cv.Optional(CONF_FREQUENCY, default='1kHz'): validate_frequency, }).extend(cv.COMPONENT_SCHEMA) def to_code(config): - pin = yield cg.gpio_pin_expression(config[CONF_PIN]) - var = cg.new_Pvariable(config[CONF_ID], pin) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield output.register_output(var, config) + pin = yield cg.gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) + cg.add(var.set_frequency(config[CONF_FREQUENCY])) + + +@automation.register_action('output.esp8266_pwm.set_frequency', SetFrequencyAction, cv.Schema({ + cv.Required(CONF_ID): cv.use_id(ESP8266PWM), + cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency), +})) +def esp8266_set_frequency_to_code(config, action_id, template_arg, args): + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = yield cg.templatable(config[CONF_FREQUENCY], args, float) + cg.add(var.set_frequency(template_)) + yield var diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 4aa25affd..50a0d99d3 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -7,6 +7,7 @@ from esphome.core import CORE, coroutine_with_priority CONFLICTS_WITH = ['wifi'] ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +AUTO_LOAD = ['network'] ethernet_ns = cg.esphome_ns.namespace('ethernet') CONF_PHY_ADDR = 'phy_addr' @@ -54,12 +55,12 @@ def validate(config): CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(EthernetComponent), - cv.Required(CONF_TYPE): cv.one_of(*ETHERNET_TYPES, upper=True), + cv.GenerateID(): cv.declare_id(EthernetComponent), + cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True), cv.Required(CONF_MDC_PIN): pins.output_pin, cv.Required(CONF_MDIO_PIN): pins.input_output_pin, - cv.Optional(CONF_CLK_MODE, default='GPIO0_IN'): cv.one_of(*CLK_MODES, upper=True, space='_'), - cv.Optional(CONF_PHY_ADDR, default=0): cv.All(cv.int_, cv.Range(min=0, max=31)), + cv.Optional(CONF_CLK_MODE, default='GPIO0_IN'): cv.enum(CLK_MODES, upper=True, space='_'), + cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, cv.Optional(CONF_DOMAIN, default='.local'): cv.domain_name, @@ -87,7 +88,7 @@ def to_code(config): cg.add(var.set_phy_addr(config[CONF_PHY_ADDR])) cg.add(var.set_mdc_pin(config[CONF_MDC_PIN])) cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN])) - cg.add(var.set_type(ETHERNET_TYPES[config[CONF_TYPE]])) + cg.add(var.set_type(config[CONF_TYPE])) cg.add(var.set_clk_mode(CLK_MODES[config[CONF_CLK_MODE]])) cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index c7787cd3f..46ad78946 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -1,6 +1,7 @@ -from esphome.automation import ACTION_REGISTRY, maybe_simple_id -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.automation import maybe_simple_id from esphome.components import mqtt from esphome.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_OSCILLATING, \ CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED, \ @@ -14,9 +15,9 @@ FanState = fan_ns.class_('FanState', cg.Nameable, cg.Component) MakeFan = cg.Application.struct('MakeFan') # Actions -TurnOnAction = fan_ns.class_('TurnOnAction', cg.Action) -TurnOffAction = fan_ns.class_('TurnOffAction', cg.Action) -ToggleAction = fan_ns.class_('ToggleAction', cg.Action) +TurnOnAction = fan_ns.class_('TurnOnAction', automation.Action) +TurnOffAction = fan_ns.class_('TurnOffAction', automation.Action) +ToggleAction = fan_ns.class_('ToggleAction', automation.Action) FanSpeed = fan_ns.enum('FanSpeed') FAN_SPEEDS = { @@ -27,8 +28,8 @@ FAN_SPEEDS = { } FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(FanState), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTFanComponent), + cv.GenerateID(): cv.declare_id(FanState), + cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTFanComponent), cv.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.All(cv.requires_component('mqtt'), cv.publish_topic), cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All(cv.requires_component('mqtt'), @@ -38,6 +39,7 @@ FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ @coroutine def setup_fan_core_(var, config): + cg.add(var.set_name(config[CONF_NAME])) if CONF_INTERNAL in config: cg.add(var.set_internal(config[CONF_INTERNAL])) @@ -66,50 +68,43 @@ def register_fan(var, config): @coroutine def create_fan_state(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield register_fan(var, config) yield var FAN_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(FanState), + cv.Required(CONF_ID): cv.use_id(FanState), }) -@ACTION_REGISTRY.register('fan.toggle', FAN_ACTION_SCHEMA) +@automation.register_action('fan.toggle', ToggleAction, FAN_ACTION_SCHEMA) def fan_toggle_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ToggleAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('fan.turn_off', FAN_ACTION_SCHEMA) +@automation.register_action('fan.turn_off', TurnOffAction, FAN_ACTION_SCHEMA) def fan_turn_off_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = TurnOffAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('fan.turn_on', maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(FanState), +@automation.register_action('fan.turn_on', TurnOnAction, maybe_simple_id({ + cv.Required(CONF_ID): cv.use_id(FanState), cv.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), - cv.Optional(CONF_SPEED): cv.templatable(cv.one_of(*FAN_SPEEDS, upper=True)), + cv.Optional(CONF_SPEED): cv.templatable(cv.enum(FAN_SPEEDS, upper=True)), })) def fan_turn_on_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = TurnOnAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) if CONF_OSCILLATING in config: template_ = yield cg.templatable(config[CONF_OSCILLATING], args, bool) - cg.add(action.set_oscillating(template_)) + cg.add(var.set_oscillating(template_)) if CONF_SPEED in config: - template_ = yield cg.templatable(config[CONF_SPEED], args, FanSpeed, - to_exp=FAN_SPEEDS) - cg.add(action.set_speed(template_)) - yield action + template_ = yield cg.templatable(config[CONF_SPEED], args, FanSpeed) + cg.add(var.set_speed(template_)) + yield var def to_code(config): diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index ebbb2fba9..dfa72a3ea 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -23,7 +23,6 @@ template class TurnOnAction : public Action { call.set_speed(this->speed_.value(x...)); } call.perform(); - this->play_next(x...); } protected: @@ -34,10 +33,7 @@ template class TurnOffAction : public Action { public: explicit TurnOffAction(FanState *state) : state_(state) {} - void play(Ts... x) override { - this->state_->turn_off().perform(); - this->play_next(x...); - } + void play(Ts... x) override { this->state_->turn_off().perform(); } protected: FanState *state_; @@ -47,10 +43,7 @@ template class ToggleAction : public Action { public: explicit ToggleAction(FanState *state) : state_(state) {} - void play(Ts... x) override { - this->state_->toggle().perform(); - this->play_next(x...); - } + void play(Ts... x) override { this->state_->toggle().perform(); } protected: FanState *state_; diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp index e662cb9ee..af170a755 100644 --- a/esphome/components/fan/fan_state.cpp +++ b/esphome/components/fan/fan_state.cpp @@ -13,10 +13,10 @@ void FanState::add_on_state_callback(std::function &&callback) { } FanState::FanState(const std::string &name) : Nameable(name) {} -FanState::StateCall FanState::turn_on() { return this->make_call().set_state(true); } -FanState::StateCall FanState::turn_off() { return this->make_call().set_state(false); } -FanState::StateCall FanState::toggle() { return this->make_call().set_state(!this->state); } -FanState::StateCall FanState::make_call() { return FanState::StateCall(this); } +FanStateCall FanState::turn_on() { return this->make_call().set_state(true); } +FanStateCall FanState::turn_off() { return this->make_call().set_state(false); } +FanStateCall FanState::toggle() { return this->make_call().set_state(!this->state); } +FanStateCall FanState::make_call() { return FanStateCall(this); } struct FanStateRTCState { bool state; @@ -39,32 +39,7 @@ void FanState::setup() { float FanState::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; } uint32_t FanState::hash_base() { return 418001110UL; } -FanState::StateCall::StateCall(FanState *state) : state_(state) {} -FanState::StateCall &FanState::StateCall::set_state(bool state) { - this->binary_state_ = state; - return *this; -} -FanState::StateCall &FanState::StateCall::set_state(optional state) { - this->binary_state_ = state; - return *this; -} -FanState::StateCall &FanState::StateCall::set_oscillating(bool oscillating) { - this->oscillating_ = oscillating; - return *this; -} -FanState::StateCall &FanState::StateCall::set_oscillating(optional oscillating) { - this->oscillating_ = oscillating; - return *this; -} -FanState::StateCall &FanState::StateCall::set_speed(FanSpeed speed) { - this->speed_ = speed; - return *this; -} -FanState::StateCall &FanState::StateCall::set_speed(optional speed) { - this->speed_ = speed; - return *this; -} -void FanState::StateCall::perform() const { +void FanStateCall::perform() const { if (this->binary_state_.has_value()) { this->state_->state = *this->binary_state_; } @@ -92,7 +67,7 @@ void FanState::StateCall::perform() const { this->state_->state_callback_.call(); } -FanState::StateCall &FanState::StateCall::set_speed(const char *speed) { +FanStateCall &FanStateCall::set_speed(const char *speed) { if (strcasecmp(speed, "low") == 0) { this->set_speed(FAN_SPEED_LOW); } else if (strcasecmp(speed, "medium") == 0) { diff --git a/esphome/components/fan/fan_state.h b/esphome/components/fan/fan_state.h index eb3faf3eb..4e937c68b 100644 --- a/esphome/components/fan/fan_state.h +++ b/esphome/components/fan/fan_state.h @@ -15,8 +15,50 @@ enum FanSpeed { FAN_SPEED_HIGH = 2 ///< The fan is running on high/full speed. }; +class FanState; + +class FanStateCall { + public: + explicit FanStateCall(FanState *state) : state_(state) {} + + FanStateCall &set_state(bool binary_state) { + this->binary_state_ = binary_state; + return *this; + } + FanStateCall &set_state(optional binary_state) { + this->binary_state_ = binary_state; + return *this; + } + FanStateCall &set_oscillating(bool oscillating) { + this->oscillating_ = oscillating; + return *this; + } + FanStateCall &set_oscillating(optional oscillating) { + this->oscillating_ = oscillating; + return *this; + } + FanStateCall &set_speed(FanSpeed speed) { + this->speed_ = speed; + return *this; + } + FanStateCall &set_speed(optional speed) { + this->speed_ = speed; + return *this; + } + FanStateCall &set_speed(const char *speed); + + void perform() const; + + protected: + FanState *const state_; + optional binary_state_; + optional oscillating_{}; + optional speed_{}; +}; + class FanState : public Nameable, public Component { public: + FanState() = default; /// Construct the fan state with name. explicit FanState(const std::string &name); @@ -35,36 +77,17 @@ class FanState : public Nameable, public Component { /// The current fan speed. FanSpeed speed{FAN_SPEED_HIGH}; - class StateCall { - public: - explicit StateCall(FanState *state); - - FanState::StateCall &set_state(bool state); - FanState::StateCall &set_state(optional state); - FanState::StateCall &set_oscillating(bool oscillating); - FanState::StateCall &set_oscillating(optional oscillating); - FanState::StateCall &set_speed(FanSpeed speed); - FanState::StateCall &set_speed(optional speed); - FanState::StateCall &set_speed(const char *speed); - - void perform() const; - - protected: - FanState *const state_; - optional binary_state_; - optional oscillating_{}; - optional speed_{}; - }; - - FanState::StateCall turn_on(); - FanState::StateCall turn_off(); - FanState::StateCall toggle(); - FanState::StateCall make_call(); + FanStateCall turn_on(); + FanStateCall turn_off(); + FanStateCall toggle(); + FanStateCall make_call(); void setup() override; float get_setup_priority() const override; protected: + friend FanStateCall; + uint32_t hash_base() override; FanTraits traits_{}; diff --git a/esphome/components/fastled_base/__init__.py b/esphome/components/fastled_base/__init__.py index 22249651b..5ad7280ee 100644 --- a/esphome/components/fastled_base/__init__.py +++ b/esphome/components/fastled_base/__init__.py @@ -19,13 +19,13 @@ RGB_ORDERS = [ ] BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(FastLEDLightOutput), + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(FastLEDLightOutput), cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True), cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, - cv.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(power_supply.PowerSupply), + cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), }).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/fastled_clockless/light.py b/esphome/components/fastled_clockless/light.py index eebc19a79..340dc0ab0 100644 --- a/esphome/components/fastled_clockless/light.py +++ b/esphome/components/fastled_clockless/light.py @@ -38,7 +38,7 @@ def validate(value): return value -CONFIG_SCHEMA = cv.nameable(fastled_base.BASE_SCHEMA.extend({ +CONFIG_SCHEMA = cv.All(fastled_base.BASE_SCHEMA.extend({ cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Required(CONF_PIN): pins.output_pin, }), validate) diff --git a/esphome/components/fastled_spi/light.py b/esphome/components/fastled_spi/light.py index ad7df9e98..959c8a1b1 100644 --- a/esphome/components/fastled_spi/light.py +++ b/esphome/components/fastled_spi/light.py @@ -17,11 +17,11 @@ CHIPSETS = [ 'DOTSTAR', ] -CONFIG_SCHEMA = cv.nameable(fastled_base.BASE_SCHEMA.extend({ +CONFIG_SCHEMA = fastled_base.BASE_SCHEMA.extend({ cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Required(CONF_DATA_PIN): pins.output_pin, cv.Required(CONF_CLOCK_PIN): pins.output_pin, -})) +}) def to_code(config): diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 888c00cb1..774b49dbc 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -67,11 +67,11 @@ DEFAULT_GLYPHS = u' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklm CONF_RAW_DATA_ID = 'raw_data_id' FONT_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_variable_id(Font), + cv.Required(CONF_ID): cv.declare_id(Font), cv.Required(CONF_FILE): validate_truetype_file, cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs, cv.Optional(CONF_SIZE, default=20): cv.All(cv.int_, cv.Range(min=1)), - cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(cg.uint8), + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), }) CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA) @@ -80,7 +80,7 @@ CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA) def to_code(config): from PIL import ImageFont - path = CORE.relative_path(config[CONF_FILE]) + path = CORE.relative_config_path(config[CONF_FILE]) try: font = ImageFont.truetype(path, config[CONF_SIZE]) except Exception as e: diff --git a/esphome/components/globals/__init__.py b/esphome/components/globals/__init__.py index c06d1cf54..4bcbc8099 100644 --- a/esphome/components/globals/__init__.py +++ b/esphome/components/globals/__init__.py @@ -11,7 +11,7 @@ GlobalsComponent = globals_ns.class_('GlobalsComponent', cg.Component) MULTI_CONF = True CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_variable_id(GlobalsComponent), + cv.Required(CONF_ID): cv.declare_id(GlobalsComponent), cv.Required(CONF_TYPE): cv.string_strict, cv.Optional(CONF_INITIAL_VALUE): cv.string_strict, cv.Optional(CONF_RESTORE_VALUE): cv.boolean, diff --git a/esphome/components/gpio/binary_sensor/__init__.py b/esphome/components/gpio/binary_sensor/__init__.py index cbe8f730c..e269de5a7 100644 --- a/esphome/components/gpio/binary_sensor/__init__.py +++ b/esphome/components/gpio/binary_sensor/__init__.py @@ -2,20 +2,21 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import binary_sensor -from esphome.const import CONF_ID, CONF_NAME, CONF_PIN +from esphome.const import CONF_ID, CONF_PIN from .. import gpio_ns GPIOBinarySensor = gpio_ns.class_('GPIOBinarySensor', binary_sensor.BinarySensor, cg.Component) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(GPIOBinarySensor), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(GPIOBinarySensor), cv.Required(CONF_PIN): pins.gpio_input_pin_schema -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield binary_sensor.register_binary_sensor(var, config) + pin = yield cg.gpio_pin_expression(config[CONF_PIN]) - rhs = GPIOBinarySensor.new(config[CONF_NAME], pin) - gpio = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(gpio, config) - yield binary_sensor.register_binary_sensor(gpio, config) + cg.add(var.set_pin(pin)) diff --git a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp index 7d7d54f6c..dff3609ce 100644 --- a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +++ b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp @@ -19,8 +19,6 @@ void GPIOBinarySensor::dump_config() { void GPIOBinarySensor::loop() { this->publish_state(this->pin_->digital_read()); } float GPIOBinarySensor::get_setup_priority() const { return setup_priority::HARDWARE; } -GPIOBinarySensor::GPIOBinarySensor(const std::string &name, GPIOPin *pin) - : binary_sensor::BinarySensor(name), pin_(pin) {} } // namespace gpio } // namespace esphome diff --git a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h index f03ce20f2..cfe49b3c9 100644 --- a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +++ b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h @@ -8,8 +8,7 @@ namespace gpio { class GPIOBinarySensor : public binary_sensor::BinarySensor, public Component { public: - explicit GPIOBinarySensor(const std::string &name, GPIOPin *pin); - + void set_pin(GPIOPin *pin) { pin_ = pin; } // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) /// Setup pin diff --git a/esphome/components/gpio/output/__init__.py b/esphome/components/gpio/output/__init__.py index 5c2228f32..bab23c824 100644 --- a/esphome/components/gpio/output/__init__.py +++ b/esphome/components/gpio/output/__init__.py @@ -9,13 +9,15 @@ GPIOBinaryOutput = gpio_ns.class_('GPIOBinaryOutput', output.BinaryOutput, cg.Component) CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_variable_id(GPIOBinaryOutput), + cv.Required(CONF_ID): cv.declare_id(GPIOBinaryOutput), cv.Required(CONF_PIN): pins.gpio_output_pin_schema, }).extend(cv.COMPONENT_SCHEMA) def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield output.register_output(var, config) + yield cg.register_component(var, config) + pin = yield cg.gpio_pin_expression(config[CONF_PIN]) - gpio = cg.new_Pvariable(config[CONF_ID], pin) - yield output.register_output(gpio, config) - yield cg.register_component(gpio, config) + cg.add(var.set_pin(pin)) diff --git a/esphome/components/gpio/output/gpio_binary_output.h b/esphome/components/gpio/output/gpio_binary_output.h index b2dcc8cbf..0a7dfb46e 100644 --- a/esphome/components/gpio/output/gpio_binary_output.h +++ b/esphome/components/gpio/output/gpio_binary_output.h @@ -9,7 +9,7 @@ namespace gpio { class GPIOBinaryOutput : public output::BinaryOutput, public Component { public: - explicit GPIOBinaryOutput(GPIOPin *pin) : pin_(pin) {} + void set_pin(GPIOPin *pin) { pin_ = pin; } void setup() override { this->turn_off(); diff --git a/esphome/components/gpio/switch/__init__.py b/esphome/components/gpio/switch/__init__.py index 69a100a00..7b383cb8a 100644 --- a/esphome/components/gpio/switch/__init__.py +++ b/esphome/components/gpio/switch/__init__.py @@ -1,8 +1,8 @@ -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv from esphome import pins from esphome.components import switch -from esphome.const import CONF_ID, CONF_INTERLOCK, CONF_NAME, CONF_PIN, CONF_RESTORE_MODE +from esphome.const import CONF_ID, CONF_INTERLOCK, CONF_PIN, CONF_RESTORE_MODE from .. import gpio_ns GPIOSwitch = gpio_ns.class_('GPIOSwitch', switch.Switch, cg.Component) @@ -15,27 +15,28 @@ RESTORE_MODES = { 'ALWAYS_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_ON, } -CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(GPIOSwitch), +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(GPIOSwitch), cv.Required(CONF_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_RESTORE_MODE, default='RESTORE_DEFAULT_OFF'): - cv.one_of(*RESTORE_MODES, upper=True, space='_'), - cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_variable_id(switch.Switch)), -}).extend(cv.COMPONENT_SCHEMA)) + cv.enum(RESTORE_MODES, upper=True, space='_'), + cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_id(switch.Switch)), +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - pin = yield cg.gpio_pin_expression(config[CONF_PIN]) - rhs = GPIOSwitch.new(config[CONF_NAME], pin) - gpio = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(gpio, config) - yield switch.register_switch(gpio, config) + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield switch.register_switch(var, config) - cg.add(gpio.set_restore_mode(RESTORE_MODES[config[CONF_RESTORE_MODE]])) + pin = yield cg.gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) + + cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) if CONF_INTERLOCK in config: interlock = [] for it in config[CONF_INTERLOCK]: lock = yield cg.get_variable(it) interlock.append(lock) - cg.add(gpio.set_interlock(interlock)) + cg.add(var.set_interlock(interlock)) diff --git a/esphome/components/gpio/switch/gpio_switch.cpp b/esphome/components/gpio/switch/gpio_switch.cpp index f7fc29ce2..22139d6b9 100644 --- a/esphome/components/gpio/switch/gpio_switch.cpp +++ b/esphome/components/gpio/switch/gpio_switch.cpp @@ -6,8 +6,6 @@ namespace gpio { static const char *TAG = "switch.gpio"; -GPIOSwitch::GPIOSwitch(const std::string &name, GPIOPin *pin) : Switch(name), Component(), pin_(pin) {} - float GPIOSwitch::get_setup_priority() const { return setup_priority::HARDWARE; } void GPIOSwitch::setup() { ESP_LOGCONFIG(TAG, "Setting up GPIO Switch '%s'...", this->name_.c_str()); diff --git a/esphome/components/gpio/switch/gpio_switch.h b/esphome/components/gpio/switch/gpio_switch.h index a7be5fbb8..ceace477b 100644 --- a/esphome/components/gpio/switch/gpio_switch.h +++ b/esphome/components/gpio/switch/gpio_switch.h @@ -15,7 +15,7 @@ enum GPIOSwitchRestoreMode { class GPIOSwitch : public switch_::Switch, public Component { public: - GPIOSwitch(const std::string &name, GPIOPin *pin); + void set_pin(GPIOPin *pin) { pin_ = pin; } void set_restore_mode(GPIOSwitchRestoreMode restore_mode); diff --git a/esphome/components/hdc1080/hdc1080.h b/esphome/components/hdc1080/hdc1080.h index 41e104018..9cb87cdb8 100644 --- a/esphome/components/hdc1080/hdc1080.h +++ b/esphome/components/hdc1080/hdc1080.h @@ -9,9 +9,6 @@ namespace hdc1080 { class HDC1080Component : public PollingComponent, public i2c::I2CDevice { public: - /// Initialize the component with the provided update interval. - HDC1080Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } diff --git a/esphome/components/hdc1080/sensor.py b/esphome/components/hdc1080/sensor.py index 451abcf62..00b829635 100644 --- a/esphome/components/hdc1080/sensor.py +++ b/esphome/components/hdc1080/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_CELSIUS, UNIT_PERCENT + ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -10,17 +10,14 @@ hdc1080_ns = cg.esphome_ns.namespace('hdc1080') HDC1080Component = hdc1080_ns.class_('HDC1080Component', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(HDC1080Component), - cv.Optional(CONF_TEMPERATURE): cv.nameable( - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1)), - cv.Optional(CONF_HUMIDITY): cv.nameable( - sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x40)) + cv.GenerateID(): cv.declare_id(HDC1080Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/hlw8012/hlw8012.cpp b/esphome/components/hlw8012/hlw8012.cpp index a97440c54..c5138b756 100644 --- a/esphome/components/hlw8012/hlw8012.cpp +++ b/esphome/components/hlw8012/hlw8012.cpp @@ -32,17 +32,17 @@ void HLW8012Component::dump_config() { float HLW8012Component::get_setup_priority() const { return setup_priority::DATA; } void HLW8012Component::update() { // HLW8012 has 50% duty cycle + const uint32_t last_rise_cf = this->cf_store_.get_last_rise(); + const uint32_t last_rise_cf1 = this->cf1_store_.get_last_rise(); + const uint32_t now = micros(); float full_cycle_cf = this->cf_store_.get_pulse_width_s() * 2; float full_cycle_cf1 = this->cf1_store_.get_pulse_width_s() * 2; - float cf_hz, cf1_hz; + float cf_hz = 0.0f, cf1_hz = 0.0f; + auto update_interval_micros = static_cast(this->update_interval_ * 1e3f); - if (full_cycle_cf == 0.0f) - cf_hz = 0.0f; - else + if (full_cycle_cf != 0.0f && now - last_rise_cf < update_interval_micros * 3) cf_hz = 1.0f / full_cycle_cf; - if (full_cycle_cf1 == 0.0f) - cf1_hz = 0.0f; - else + if (full_cycle_cf1 != 0.0f && now - last_rise_cf1 < update_interval_micros * 3) cf1_hz = 1.0f / full_cycle_cf1; if (this->nth_value_++ < 2) { diff --git a/esphome/components/hlw8012/hlw8012.h b/esphome/components/hlw8012/hlw8012.h index e4553ed12..3eb1ea97c 100644 --- a/esphome/components/hlw8012/hlw8012.h +++ b/esphome/components/hlw8012/hlw8012.h @@ -10,8 +10,6 @@ namespace hlw8012 { class HLW8012Component : public PollingComponent { public: - HLW8012Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/hlw8012/sensor.py b/esphome/components/hlw8012/sensor.py index c4b971df9..3dd1f0ae3 100644 --- a/esphome/components/hlw8012/sensor.py +++ b/esphome/components/hlw8012/sensor.py @@ -3,8 +3,8 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import sensor from esphome.const import CONF_CHANGE_MODE_EVERY, CONF_CURRENT, \ - CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_SEL_PIN, CONF_UPDATE_INTERVAL, \ - CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT + CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_SEL_PIN, CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER, \ + ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT AUTO_LOAD = ['pulse_width'] @@ -14,26 +14,25 @@ HLW8012Component = hlw8012_ns.class_('HLW8012Component', cg.PollingComponent) CONF_CF1_PIN = 'cf1_pin' CONF_CF_PIN = 'cf_pin' CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(HLW8012Component), + cv.GenerateID(): cv.declare_id(HLW8012Component), cv.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_CF_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt), cv.Required(CONF_CF1_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt), - cv.Optional(CONF_VOLTAGE): cv.nameable(sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1)), - cv.Optional(CONF_CURRENT): cv.nameable(sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2)), - cv.Optional(CONF_POWER): cv.nameable(sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1)), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance, cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float, cv.Optional(CONF_CHANGE_MODE_EVERY, default=8): cv.All(cv.uint32_t, cv.Range(min=1)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) sel = yield cg.gpio_pin_expression(config[CONF_SEL_PIN]) diff --git a/esphome/components/hmc5883l/hmc5883l.h b/esphome/components/hmc5883l/hmc5883l.h index d531a581e..3946f1fb1 100644 --- a/esphome/components/hmc5883l/hmc5883l.h +++ b/esphome/components/hmc5883l/hmc5883l.h @@ -20,8 +20,6 @@ enum HMC5883LRange { class HMC5883LComponent : public PollingComponent, public i2c::I2CDevice { public: - HMC5883LComponent(uint32_t update_interval) : PollingComponent(update_interval) {} - void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/hmc5883l/sensor.py b/esphome/components/hmc5883l/sensor.py index 800e563fc..afb8ffac7 100644 --- a/esphome/components/hmc5883l/sensor.py +++ b/esphome/components/hmc5883l/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ADDRESS, CONF_ID, CONF_RANGE, CONF_UPDATE_INTERVAL, \ - ICON_MAGNET, UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION +from esphome.const import CONF_ADDRESS, CONF_ID, CONF_RANGE, ICON_MAGNET, UNIT_MICROTESLA, \ + UNIT_DEGREES, ICON_SCREEN_ROTATION DEPENDENCIES = ['i2c'] @@ -33,30 +33,29 @@ def validate_range(value): value = cv.string(value) if value.endswith(u'µT') or value.endswith('uT'): value = value[:-2] - return cv.one_of(*HMC5883L_RANGES, int=True)(value) + return cv.enum(HMC5883L_RANGES, int=True)(value) field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1) heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(HMC5883LComponent), + cv.GenerateID(): cv.declare_id(HMC5883LComponent), cv.Optional(CONF_ADDRESS): cv.i2c_address, - cv.Optional(CONF_FIELD_STRENGTH_X): cv.nameable(field_strength_schema), - cv.Optional(CONF_FIELD_STRENGTH_Y): cv.nameable(field_strength_schema), - cv.Optional(CONF_FIELD_STRENGTH_Z): cv.nameable(field_strength_schema), - cv.Optional(CONF_HEADING): cv.nameable(heading_schema), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, + cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema, + cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema, + cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema, + cv.Optional(CONF_HEADING): heading_schema, cv.Optional(CONF_RANGE, default='130uT'): validate_range, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x1E)) +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x1E)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) - cg.add(var.set_range(HMC5883L_RANGES[config[CONF_RANGE]])) + cg.add(var.set_range(config[CONF_RANGE])) if CONF_FIELD_STRENGTH_X in config: sens = yield sensor.new_sensor(config[CONF_FIELD_STRENGTH_X]) cg.add(var.set_x_sensor(sens)) diff --git a/esphome/components/homeassistant/binary_sensor/__init__.py b/esphome/components/homeassistant/binary_sensor/__init__.py index e738f2f1b..b78836f18 100644 --- a/esphome/components/homeassistant/binary_sensor/__init__.py +++ b/esphome/components/homeassistant/binary_sensor/__init__.py @@ -1,20 +1,22 @@ -from esphome.components import binary_sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ENTITY_ID, CONF_ID, CONF_NAME +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_ENTITY_ID, CONF_ID from .. import homeassistant_ns DEPENDENCIES = ['api'] HomeassistantBinarySensor = homeassistant_ns.class_('HomeassistantBinarySensor', binary_sensor.BinarySensor) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(HomeassistantBinarySensor), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(HomeassistantBinarySensor), cv.Required(CONF_ENTITY_ID): cv.entity_id, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_ENTITY_ID]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield binary_sensor.register_binary_sensor(var, config) + + cg.add(var.set_entity_id(config[CONF_ENTITY_ID])) diff --git a/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.cpp b/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.cpp index 801ee5aab..61c73d272 100644 --- a/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.cpp +++ b/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.cpp @@ -31,8 +31,6 @@ void HomeassistantBinarySensor::dump_config() { ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str()); } float HomeassistantBinarySensor::get_setup_priority() const { return setup_priority::AFTER_WIFI; } -HomeassistantBinarySensor::HomeassistantBinarySensor(const std::string &name, const std::string &entity_id) - : BinarySensor(name), entity_id_(entity_id) {} } // namespace homeassistant } // namespace esphome diff --git a/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.h b/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.h index fd675d31f..c2c7ec448 100644 --- a/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.h +++ b/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.h @@ -8,7 +8,7 @@ namespace homeassistant { class HomeassistantBinarySensor : public binary_sensor::BinarySensor, public Component { public: - HomeassistantBinarySensor(const std::string &name, const std::string &entity_id); + void set_entity_id(const std::string &entity_id) { entity_id_ = entity_id; } void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/homeassistant/sensor/__init__.py b/esphome/components/homeassistant/sensor/__init__.py index 23aeba77d..cd5e4a74e 100644 --- a/esphome/components/homeassistant/sensor/__init__.py +++ b/esphome/components/homeassistant/sensor/__init__.py @@ -1,20 +1,22 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ENTITY_ID, CONF_ID, CONF_NAME +from esphome.const import CONF_ENTITY_ID, CONF_ID, ICON_EMPTY, UNIT_EMPTY from .. import homeassistant_ns DEPENDENCIES = ['api'] HomeassistantSensor = homeassistant_ns.class_('HomeassistantSensor', sensor.Sensor) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(HomeassistantSensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({ + cv.GenerateID(): cv.declare_id(HomeassistantSensor), cv.Required(CONF_ENTITY_ID): cv.entity_id, -})) +}) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_ENTITY_ID]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) + + cg.add(var.set_entity_id(config[CONF_ENTITY_ID])) diff --git a/esphome/components/homeassistant/sensor/homeassistant_sensor.cpp b/esphome/components/homeassistant/sensor/homeassistant_sensor.cpp index 63555e61b..6b1299f70 100644 --- a/esphome/components/homeassistant/sensor/homeassistant_sensor.cpp +++ b/esphome/components/homeassistant/sensor/homeassistant_sensor.cpp @@ -7,8 +7,6 @@ namespace homeassistant { static const char *TAG = "homeassistant.sensor"; -HomeassistantSensor::HomeassistantSensor(const std::string &name, const std::string &entity_id) - : Sensor(name), entity_id_(entity_id) {} void HomeassistantSensor::setup() { api::global_api_server->subscribe_home_assistant_state(this->entity_id_, [this](std::string state) { auto val = parse_float(state); diff --git a/esphome/components/homeassistant/sensor/homeassistant_sensor.h b/esphome/components/homeassistant/sensor/homeassistant_sensor.h index 0d7ed61b1..baca6594c 100644 --- a/esphome/components/homeassistant/sensor/homeassistant_sensor.h +++ b/esphome/components/homeassistant/sensor/homeassistant_sensor.h @@ -8,7 +8,7 @@ namespace homeassistant { class HomeassistantSensor : public sensor::Sensor, public Component { public: - HomeassistantSensor(const std::string &name, const std::string &entity_id); + void set_entity_id(const std::string &entity_id) { entity_id_ = entity_id; } void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/homeassistant/text_sensor/__init__.py b/esphome/components/homeassistant/text_sensor/__init__.py index 105be3cb5..2d06473f3 100644 --- a/esphome/components/homeassistant/text_sensor/__init__.py +++ b/esphome/components/homeassistant/text_sensor/__init__.py @@ -1,7 +1,7 @@ -from esphome.components import text_sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ENTITY_ID, CONF_ID, CONF_NAME +import esphome.config_validation as cv +from esphome.components import text_sensor +from esphome.const import CONF_ENTITY_ID, CONF_ID from .. import homeassistant_ns DEPENDENCIES = ['api'] @@ -9,13 +9,15 @@ DEPENDENCIES = ['api'] HomeassistantTextSensor = homeassistant_ns.class_('HomeassistantTextSensor', text_sensor.TextSensor, cg.Component) -CONFIG_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(HomeassistantTextSensor), +CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(HomeassistantTextSensor), cv.Required(CONF_ENTITY_ID): cv.entity_id, -})) +}) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_ENTITY_ID]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield text_sensor.register_text_sensor(var, config) + + cg.add(var.set_entity_id(config[CONF_ENTITY_ID])) diff --git a/esphome/components/homeassistant/text_sensor/homeassistant_text_sensor.h b/esphome/components/homeassistant/text_sensor/homeassistant_text_sensor.h index d38fd288e..02a74af1d 100644 --- a/esphome/components/homeassistant/text_sensor/homeassistant_text_sensor.h +++ b/esphome/components/homeassistant/text_sensor/homeassistant_text_sensor.h @@ -8,8 +8,7 @@ namespace homeassistant { class HomeassistantTextSensor : public text_sensor::TextSensor, public Component { public: - HomeassistantTextSensor(const std::string &name, const std::string &entity_id) - : TextSensor(name), entity_id_(entity_id) {} + void set_entity_id(const std::string &entity_id) { entity_id_ = entity_id; } void dump_config() override; void setup() override; diff --git a/esphome/components/homeassistant/time/__init__.py b/esphome/components/homeassistant/time/__init__.py index 233be2945..fd40de68d 100644 --- a/esphome/components/homeassistant/time/__init__.py +++ b/esphome/components/homeassistant/time/__init__.py @@ -9,7 +9,7 @@ DEPENDENCIES = ['api'] HomeassistantTime = homeassistant_ns.class_('HomeassistantTime', time_.RealTimeClock) CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(HomeassistantTime), + cv.GenerateID(): cv.declare_id(HomeassistantTime), }).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/homeassistant/time/homeassistant_time.cpp b/esphome/components/homeassistant/time/homeassistant_time.cpp index 5e0004bff..b21fd4c0c 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.cpp +++ b/esphome/components/homeassistant/time/homeassistant_time.cpp @@ -14,7 +14,7 @@ float HomeassistantTime::get_setup_priority() const { return setup_priority::DAT void HomeassistantTime::setup() { global_homeassistant_time = this; - this->set_interval(15 * 60 * 1000, [this]() { + this->set_interval(15 * 60 * 1000, []() { // re-request time every 15 minutes api::global_api_server->request_time(); }); diff --git a/esphome/components/htu21d/htu21d.h b/esphome/components/htu21d/htu21d.h index 4b18b7ae5..a408f06d0 100644 --- a/esphome/components/htu21d/htu21d.h +++ b/esphome/components/htu21d/htu21d.h @@ -9,7 +9,6 @@ namespace htu21d { class HTU21DComponent : public PollingComponent, public i2c::I2CDevice { public: - HTU21DComponent(uint32_t update_interval) : PollingComponent(update_interval) {} void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } diff --git a/esphome/components/htu21d/sensor.py b/esphome/components/htu21d/sensor.py index 880a87165..20053d27d 100644 --- a/esphome/components/htu21d/sensor.py +++ b/esphome/components/htu21d/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL, ICON_THERMOMETER, UNIT_CELSIUS, UNIT_PERCENT, ICON_WATER_PERCENT + ICON_THERMOMETER, UNIT_CELSIUS, UNIT_PERCENT, ICON_WATER_PERCENT DEPENDENCIES = ['i2c'] @@ -10,17 +10,14 @@ htu21d_ns = cg.esphome_ns.namespace('htu21d') HTU21DComponent = htu21d_ns.class_('HTU21DComponent', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(HTU21DComponent), - cv.Required(CONF_TEMPERATURE): cv.nameable( - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1)), - cv.Required(CONF_HUMIDITY): cv.nameable( - sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x40)) + cv.GenerateID(): cv.declare_id(HTU21DComponent), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/hx711/hx711.cpp b/esphome/components/hx711/hx711.cpp index 8c5dfad41..efa9e7b26 100644 --- a/esphome/components/hx711/hx711.cpp +++ b/esphome/components/hx711/hx711.cpp @@ -58,10 +58,6 @@ bool HX711Sensor::read_sensor_(uint32_t *result) { *result = data; return true; } -HX711Sensor::HX711Sensor(const std::string &name, GPIOPin *dout, GPIOPin *sck, uint32_t update_interval) - : PollingSensorComponent(name, update_interval), dout_pin_(dout), sck_pin_(sck) {} - -void HX711Sensor::set_gain(HX711Gain gain) { this->gain_ = gain; } } // namespace hx711 } // namespace esphome diff --git a/esphome/components/hx711/hx711.h b/esphome/components/hx711/hx711.h index 81c78003e..91c8317ee 100644 --- a/esphome/components/hx711/hx711.h +++ b/esphome/components/hx711/hx711.h @@ -13,17 +13,17 @@ enum HX711Gain { HX711_GAIN_64 = 3, }; -class HX711Sensor : public sensor::PollingSensorComponent { +class HX711Sensor : public sensor::Sensor, public PollingComponent { public: - HX711Sensor(const std::string &name, GPIOPin *dout, GPIOPin *sck, uint32_t update_interval); + void set_dout_pin(GPIOPin *dout_pin) { dout_pin_ = dout_pin; } + void set_sck_pin(GPIOPin *sck_pin) { sck_pin_ = sck_pin; } + void set_gain(HX711Gain gain) { gain_ = gain; } void setup() override; void dump_config() override; float get_setup_priority() const override; void update() override; - void set_gain(HX711Gain gain); - protected: bool read_sensor_(uint32_t *result); diff --git a/esphome/components/hx711/sensor.py b/esphome/components/hx711/sensor.py index ec7df245f..bc2239706 100644 --- a/esphome/components/hx711/sensor.py +++ b/esphome/components/hx711/sensor.py @@ -1,9 +1,8 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -import esphome.config_validation as cv -import esphome.codegen as cg -from esphome.const import CONF_CLK_PIN, CONF_GAIN, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, \ - ICON_SCALE +from esphome.const import CONF_CLK_PIN, CONF_GAIN, CONF_ID, ICON_SCALE hx711_ns = cg.esphome_ns.namespace('hx711') HX711Sensor = hx711_ns.class_('HX711Sensor', sensor.PollingSensorComponent) @@ -17,21 +16,21 @@ GAINS = { 64: HX711Gain.HX711_GAIN_64, } -CONFIG_SCHEMA = cv.nameable(sensor.sensor_schema('', ICON_SCALE, 0).extend({ - cv.GenerateID(): cv.declare_variable_id(HX711Sensor), +CONFIG_SCHEMA = sensor.sensor_schema('', ICON_SCALE, 0).extend({ + cv.GenerateID(): cv.declare_id(HX711Sensor), cv.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema, cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_GAIN, default=128): cv.one_of(*GAINS, int=True), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA)) + cv.Optional(CONF_GAIN, default=128): cv.enum(GAINS, int=True), +}).extend(cv.polling_component_schema('60s')) def to_code(config): - dout_pin = yield cg.gpio_pin_expression(config[CONF_DOUT_PIN]) - sck_pin = yield cg.gpio_pin_expression(config[CONF_CLK_PIN]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], dout_pin, sck_pin, - config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) - cg.add(var.set_gain(GAINS[config[CONF_GAIN]])) + dout_pin = yield cg.gpio_pin_expression(config[CONF_DOUT_PIN]) + cg.add(var.set_dout_pin(dout_pin)) + sck_pin = yield cg.gpio_pin_expression(config[CONF_CLK_PIN]) + cg.add(var.set_sck_pin(sck_pin)) + cg.add(var.set_gain(config[CONF_GAIN])) diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index 1a0e01668..733dcc649 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -11,7 +11,7 @@ I2CDevice = i2c_ns.class_('I2CDevice') MULTI_CONF = True CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(I2CComponent), + cv.GenerateID(): cv.declare_id(I2CComponent), cv.Optional(CONF_SDA, default='SDA'): pins.input_pin, cv.Optional(CONF_SCL, default='SCL'): pins.input_pin, cv.Optional(CONF_FREQUENCY, default='50kHz'): @@ -21,17 +21,26 @@ CONFIG_SCHEMA = cv.Schema({ def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_SDA], config[CONF_SCL], - int(config[CONF_FREQUENCY]), config[CONF_SCAN]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + cg.add(var.set_sda_pin(config[CONF_SDA])) + cg.add(var.set_scl_pin(config[CONF_SCL])) + cg.add(var.set_frequency(int(config[CONF_FREQUENCY]))) + cg.add(var.set_scan(config[CONF_SCAN])) cg.add_library('Wire', None) cg.add_global(i2c_ns.using) def i2c_device_schema(default_address): + """Create a schema for a i2c device. + + :param default_address: The default address of the i2c device, can be None to represent + a required option. + :return: The i2c device schema, `extend` this in your config schema. + """ schema = { - cv.GenerateID(CONF_I2C_ID): cv.use_variable_id(I2CComponent), + cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CComponent), } if default_address is None: schema[cv.Required(CONF_ADDRESS)] = cv.i2c_address @@ -42,6 +51,12 @@ def i2c_device_schema(default_address): @coroutine def register_i2c_device(var, config): + """Register an i2c device with the given config. + + Sets the i2c bus to use and the i2c address. + + This is a coroutine, you need to await it with a 'yield' expression! + """ parent = yield cg.get_variable(config[CONF_I2C_ID]) cg.add(var.set_i2c_parent(parent)) cg.add(var.set_i2c_address(config[CONF_ADDRESS])) diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index d24c9e844..834fb1334 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -8,8 +8,7 @@ namespace i2c { static const char *TAG = "i2c"; -I2CComponent::I2CComponent(uint8_t sda_pin, uint8_t scl_pin, uint32_t frequency, bool scan) - : sda_pin_(sda_pin), scl_pin_(scl_pin), frequency_(frequency), scan_(scan) { +I2CComponent::I2CComponent() { #ifdef ARDUINO_ARCH_ESP32 if (next_i2c_bus_num_ == 0) this->wire_ = &Wire; diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index e8a13daaa..0184c8020 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -25,7 +25,11 @@ namespace i2c { */ class I2CComponent : public Component { public: - I2CComponent(uint8_t sda_pin, uint8_t scl_pin, uint32_t frequency, bool scan); + I2CComponent(); + void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } + void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } + void set_frequency(uint32_t frequency) { frequency_ = frequency; } + void set_scan(bool scan) { scan_ = scan; } /** Read len amount of bytes from a register into data. Optionally with a conversion time after * writing the register value to the bus. diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index a7db6b5ab..d933af1a9 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -17,10 +17,10 @@ Image_ = display.display_ns.class_('Image') CONF_RAW_DATA_ID = 'raw_data_id' IMAGE_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_variable_id(Image_), + cv.Required(CONF_ID): cv.declare_id(Image_), cv.Required(CONF_FILE): cv.file_, cv.Optional(CONF_RESIZE): cv.dimensions, - cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(cg.uint8), + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), }) CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA) @@ -29,7 +29,7 @@ CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA) def to_code(config): from PIL import Image - path = CORE.relative_path(config[CONF_FILE]) + path = CORE.relative_config_path(config[CONF_FILE]) try: image = Image.open(path) except Exception as e: diff --git a/esphome/components/ina219/ina219.h b/esphome/components/ina219/ina219.h index 1df4e9726..31cd22375 100644 --- a/esphome/components/ina219/ina219.h +++ b/esphome/components/ina219/ina219.h @@ -9,7 +9,6 @@ namespace ina219 { class INA219Component : public PollingComponent, public i2c::I2CDevice { public: - INA219Component(uint32_t update_interval) : PollingComponent(update_interval) {} void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/ina219/sensor.py b/esphome/components/ina219/sensor.py index a3b60be43..a6f415edb 100644 --- a/esphome/components/ina219/sensor.py +++ b/esphome/components/ina219/sensor.py @@ -4,7 +4,7 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, \ CONF_MAX_CURRENT, CONF_MAX_VOLTAGE, CONF_POWER, CONF_SHUNT_RESISTANCE, \ - CONF_SHUNT_VOLTAGE, CONF_UPDATE_INTERVAL, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT + CONF_SHUNT_VOLTAGE, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT DEPENDENCIES = ['i2c'] @@ -12,21 +12,20 @@ ina219_ns = cg.esphome_ns.namespace('ina219') INA219Component = ina219_ns.class_('INA219Component', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(INA219Component), - cv.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2)), - cv.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2)), - cv.Optional(CONF_CURRENT): cv.nameable(sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 3)), - cv.Optional(CONF_POWER): cv.nameable(sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2)), + cv.GenerateID(): cv.declare_id(INA219Component), + cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), + cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 3), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, cv.Range(min=0.0, max=32.0)), cv.Optional(CONF_MAX_VOLTAGE, default=32.0): cv.All(cv.voltage, cv.Range(min=0.0, max=32.0)), cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(cv.current, cv.Range(min=0.0)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x40)) +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/ina3221/ina3221.h b/esphome/components/ina3221/ina3221.h index b30fea4ee..f593badc0 100644 --- a/esphome/components/ina3221/ina3221.h +++ b/esphome/components/ina3221/ina3221.h @@ -9,7 +9,6 @@ namespace ina3221 { class INA3221Component : public PollingComponent, public i2c::I2CDevice { public: - INA3221Component(uint32_t update_interval) : PollingComponent(update_interval) {} void setup() override; void dump_config() override; void update() override; diff --git a/esphome/components/ina3221/sensor.py b/esphome/components/ina3221/sensor.py index 9e240e8b0..199f7be62 100644 --- a/esphome/components/ina3221/sensor.py +++ b/esphome/components/ina3221/sensor.py @@ -3,7 +3,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_POWER, \ - CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, CONF_UPDATE_INTERVAL, ICON_FLASH, \ + CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, ICON_FLASH, \ UNIT_VOLT, UNIT_AMPERE, UNIT_WATT DEPENDENCIES = ['i2c'] @@ -16,25 +16,24 @@ ina3221_ns = cg.esphome_ns.namespace('ina3221') INA3221Component = ina3221_ns.class_('INA3221Component', cg.PollingComponent, i2c.I2CDevice) INA3221_CHANNEL_SCHEMA = cv.Schema({ - cv.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2)), - cv.Optional(CONF_SHUNT_VOLTAGE): cv.nameable(sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2)), - cv.Optional(CONF_CURRENT): cv.nameable(sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2)), - cv.Optional(CONF_POWER): cv.nameable(sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2)), + cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), + cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, cv.Range(min=0.0, max=32.0)), }) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(INA3221Component), + cv.GenerateID(): cv.declare_id(INA3221Component), cv.Optional(CONF_CHANNEL_1): INA3221_CHANNEL_SCHEMA, cv.Optional(CONF_CHANNEL_2): INA3221_CHANNEL_SCHEMA, cv.Optional(CONF_CHANNEL_3): INA3221_CHANNEL_SCHEMA, - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x40)) +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/interval/__init__.py b/esphome/components/interval/__init__.py index a4a423547..e0816b740 100644 --- a/esphome/components/interval/__init__.py +++ b/esphome/components/interval/__init__.py @@ -4,16 +4,19 @@ from esphome import automation from esphome.const import CONF_ID, CONF_INTERVAL interval_ns = cg.esphome_ns.namespace('interval') -IntervalTrigger = interval_ns.class_('IntervalTrigger', cg.Trigger.template(), cg.PollingComponent) +IntervalTrigger = interval_ns.class_('IntervalTrigger', automation.Trigger.template(), + cg.PollingComponent) CONFIG_SCHEMA = automation.validate_automation(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(IntervalTrigger), + cv.GenerateID(): cv.declare_id(IntervalTrigger), cv.Required(CONF_INTERVAL): cv.positive_time_period_milliseconds, }).extend(cv.COMPONENT_SCHEMA)) def to_code(config): for conf in config: - var = cg.new_Pvariable(conf[CONF_ID], conf[CONF_INTERVAL]) + var = cg.new_Pvariable(conf[CONF_ID]) yield cg.register_component(var, conf) yield automation.build_automation(var, [], conf) + + cg.add(var.set_update_interval(conf[CONF_INTERVAL])) diff --git a/esphome/components/interval/interval.h b/esphome/components/interval/interval.h index a8365815d..605ac868f 100644 --- a/esphome/components/interval/interval.h +++ b/esphome/components/interval/interval.h @@ -8,7 +8,6 @@ namespace interval { class IntervalTrigger : public Trigger<>, public PollingComponent { public: - IntervalTrigger(uint32_t update_interval) : PollingComponent(update_interval) {} void update() override { this->trigger(); } float get_setup_priority() const override { return setup_priority::DATA; } }; diff --git a/esphome/components/lcd_base/__init__.py b/esphome/components/lcd_base/__init__.py index 3cbda9e56..27f65f933 100644 --- a/esphome/components/lcd_base/__init__.py +++ b/esphome/components/lcd_base/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import display -from esphome.const import CONF_DIMENSIONS, CONF_LAMBDA, CONF_UPDATE_INTERVAL +from esphome.const import CONF_DIMENSIONS, CONF_LAMBDA from esphome.core import coroutine lcd_base_ns = cg.esphome_ns.namespace('lcd_base') @@ -20,8 +20,7 @@ def validate_lcd_dimensions(value): LCD_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ cv.Required(CONF_DIMENSIONS): validate_lcd_dimensions, - cv.Optional(CONF_UPDATE_INTERVAL, default='1s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA) +}).extend(cv.polling_component_schema('1s')) @coroutine diff --git a/esphome/components/lcd_base/lcd_display.h b/esphome/components/lcd_base/lcd_display.h index 366f86973..200600eb9 100644 --- a/esphome/components/lcd_base/lcd_display.h +++ b/esphome/components/lcd_base/lcd_display.h @@ -16,8 +16,6 @@ using lcd_writer_t = std::function; class LCDDisplay : public PollingComponent { public: - LCDDisplay(uint32_t update_interval) : PollingComponent(update_interval) {} - void set_writer(lcd_writer_t &&writer) { this->writer_ = std::move(writer); } void set_dimensions(uint8_t columns, uint8_t rows) { this->columns_ = columns; diff --git a/esphome/components/lcd_gpio/display.py b/esphome/components/lcd_gpio/display.py index 435299058..1f98955ec 100644 --- a/esphome/components/lcd_gpio/display.py +++ b/esphome/components/lcd_gpio/display.py @@ -2,8 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import lcd_base -from esphome.const import CONF_DATA_PINS, CONF_ENABLE_PIN, CONF_RS_PIN, CONF_RW_PIN, CONF_ID, \ - CONF_UPDATE_INTERVAL +from esphome.const import CONF_DATA_PINS, CONF_ENABLE_PIN, CONF_RS_PIN, CONF_RW_PIN, CONF_ID AUTO_LOAD = ['lcd_base'] @@ -19,7 +18,7 @@ def validate_pin_length(value): CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(GPIOLCDDisplay), + cv.GenerateID(): cv.declare_id(GPIOLCDDisplay), cv.Required(CONF_DATA_PINS): cv.All([pins.gpio_output_pin_schema], validate_pin_length), cv.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_RS_PIN): pins.gpio_output_pin_schema, @@ -28,7 +27,7 @@ CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend({ def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield lcd_base.setup_lcd_display(var, config) pins_ = [] for conf in config[CONF_DATA_PINS]: diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.h b/esphome/components/lcd_gpio/gpio_lcd_display.h index 121b843fa..ed3b0c113 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.h +++ b/esphome/components/lcd_gpio/gpio_lcd_display.h @@ -8,8 +8,6 @@ namespace lcd_gpio { class GPIOLCDDisplay : public lcd_base::LCDDisplay { public: - GPIOLCDDisplay(uint32_t update_interval) : LCDDisplay(update_interval) {} - void setup() override; void set_data_pins(GPIOPin *d0, GPIOPin *d1, GPIOPin *d2, GPIOPin *d3) { this->data_pins_[0] = d0; diff --git a/esphome/components/lcd_pcf8574/display.py b/esphome/components/lcd_pcf8574/display.py index bdfd90d57..2bc04a283 100644 --- a/esphome/components/lcd_pcf8574/display.py +++ b/esphome/components/lcd_pcf8574/display.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import lcd_base, i2c -from esphome.const import CONF_ID, CONF_UPDATE_INTERVAL +from esphome.const import CONF_ID DEPENDENCIES = ['i2c'] AUTO_LOAD = ['lcd_base'] @@ -10,11 +10,11 @@ lcd_pcf8574_ns = cg.esphome_ns.namespace('lcd_pcf8574') PCF8574LCDDisplay = lcd_pcf8574_ns.class_('PCF8574LCDDisplay', lcd_base.LCDDisplay, i2c.I2CDevice) CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(PCF8574LCDDisplay), + cv.GenerateID(): cv.declare_id(PCF8574LCDDisplay), }).extend(i2c.i2c_device_schema(0x3F)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield lcd_base.setup_lcd_display(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.h b/esphome/components/lcd_pcf8574/pcf8574_display.h index d491e53da..133679c50 100644 --- a/esphome/components/lcd_pcf8574/pcf8574_display.h +++ b/esphome/components/lcd_pcf8574/pcf8574_display.h @@ -9,7 +9,6 @@ namespace lcd_pcf8574 { class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice { public: - PCF8574LCDDisplay(uint32_t update_interval) : lcd_base::LCDDisplay(update_interval) {} void setup() override; void dump_config() override; diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index 1a45ad17e..fd7594267 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -22,7 +22,7 @@ ledc_ns = cg.esphome_ns.namespace('ledc') LEDCOutput = ledc_ns.class_('LEDCOutput', output.FloatOutput, cg.Component) CONFIG_SCHEMA = cv.All(output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_variable_id(LEDCOutput), + cv.Required(CONF_ID): cv.declare_id(LEDCOutput), cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_FREQUENCY, default='1kHz'): cv.frequency, cv.Optional(CONF_BIT_DEPTH, default=12): cv.All(cv.int_, cv.Range(min=1, max=15)), diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 9e550e93b..db0601496 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.automation import ACTION_REGISTRY, maybe_simple_id +from esphome import automation +from esphome.automation import maybe_simple_id from esphome.components import mqtt from esphome.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, CONF_COLOR_CORRECT, \ CONF_COLOR_TEMPERATURE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_DURATION, CONF_EFFECT, \ @@ -9,7 +10,7 @@ from esphome.const import CONF_ALPHA, CONF_BLUE, CONF_BRIGHTNESS, CONF_COLORS, C CONF_SPEED, CONF_STATE, CONF_TRANSITION_LENGTH, CONF_UPDATE_INTERVAL, CONF_WHITE, CONF_WIDTH, \ CONF_MQTT_ID from esphome.core import coroutine -from esphome.util import ServiceRegistry +from esphome.util import Registry IS_PLATFORM_COMPONENT = True @@ -23,8 +24,8 @@ AddressableLight = light_ns.class_('AddressableLight') AddressableLightRef = AddressableLight.operator('ref') # Actions -ToggleAction = light_ns.class_('ToggleAction', cg.Action) -LightControlAction = light_ns.class_('LightControlAction', cg.Action) +ToggleAction = light_ns.class_('ToggleAction', automation.Action) +LightControlAction = light_ns.class_('LightControlAction', automation.Action) LightColorValues = light_ns.class_('LightColorValues') @@ -78,27 +79,24 @@ ADDRESSABLE_EFFECTS = RGB_EFFECTS + [CONF_ADDRESSABLE_LAMBDA, CONF_ADDRESSABLE_R CONF_ADDRESSABLE_TWINKLE, CONF_ADDRESSABLE_RANDOM_TWINKLE, CONF_ADDRESSABLE_FIREWORKS, CONF_ADDRESSABLE_FLICKER] -EFFECTS_REGISTRY = ServiceRegistry() +EFFECTS_REGISTRY = Registry() def register_effect(name, effect_type, default_name, schema, *extra_validators): schema = cv.Schema(schema).extend({ - cv.GenerateID(): cv.declare_variable_id(effect_type), cv.Optional(CONF_NAME, default=default_name): cv.string_strict, }) validator = cv.All(schema, *extra_validators) - register = EFFECTS_REGISTRY.register(name, validator) - - return register + return EFFECTS_REGISTRY.register(name, effect_type, validator) @register_effect('lambda', LambdaLightEffect, "Lambda", { cv.Required(CONF_LAMBDA): cv.lambda_, cv.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.update_interval, }) -def lambda_effect_to_code(config): +def lambda_effect_to_code(config, effect_id): lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [], return_type=cg.void) - yield cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], lambda_, + yield cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL]) @@ -106,8 +104,8 @@ def lambda_effect_to_code(config): cv.Optional(CONF_TRANSITION_LENGTH, default='7.5s'): cv.positive_time_period_milliseconds, cv.Optional(CONF_UPDATE_INTERVAL, default='10s'): cv.positive_time_period_milliseconds, }) -def random_effect_to_code(config): - effect = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def random_effect_to_code(config, effect_id): + effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(effect.set_transition_length(config[CONF_TRANSITION_LENGTH])) cg.add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL])) yield effect @@ -128,8 +126,8 @@ def random_effect_to_code(config): }), cv.has_at_least_one_key(CONF_STATE, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE)), cv.Length(min=2)), }) -def strobe_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def strobe_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) colors = [] for color in config.get(CONF_COLORS, []): colors.append(cg.StructInitializer( @@ -147,8 +145,8 @@ def strobe_effect_to_code(config): cv.Optional(CONF_ALPHA, default=0.95): cv.percentage, cv.Optional(CONF_INTENSITY, default=0.015): cv.percentage, }) -def flicker_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def flicker_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_alpha(config[CONF_ALPHA])) cg.add(var.set_intensity(config[CONF_INTENSITY])) yield var @@ -158,10 +156,10 @@ def flicker_effect_to_code(config): cv.Required(CONF_LAMBDA): cv.lambda_, cv.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds, }) -def addressable_lambda_effect_to_code(config): +def addressable_lambda_effect_to_code(config, effect_id): args = [(AddressableLightRef, 'it')] lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], args, return_type=cg.void) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], lambda_, + var = cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL]) yield var @@ -170,8 +168,8 @@ def addressable_lambda_effect_to_code(config): cv.Optional(CONF_SPEED, default=10): cv.uint32_t, cv.Optional(CONF_WIDTH, default=50): cv.uint32_t, }) -def addressable_rainbow_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def addressable_rainbow_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_speed(config[CONF_SPEED])) cg.add(var.set_width(config[CONF_WIDTH])) yield var @@ -189,8 +187,8 @@ def addressable_rainbow_effect_to_code(config): cv.Optional(CONF_ADD_LED_INTERVAL, default='0.1s'): cv.positive_time_period_milliseconds, cv.Optional(CONF_REVERSE, default=False): cv.boolean, }) -def addressable_color_wipe_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def addressable_color_wipe_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_add_led_interval(config[CONF_ADD_LED_INTERVAL])) cg.add(var.set_reverse(config[CONF_REVERSE])) colors = [] @@ -211,8 +209,8 @@ def addressable_color_wipe_effect_to_code(config): @register_effect('addressable_scan', AddressableScanEffect, "Scan", { cv.Optional(CONF_MOVE_INTERVAL, default='0.1s'): cv.positive_time_period_milliseconds, }) -def addressable_scan_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def addressable_scan_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_move_interval(config[CONF_MOVE_INTERVAL])) yield var @@ -221,8 +219,8 @@ def addressable_scan_effect_to_code(config): cv.Optional(CONF_TWINKLE_PROBABILITY, default='5%'): cv.percentage, cv.Optional(CONF_PROGRESS_INTERVAL, default='4ms'): cv.positive_time_period_milliseconds, }) -def addressable_twinkle_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def addressable_twinkle_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY])) cg.add(var.set_progress_interval(config[CONF_PROGRESS_INTERVAL])) yield var @@ -232,8 +230,8 @@ def addressable_twinkle_effect_to_code(config): cv.Optional(CONF_TWINKLE_PROBABILITY, default='5%'): cv.percentage, cv.Optional(CONF_PROGRESS_INTERVAL, default='32ms'): cv.positive_time_period_milliseconds, }) -def addressable_random_twinkle_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def addressable_random_twinkle_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY])) cg.add(var.set_progress_interval(config[CONF_PROGRESS_INTERVAL])) yield var @@ -245,8 +243,8 @@ def addressable_random_twinkle_effect_to_code(config): cv.Optional(CONF_USE_RANDOM_COLOR, default=False): cv.boolean, cv.Optional(CONF_FADE_OUT_RATE, default=120): cv.uint8_t, }) -def addressable_fireworks_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def addressable_fireworks_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_update_interval(config[CONF_UPDATE_INTERVAL])) cg.add(var.set_spark_probability(config[CONF_SPARK_PROBABILITY])) cg.add(var.set_use_random_color(config[CONF_USE_RANDOM_COLOR])) @@ -258,8 +256,8 @@ def addressable_fireworks_effect_to_code(config): cv.Optional(CONF_UPDATE_INTERVAL, default='16ms'): cv.positive_time_period_milliseconds, cv.Optional(CONF_INTENSITY, default='5%'): cv.percentage, }) -def addressable_flicker_effect_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) +def addressable_flicker_effect_to_code(config, effect_id): + var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_update_interval(config[CONF_UPDATE_INTERVAL])) cg.add(var.set_intensity(config[CONF_INTENSITY])) yield var @@ -267,7 +265,7 @@ def addressable_flicker_effect_to_code(config): def validate_effects(allowed_effects): def validator(value): - value = cv.validate_registry('effect', EFFECTS_REGISTRY, [])(value) + value = cv.validate_registry('effect', EFFECTS_REGISTRY)(value) errors = [] names = set() for i, x in enumerate(value): @@ -294,8 +292,8 @@ def validate_effects(allowed_effects): LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(LightState), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTJSONLightComponent), + cv.GenerateID(): cv.declare_id(LightState), + cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTJSONLightComponent), }) BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend({ @@ -313,7 +311,7 @@ RGB_LIGHT_SCHEMA = BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend({ }) ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(AddressableLightState), + cv.GenerateID(): cv.declare_id(AddressableLightState), cv.Optional(CONF_EFFECTS): validate_effects(ADDRESSABLE_EFFECTS), cv.Optional(CONF_COLOR_CORRECT): cv.All([cv.percentage], cv.Length(min=3, max=4)), }) @@ -346,38 +344,21 @@ def register_light(output_var, config): yield setup_light_core_(light_var, output_var, config) -@ACTION_REGISTRY.register('light.toggle', maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(LightState), +@automation.register_action('light.toggle', ToggleAction, maybe_simple_id({ + cv.Required(CONF_ID): cv.use_id(LightState), cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), })) def light_toggle_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ToggleAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) if CONF_TRANSITION_LENGTH in config: template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) - cg.add(action.set_transition_length(template_)) - yield action - - -@ACTION_REGISTRY.register('light.turn_off', maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(LightState), - cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), -})) -def light_turn_off_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = LightControlAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) - if CONF_TRANSITION_LENGTH in config: - template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) - cg.add(action.set_transition_length(template_)) - yield action + cg.add(var.set_transition_length(template_)) + yield var LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(LightState), + cv.Required(CONF_ID): cv.use_id(LightState), cv.Optional(CONF_STATE): cv.templatable(cv.boolean), cv.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'): cv.templatable(cv.positive_time_period_milliseconds), @@ -392,7 +373,7 @@ LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema({ cv.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.color_temperature), }) LIGHT_TURN_OFF_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(LightState), + cv.Required(CONF_ID): cv.use_id(LightState), cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), cv.Optional(CONF_STATE, default=False): False, }) @@ -401,45 +382,43 @@ LIGHT_TURN_ON_ACTION_SCHEMA = maybe_simple_id(LIGHT_CONTROL_ACTION_SCHEMA.extend })) -@ACTION_REGISTRY.register('light.turn_off', LIGHT_TURN_OFF_ACTION_SCHEMA) -@ACTION_REGISTRY.register('light.turn_on', LIGHT_TURN_ON_ACTION_SCHEMA) -@ACTION_REGISTRY.register('light.control', LIGHT_CONTROL_ACTION_SCHEMA) -def light_control_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = LightControlAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) +@automation.register_action('light.turn_off', LightControlAction, LIGHT_TURN_OFF_ACTION_SCHEMA) +@automation.register_action('light.turn_on', LightControlAction, LIGHT_TURN_ON_ACTION_SCHEMA) +@automation.register_action('light.control', LightControlAction, LIGHT_CONTROL_ACTION_SCHEMA) +def light_control_to_code(config, var, template_arg, args): + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(var, template_arg, paren) if CONF_STATE in config: template_ = yield cg.templatable(config[CONF_STATE], args, bool) - cg.add(action.set_state(template_)) + cg.add(var.set_state(template_)) if CONF_TRANSITION_LENGTH in config: template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) - cg.add(action.set_transition_length(template_)) + cg.add(var.set_transition_length(template_)) if CONF_FLASH_LENGTH in config: template_ = yield cg.templatable(config[CONF_FLASH_LENGTH], args, cg.uint32) - cg.add(action.set_flash_length(template_)) + cg.add(var.set_flash_length(template_)) if CONF_BRIGHTNESS in config: template_ = yield cg.templatable(config[CONF_BRIGHTNESS], args, float) - cg.add(action.set_brightness(template_)) + cg.add(var.set_brightness(template_)) if CONF_RED in config: template_ = yield cg.templatable(config[CONF_RED], args, float) - cg.add(action.set_red(template_)) + cg.add(var.set_red(template_)) if CONF_GREEN in config: template_ = yield cg.templatable(config[CONF_GREEN], args, float) - cg.add(action.set_green(template_)) + cg.add(var.set_green(template_)) if CONF_BLUE in config: template_ = yield cg.templatable(config[CONF_BLUE], args, float) - cg.add(action.set_blue(template_)) + cg.add(var.set_blue(template_)) if CONF_WHITE in config: template_ = yield cg.templatable(config[CONF_WHITE], args, float) - cg.add(action.set_white(template_)) + cg.add(var.set_white(template_)) if CONF_COLOR_TEMPERATURE in config: template_ = yield cg.templatable(config[CONF_COLOR_TEMPERATURE], args, float) - cg.add(action.set_color_temperature(template_)) + cg.add(var.set_color_temperature(template_)) if CONF_EFFECT in config: template_ = yield cg.templatable(config[CONF_EFFECT], args, cg.std_string) - cg.add(action.set_effect(template_)) - yield action + cg.add(var.set_effect(template_)) + yield var def to_code(config): diff --git a/esphome/components/light/automation.h b/esphome/components/light/automation.h index 4fd001f5b..c6ea678f3 100644 --- a/esphome/components/light/automation.h +++ b/esphome/components/light/automation.h @@ -16,7 +16,6 @@ template class ToggleAction : public Action { auto call = this->state_->toggle(); call.set_transition_length(this->transition_length_.optional_value(x...)); call.perform(); - this->play_next(x...); } protected: @@ -51,7 +50,6 @@ template class LightControlAction : public Action { call.set_flash_length(this->flash_length_.optional_value(x...)); call.set_transition_length(this->transition_length_.optional_value(x...)); call.perform(); - this->play_next(x...); } protected: diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index c8b3dbaa6..9e351d0b0 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -2,7 +2,8 @@ import re import esphome.codegen as cg import esphome.config_validation as cv -from esphome.automation import ACTION_REGISTRY, LambdaAction +from esphome import automation +from esphome.automation import LambdaAction from esphome.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_HARDWARE_UART, CONF_ID, \ CONF_LEVEL, CONF_LOGS, CONF_TAG, CONF_TX_BUFFER_SIZE from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority @@ -71,7 +72,7 @@ def validate_local_no_higher_than_global(value): Logger = logger_ns.class_('Logger', cg.Component) CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(Logger), + cv.GenerateID(): cv.declare_id(Logger), cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int, cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes, cv.Optional(CONF_HARDWARE_UART, default='UART0'): uart_selection, @@ -172,7 +173,7 @@ LOGGER_LOG_ACTION_SCHEMA = cv.All(maybe_simple_message({ }), validate_printf) -@ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA) +@automation.register_action(CONF_LOGGER_LOG, LambdaAction, LOGGER_LOG_ACTION_SCHEMA) def logger_log_action_to_code(config, action_id, template_arg, args): esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]] args_ = [cg.RawExpression(text_type(x)) for x in config[CONF_ARGS]] @@ -180,6 +181,4 @@ def logger_log_action_to_code(config, action_id, template_arg, args): text = text_type(cg.statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args_))) lambda_ = yield cg.process_lambda(Lambda(text), args, return_type=cg.void) - rhs = LambdaAction.new(template_arg, lambda_) - type = LambdaAction.template(template_arg) - yield cg.Pvariable(action_id, rhs, type=type) + yield cg.new_Pvariable(action_id, template_arg, lambda_) diff --git a/esphome/components/max31855/max31855.cpp b/esphome/components/max31855/max31855.cpp index 446517343..18a00b10d 100644 --- a/esphome/components/max31855/max31855.cpp +++ b/esphome/components/max31855/max31855.cpp @@ -6,9 +6,6 @@ namespace max31855 { static const char *TAG = "max31855"; -MAX31855Sensor::MAX31855Sensor(const std::string &name, uint32_t update_interval) - : PollingSensorComponent(name, update_interval) {} - void MAX31855Sensor::update() { this->enable(); delay(1); diff --git a/esphome/components/max31855/max31855.h b/esphome/components/max31855/max31855.h index b40799ff5..f9cdf335f 100644 --- a/esphome/components/max31855/max31855.h +++ b/esphome/components/max31855/max31855.h @@ -7,10 +7,8 @@ namespace esphome { namespace max31855 { -class MAX31855Sensor : public sensor::PollingSensorComponent, public spi::SPIDevice { +class MAX31855Sensor : public sensor::Sensor, public PollingComponent, public spi::SPIDevice { public: - MAX31855Sensor(const std::string &name, uint32_t update_interval); - void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/max31855/sensor.py b/esphome/components/max31855/sensor.py index d76766ca8..7178488eb 100644 --- a/esphome/components/max31855/sensor.py +++ b/esphome/components/max31855/sensor.py @@ -1,21 +1,18 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, \ - ICON_THERMOMETER, UNIT_CELSIUS +from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS max31855_ns = cg.esphome_ns.namespace('max31855') MAX31855Sensor = max31855_ns.class_('MAX31855Sensor', sensor.PollingSensorComponent, spi.SPIDevice) -CONFIG_SCHEMA = cv.nameable( - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ - cv.GenerateID(): cv.declare_variable_id(MAX31855Sensor), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, - }).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA)) +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ + cv.GenerateID(): cv.declare_id(MAX31855Sensor), +}).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield spi.register_spi_device(var, config) yield sensor.register_sensor(var, config) diff --git a/esphome/components/max6675/max6675.cpp b/esphome/components/max6675/max6675.cpp index 9918aafe8..e9bbfb4a0 100644 --- a/esphome/components/max6675/max6675.cpp +++ b/esphome/components/max6675/max6675.cpp @@ -6,9 +6,6 @@ namespace max6675 { static const char *TAG = "max6675"; -MAX6675Sensor::MAX6675Sensor(const std::string &name, uint32_t update_interval) - : PollingSensorComponent(name, update_interval) {} - void MAX6675Sensor::update() { this->enable(); delay(1); diff --git a/esphome/components/max6675/max6675.h b/esphome/components/max6675/max6675.h index e3e4f2061..48f51fbe1 100644 --- a/esphome/components/max6675/max6675.h +++ b/esphome/components/max6675/max6675.h @@ -7,10 +7,8 @@ namespace esphome { namespace max6675 { -class MAX6675Sensor : public sensor::PollingSensorComponent, public spi::SPIDevice { +class MAX6675Sensor : public sensor::Sensor, public PollingComponent, public spi::SPIDevice { public: - MAX6675Sensor(const std::string &name, uint32_t update_interval); - void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/max6675/sensor.py b/esphome/components/max6675/sensor.py index 9e27f483c..af089614f 100644 --- a/esphome/components/max6675/sensor.py +++ b/esphome/components/max6675/sensor.py @@ -1,21 +1,19 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, ICON_THERMOMETER, UNIT_CELSIUS +from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS max6675_ns = cg.esphome_ns.namespace('max6675') MAX6675Sensor = max6675_ns.class_('MAX6675Sensor', sensor.PollingSensorComponent, spi.SPIDevice) -CONFIG_SCHEMA = cv.nameable( - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ - cv.GenerateID(): cv.declare_variable_id(MAX6675Sensor), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, - }).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA)) +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ + cv.GenerateID(): cv.declare_id(MAX6675Sensor), +}).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield spi.register_spi_device(var, config) yield sensor.register_sensor(var, config) diff --git a/esphome/components/max7219/display.py b/esphome/components/max7219/display.py index 92f386a9d..b40fbafdd 100644 --- a/esphome/components/max7219/display.py +++ b/esphome/components/max7219/display.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import display, spi -from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS, CONF_UPDATE_INTERVAL +from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS DEPENDENCIES = ['spi'] @@ -10,16 +10,15 @@ MAX7219Component = max7219_ns.class_('MAX7219Component', cg.PollingComponent, sp MAX7219ComponentRef = MAX7219Component.operator('ref') CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(MAX7219Component), + cv.GenerateID(): cv.declare_id(MAX7219Component), - cv.Optional(CONF_UPDATE_INTERVAL, default='1s'): cv.update_interval, cv.Optional(CONF_NUM_CHIPS, default=1): cv.All(cv.uint8_t, cv.Range(min=1)), cv.Optional(CONF_INTENSITY, default=15): cv.All(cv.uint8_t, cv.Range(min=0, max=15)), -}).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA) +}).extend(cv.polling_component_schema('1s')).extend(spi.SPI_DEVICE_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield spi.register_spi_device(var, config) yield display.register_display(var, config) diff --git a/esphome/components/max7219/max7219.h b/esphome/components/max7219/max7219.h index 6b3c7c399..e2379fa69 100644 --- a/esphome/components/max7219/max7219.h +++ b/esphome/components/max7219/max7219.h @@ -18,8 +18,6 @@ using max7219_writer_t = std::function; class MAX7219Component : public PollingComponent, public spi::SPIDevice { public: - MAX7219Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void set_writer(max7219_writer_t &&writer); void setup() override; diff --git a/esphome/components/mcp23017/__init__.py b/esphome/components/mcp23017/__init__.py index 94435c630..4b798bf43 100644 --- a/esphome/components/mcp23017/__init__.py +++ b/esphome/components/mcp23017/__init__.py @@ -19,7 +19,7 @@ MCP23017 = mcp23017_ns.class_('MCP23017', cg.Component, i2c.I2CDevice) MCP23017GPIOPin = mcp23017_ns.class_('MCP23017GPIOPin', cg.GPIOPin) CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_variable_id(MCP23017), + cv.Required(CONF_ID): cv.declare_id(MCP23017), }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20)) @@ -31,15 +31,15 @@ def to_code(config): CONF_MCP23017 = 'mcp23017' MCP23017_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23017): cv.use_variable_id(MCP23017), + cv.Required(CONF_MCP23017): cv.use_id(MCP23017), cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.one_of(*MCP23017_GPIO_MODES, upper=True), + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(MCP23017_GPIO_MODES, upper=True), cv.Optional(CONF_INVERTED, default=False): cv.boolean, }) MCP23017_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23017): cv.use_variable_id(MCP23017), + cv.Required(CONF_MCP23017): cv.use_id(MCP23017), cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.one_of(*MCP23017_GPIO_MODES, upper=True), + cv.Optional(CONF_MODE, default="INPUT"): cv.enum(MCP23017_GPIO_MODES, upper=True), cv.Optional(CONF_INVERTED, default=False): cv.boolean, }) @@ -48,5 +48,4 @@ MCP23017_INPUT_PIN_SCHEMA = cv.Schema({ (MCP23017_OUTPUT_PIN_SCHEMA, MCP23017_INPUT_PIN_SCHEMA)) def mcp23017_pin_to_code(config): parent = yield cg.get_variable(config[CONF_MCP23017]) - yield MCP23017GPIOPin.new(parent, config[CONF_NUMBER], - MCP23017_GPIO_MODES[config[CONF_MODE]], config[CONF_INVERTED]) + yield MCP23017GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED]) diff --git a/esphome/components/mhz19/mhz19.h b/esphome/components/mhz19/mhz19.h index 857e86a02..3604628af 100644 --- a/esphome/components/mhz19/mhz19.h +++ b/esphome/components/mhz19/mhz19.h @@ -9,8 +9,6 @@ namespace mhz19 { class MHZ19Component : public PollingComponent, public uart::UARTDevice { public: - MHZ19Component(uint32_t update_interval) : PollingComponent(update_interval) {} - float get_setup_priority() const override; void update() override; diff --git a/esphome/components/mhz19/sensor.py b/esphome/components/mhz19/sensor.py index de0d5c742..368426e6f 100644 --- a/esphome/components/mhz19/sensor.py +++ b/esphome/components/mhz19/sensor.py @@ -1,9 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, \ - ICON_PERIODIC_TABLE_CO2, UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, \ - ICON_THERMOMETER +from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, ICON_PERIODIC_TABLE_CO2, \ + UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_THERMOMETER DEPENDENCIES = ['uart'] @@ -11,17 +10,14 @@ mhz19_ns = cg.esphome_ns.namespace('mhz19') MHZ19Component = mhz19_ns.class_('MHZ19Component', cg.PollingComponent, uart.UARTDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(MHZ19Component), - cv.Required(CONF_CO2): cv.nameable( - sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0)), - cv.Optional(CONF_TEMPERATURE): cv.nameable( - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 0)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) + cv.GenerateID(): cv.declare_id(MHZ19Component), + cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 0), +}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield uart.register_uart_device(var, config) diff --git a/esphome/components/monochromatic/light.py b/esphome/components/monochromatic/light.py index c69f54776..79faacff6 100644 --- a/esphome/components/monochromatic/light.py +++ b/esphome/components/monochromatic/light.py @@ -6,13 +6,15 @@ from esphome.const import CONF_OUTPUT_ID, CONF_OUTPUT monochromatic_ns = cg.esphome_ns.namespace('monochromatic') MonochromaticLightOutput = monochromatic_ns.class_('MonochromaticLightOutput', light.LightOutput) -CONFIG_SCHEMA = cv.nameable(light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(MonochromaticLightOutput), - cv.Required(CONF_OUTPUT): cv.use_variable_id(output.FloatOutput), -})) +CONFIG_SCHEMA = light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(MonochromaticLightOutput), + cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput), +}) def to_code(config): - out = yield cg.get_variable(config[CONF_OUTPUT]) - var = cg.new_Pvariable(config[CONF_OUTPUT_ID], out) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) yield light.register_light(var, config) + + out = yield cg.get_variable(config[CONF_OUTPUT]) + cg.add(var.set_output(out)) diff --git a/esphome/components/monochromatic/monochromatic_light_output.h b/esphome/components/monochromatic/monochromatic_light_output.h index 93c10bb27..c3a015ff3 100644 --- a/esphome/components/monochromatic/monochromatic_light_output.h +++ b/esphome/components/monochromatic/monochromatic_light_output.h @@ -9,7 +9,7 @@ namespace monochromatic { class MonochromaticLightOutput : public light::LightOutput { public: - MonochromaticLightOutput(output::FloatOutput *output) : output_(output) {} + void set_output(output::FloatOutput *output) { output_ = output; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); traits.set_supports_brightness(true); diff --git a/esphome/components/mpr121/__init__.py b/esphome/components/mpr121/__init__.py index 79a69037f..9e1bd3726 100644 --- a/esphome/components/mpr121/__init__.py +++ b/esphome/components/mpr121/__init__.py @@ -12,7 +12,7 @@ MPR121Component = mpr121_ns.class_('MPR121Component', cg.Component, i2c.I2CDevic MULTI_CONF = True CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(MPR121Component), + cv.GenerateID(): cv.declare_id(MPR121Component), }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x5A)) diff --git a/esphome/components/mpr121/binary_sensor.py b/esphome/components/mpr121/binary_sensor.py index 7b2e5f5ac..100dacd6d 100644 --- a/esphome/components/mpr121/binary_sensor.py +++ b/esphome/components/mpr121/binary_sensor.py @@ -1,21 +1,24 @@ -from esphome.components import binary_sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_CHANNEL, CONF_NAME, CONF_ID +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_CHANNEL, CONF_ID from . import mpr121_ns, MPR121Component, CONF_MPR121_ID DEPENDENCIES = ['mpr121'] MPR121Channel = mpr121_ns.class_('MPR121Channel', binary_sensor.BinarySensor) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(MPR121Channel), - cv.GenerateID(CONF_MPR121_ID): cv.use_variable_id(MPR121Component), - cv.Required(CONF_CHANNEL): cv.All(cv.int_, cv.Range(min=0, max=11)) -})) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(MPR121Channel), + cv.GenerateID(CONF_MPR121_ID): cv.use_id(MPR121Component), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=11), +}) def to_code(config): - hub = yield cg.get_variable(config[CONF_MPR121_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_CHANNEL]) + var = cg.new_Pvariable(config[CONF_ID]) yield binary_sensor.register_binary_sensor(var, config) + + cg.add(var.set_channel(config[CONF_CHANNEL])) + + hub = yield cg.get_variable(config[CONF_MPR121_ID]) cg.add(hub.register_channel(var)) diff --git a/esphome/components/mpr121/mpr121.h b/esphome/components/mpr121/mpr121.h index 4b05600e0..d5a2ec324 100644 --- a/esphome/components/mpr121/mpr121.h +++ b/esphome/components/mpr121/mpr121.h @@ -47,11 +47,11 @@ enum { class MPR121Channel : public binary_sensor::BinarySensor { public: - MPR121Channel(const std::string &name, int channel) : BinarySensor(name), channel_(channel) {} + void set_channel(uint8_t channel) { channel_ = channel; } void process(uint16_t data) { this->publish_state(static_cast(data & (1 << this->channel_))); } protected: - int channel_{0}; + uint8_t channel_{0}; }; class MPR121Component : public Component, public i2c::I2CDevice { diff --git a/esphome/components/mpu6050/mpu6050.h b/esphome/components/mpu6050/mpu6050.h index 3f6519bd2..ab410105c 100644 --- a/esphome/components/mpu6050/mpu6050.h +++ b/esphome/components/mpu6050/mpu6050.h @@ -9,8 +9,6 @@ namespace mpu6050 { class MPU6050Component : public PollingComponent, public i2c::I2CDevice { public: - MPU6050Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void setup() override; void dump_config() override; diff --git a/esphome/components/mpu6050/sensor.py b/esphome/components/mpu6050/sensor.py index 3dd7355f4..bf44c6784 100644 --- a/esphome/components/mpu6050/sensor.py +++ b/esphome/components/mpu6050/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, CONF_TEMPERATURE, \ - CONF_UPDATE_INTERVAL, ICON_BRIEFCASE_DOWNLOAD, UNIT_METER_PER_SECOND_SQUARED, \ + ICON_BRIEFCASE_DOWNLOAD, UNIT_METER_PER_SECOND_SQUARED, \ ICON_SCREEN_ROTATION, UNIT_DEGREE_PER_SECOND, ICON_THERMOMETER, UNIT_CELSIUS DEPENDENCIES = ['i2c'] @@ -22,20 +22,19 @@ gyro_schema = sensor.sensor_schema(UNIT_DEGREE_PER_SECOND, ICON_SCREEN_ROTATION, temperature_schema = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(MPU6050Component), - cv.Optional(CONF_ACCEL_X): cv.nameable(accel_schema), - cv.Optional(CONF_ACCEL_Y): cv.nameable(accel_schema), - cv.Optional(CONF_ACCEL_Z): cv.nameable(accel_schema), - cv.Optional(CONF_GYRO_X): cv.nameable(gyro_schema), - cv.Optional(CONF_GYRO_Y): cv.nameable(gyro_schema), - cv.Optional(CONF_GYRO_Z): cv.nameable(gyro_schema), - cv.Optional(CONF_TEMPERATURE): cv.nameable(temperature_schema), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x68)) + cv.GenerateID(): cv.declare_id(MPU6050Component), + cv.Optional(CONF_ACCEL_X): accel_schema, + cv.Optional(CONF_ACCEL_Y): accel_schema, + cv.Optional(CONF_ACCEL_Z): accel_schema, + cv.Optional(CONF_GYRO_X): gyro_schema, + cv.Optional(CONF_GYRO_Y): gyro_schema, + cv.Optional(CONF_GYRO_Z): gyro_schema, + cv.Optional(CONF_TEMPERATURE): temperature_schema, +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x68)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 3149bb8a6..5ac1c9896 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -1,10 +1,10 @@ import re -from esphome import automation -from esphome.automation import ACTION_REGISTRY, CONDITION_REGISTRY, Condition -from esphome.components import logger -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.automation import Condition +from esphome.components import logger from esphome.const import CONF_AVAILABILITY, CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, \ CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, \ CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, CONF_LOG_TOPIC, CONF_ON_JSON_MESSAGE, CONF_ON_MESSAGE, \ @@ -14,6 +14,7 @@ from esphome.const import CONF_AVAILABILITY, CONF_BIRTH_MESSAGE, CONF_BROKER, CO CONF_WILL_MESSAGE from esphome.core import coroutine_with_priority, coroutine, CORE +DEPENDENCIES = ['network'] AUTO_LOAD = ['json'] @@ -37,11 +38,12 @@ MQTT_MESSAGE_SCHEMA = cv.Any(None, MQTT_MESSAGE_BASE.extend({ mqtt_ns = cg.esphome_ns.namespace('mqtt') MQTTMessage = mqtt_ns.struct('MQTTMessage') MQTTClientComponent = mqtt_ns.class_('MQTTClientComponent', cg.Component) -MQTTPublishAction = mqtt_ns.class_('MQTTPublishAction', cg.Action) -MQTTPublishJsonAction = mqtt_ns.class_('MQTTPublishJsonAction', cg.Action) -MQTTMessageTrigger = mqtt_ns.class_('MQTTMessageTrigger', cg.Trigger.template(cg.std_string)) +MQTTPublishAction = mqtt_ns.class_('MQTTPublishAction', automation.Action) +MQTTPublishJsonAction = mqtt_ns.class_('MQTTPublishJsonAction', automation.Action) +MQTTMessageTrigger = mqtt_ns.class_('MQTTMessageTrigger', + automation.Trigger.template(cg.std_string)) MQTTJsonMessageTrigger = mqtt_ns.class_('MQTTJsonMessageTrigger', - cg.Trigger.template(cg.JsonObjectConstRef)) + automation.Trigger.template(cg.JsonObjectConstRef)) MQTTComponent = mqtt_ns.class_('MQTTComponent', cg.Component) MQTTConnectedCondition = mqtt_ns.class_('MQTTConnectedCondition', Condition) @@ -97,12 +99,12 @@ def validate_fingerprint(value): CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(MQTTClientComponent), + cv.GenerateID(): cv.declare_id(MQTTClientComponent), cv.Required(CONF_BROKER): cv.string_strict, cv.Optional(CONF_PORT, default=1883): cv.port, cv.Optional(CONF_USERNAME, default=''): cv.string, cv.Optional(CONF_PASSWORD, default=''): cv.string, - cv.Optional(CONF_CLIENT_ID, default=lambda: CORE.name): cv.All(cv.string, cv.Length(max=23)), + cv.Optional(CONF_CLIENT_ID, default=lambda: CORE.name): cv.string, cv.Optional(CONF_DISCOVERY, default=True): cv.Any(cv.boolean, cv.one_of("CLEAN", upper=True)), cv.Optional(CONF_DISCOVERY_RETAIN, default=True): cv.boolean, cv.Optional(CONF_DISCOVERY_PREFIX, default="homeassistant"): cv.publish_topic, @@ -120,13 +122,13 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ cv.Optional(CONF_KEEPALIVE, default='15s'): cv.positive_time_period_seconds, cv.Optional(CONF_REBOOT_TIMEOUT, default='5min'): cv.positive_time_period_milliseconds, cv.Optional(CONF_ON_MESSAGE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTMessageTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MQTTMessageTrigger), cv.Required(CONF_TOPIC): cv.subscribe_topic, cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, cv.Optional(CONF_PAYLOAD): cv.string_strict, }), cv.Optional(CONF_ON_JSON_MESSAGE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(MQTTJsonMessageTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MQTTJsonMessageTrigger), cv.Required(CONF_TOPIC): cv.subscribe_topic, cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, }), @@ -221,7 +223,7 @@ def to_code(config): MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_variable_id(MQTTClientComponent), + cv.GenerateID(): cv.use_id(MQTTClientComponent), cv.Required(CONF_TOPIC): cv.templatable(cv.publish_topic), cv.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload), cv.Optional(CONF_QOS, default=0): cv.templatable(cv.mqtt_qos), @@ -229,26 +231,24 @@ MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema({ }) -@ACTION_REGISTRY.register('mqtt.publish', MQTT_PUBLISH_ACTION_SCHEMA) +@automation.register_action('mqtt.publish', MQTTPublishAction, MQTT_PUBLISH_ACTION_SCHEMA) def mqtt_publish_action_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = MQTTPublishAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_TOPIC], args, cg.std_string) - cg.add(action.set_topic(template_)) + cg.add(var.set_topic(template_)) template_ = yield cg.templatable(config[CONF_PAYLOAD], args, cg.std_string) - cg.add(action.set_payload(template_)) + cg.add(var.set_payload(template_)) template_ = yield cg.templatable(config[CONF_QOS], args, cg.uint8) - cg.add(action.set_qos(template_)) + cg.add(var.set_qos(template_)) template_ = yield cg.templatable(config[CONF_RETAIN], args, bool) - cg.add(action.set_retain(template_)) - yield action + cg.add(var.set_retain(template_)) + yield var MQTT_PUBLISH_JSON_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_variable_id(MQTTClientComponent), + cv.GenerateID(): cv.use_id(MQTTClientComponent), cv.Required(CONF_TOPIC): cv.templatable(cv.publish_topic), cv.Required(CONF_PAYLOAD): cv.lambda_, cv.Optional(CONF_QOS, default=0): cv.templatable(cv.mqtt_qos), @@ -256,23 +256,22 @@ MQTT_PUBLISH_JSON_ACTION_SCHEMA = cv.Schema({ }) -@ACTION_REGISTRY.register('mqtt.publish_json', MQTT_PUBLISH_JSON_ACTION_SCHEMA) +@automation.register_action('mqtt.publish_json', MQTTPublishJsonAction, + MQTT_PUBLISH_JSON_ACTION_SCHEMA) def mqtt_publish_json_action_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = MQTTPublishJsonAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_TOPIC], args, cg.std_string) - cg.add(action.set_topic(template_)) + cg.add(var.set_topic(template_)) args_ = args + [(cg.JsonObjectRef, 'root')] lambda_ = yield cg.process_lambda(config[CONF_PAYLOAD], args_, return_type=cg.void) - cg.add(action.set_payload(lambda_)) + cg.add(var.set_payload(lambda_)) template_ = yield cg.templatable(config[CONF_QOS], args, cg.uint8) - cg.add(action.set_qos(template_)) + cg.add(var.set_qos(template_)) template_ = yield cg.templatable(config[CONF_RETAIN], args, bool) - cg.add(action.set_retain(template_)) - yield action + cg.add(var.set_retain(template_)) + yield var def get_default_topic_for(data, component_type, name, suffix): @@ -304,11 +303,9 @@ def register_mqtt_component(var, config): availability[CONF_PAYLOAD_NOT_AVAILABLE])) -@CONDITION_REGISTRY.register('mqtt.connected', cv.Schema({ - cv.GenerateID(): cv.use_variable_id(MQTTClientComponent), +@automation.register_condition('mqtt.connected', MQTTConnectedCondition, cv.Schema({ + cv.GenerateID(): cv.use_id(MQTTClientComponent), })) def mqtt_connected_to_code(config, condition_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = MQTTConnectedCondition.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(condition_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(condition_id, template_arg, paren) diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 5736a9daa..6f14b0c92 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -302,7 +302,6 @@ template class MQTTPublishAction : public Action { void play(Ts... x) override { this->parent_->publish(this->topic_.value(x...), this->payload_.value(x...), this->qos_.value(x...), this->retain_.value(x...)); - this->play_next(x...); } protected: @@ -323,7 +322,6 @@ template class MQTTPublishJsonAction : public Action { auto qos = this->qos_.value(x...); auto retain = this->retain_.value(x...); this->parent_->publish_json(topic, f, qos, retain); - this->play_next(x...); } protected: diff --git a/esphome/components/mqtt_subscribe/sensor/__init__.py b/esphome/components/mqtt_subscribe/sensor/__init__.py index 22b9f7028..dedc2ac9a 100644 --- a/esphome/components/mqtt_subscribe/sensor/__init__.py +++ b/esphome/components/mqtt_subscribe/sensor/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import mqtt, sensor -from esphome.const import CONF_ID, CONF_NAME, CONF_QOS, CONF_TOPIC +from esphome.const import CONF_ID, CONF_QOS, CONF_TOPIC, UNIT_EMPTY, ICON_EMPTY from .. import mqtt_subscribe_ns DEPENDENCIES = ['mqtt'] @@ -9,19 +9,20 @@ DEPENDENCIES = ['mqtt'] CONF_MQTT_PARENT_ID = 'mqtt_parent_id' MQTTSubscribeSensor = mqtt_subscribe_ns.class_('MQTTSubscribeSensor', sensor.Sensor, cg.Component) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(MQTTSubscribeSensor), - cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_variable_id(mqtt.MQTTClientComponent), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({ + cv.GenerateID(): cv.declare_id(MQTTSubscribeSensor), + cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent), cv.Required(CONF_TOPIC): cv.subscribe_topic, cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - parent = yield cg.get_variable(config[CONF_MQTT_PARENT_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], parent, config[CONF_TOPIC]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) - - cg.add(var.set_qos(config[CONF_QOS])) - yield sensor.register_sensor(var, config) + + parent = yield cg.get_variable(config[CONF_MQTT_PARENT_ID]) + cg.add(var.set_parent(parent)) + cg.add(var.set_topic(config[CONF_TOPIC])) + cg.add(var.set_qos(config[CONF_QOS])) diff --git a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h index 7e02797cb..a303ccad8 100644 --- a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h +++ b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h @@ -9,9 +9,8 @@ namespace mqtt_subscribe { class MQTTSubscribeSensor : public sensor::Sensor, public Component { public: - MQTTSubscribeSensor(const std::string &name, mqtt::MQTTClientComponent *parent, const std::string &topic) - : Sensor(name), parent_(parent), topic_(topic) {} - + void set_parent(mqtt::MQTTClientComponent *parent) { parent_ = parent; } + void set_topic(const std::string &topic) { topic_ = topic; } void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/mqtt_subscribe/text_sensor/__init__.py b/esphome/components/mqtt_subscribe/text_sensor/__init__.py index 7dc5ae89e..c80909669 100644 --- a/esphome/components/mqtt_subscribe/text_sensor/__init__.py +++ b/esphome/components/mqtt_subscribe/text_sensor/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import text_sensor, mqtt -from esphome.const import CONF_ID, CONF_NAME, CONF_QOS, CONF_TOPIC +from esphome.const import CONF_ID, CONF_QOS, CONF_TOPIC from .. import mqtt_subscribe_ns DEPENDENCIES = ['mqtt'] @@ -10,19 +10,20 @@ CONF_MQTT_PARENT_ID = 'mqtt_parent_id' MQTTSubscribeTextSensor = mqtt_subscribe_ns.class_('MQTTSubscribeTextSensor', text_sensor.TextSensor, cg.Component) -CONFIG_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(MQTTSubscribeTextSensor), - cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_variable_id(mqtt.MQTTClientComponent), +CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(MQTTSubscribeTextSensor), + cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent), cv.Required(CONF_TOPIC): cv.subscribe_topic, cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - parent = yield cg.get_variable(config[CONF_MQTT_PARENT_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], parent, config[CONF_TOPIC]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) - - cg.add(var.set_qos(config[CONF_QOS])) - yield text_sensor.register_text_sensor(var, config) + + parent = yield cg.get_variable(config[CONF_MQTT_PARENT_ID]) + cg.add(var.set_parent(parent)) + cg.add(var.set_topic(config[CONF_TOPIC])) + cg.add(var.set_qos(config[CONF_QOS])) diff --git a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h index d1e26307a..69409f634 100644 --- a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h +++ b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h @@ -9,9 +9,8 @@ namespace mqtt_subscribe { class MQTTSubscribeTextSensor : public text_sensor::TextSensor, public Component { public: - MQTTSubscribeTextSensor(const std::string &name, mqtt::MQTTClientComponent *parent, const std::string &topic) - : TextSensor(name), parent_(parent), topic_(topic) {} - + void set_parent(mqtt::MQTTClientComponent *parent) { parent_ = parent; } + void set_topic(const std::string &topic) { topic_ = topic; } void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/ms5611/ms5611.h b/esphome/components/ms5611/ms5611.h index 1f09bdf8b..b5663ad73 100644 --- a/esphome/components/ms5611/ms5611.h +++ b/esphome/components/ms5611/ms5611.h @@ -9,7 +9,6 @@ namespace ms5611 { class MS5611Component : public PollingComponent, public i2c::I2CDevice { public: - MS5611Component(uint32_t update_interval) : PollingComponent(update_interval) {} void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/ms5611/sensor.py b/esphome/components/ms5611/sensor.py index d8c18319a..ab9aac6d5 100644 --- a/esphome/components/ms5611/sensor.py +++ b/esphome/components/ms5611/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, CONF_PRESSURE, \ - CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, ICON_THERMOMETER, UNIT_CELSIUS, ICON_GAUGE, \ + CONF_TEMPERATURE, ICON_THERMOMETER, UNIT_CELSIUS, ICON_GAUGE, \ UNIT_HECTOPASCAL DEPENDENCIES = ['i2c'] @@ -11,16 +11,14 @@ ms5611_ns = cg.esphome_ns.namespace('ms5611') MS5611Component = ms5611_ns.class_('MS5611Component', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(MS5611Component), - cv.Required(CONF_TEMPERATURE): cv.nameable( - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1)), - cv.Required(CONF_PRESSURE): cv.nameable(sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x77)) + cv.GenerateID(): cv.declare_id(MS5611Component), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Required(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/my9231/__init__.py b/esphome/components/my9231/__init__.py index adc4201c4..82dfe3e08 100644 --- a/esphome/components/my9231/__init__.py +++ b/esphome/components/my9231/__init__.py @@ -10,7 +10,7 @@ MY9231OutputComponent = my9231_ns.class_('MY9231OutputComponent', cg.Component) MULTI_CONF = True CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(MY9231OutputComponent), + cv.GenerateID(): cv.declare_id(MY9231OutputComponent), cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_NUM_CHANNELS, default=6): cv.All(cv.int_, cv.Range(min=3, max=1020)), @@ -20,11 +20,14 @@ CONFIG_SCHEMA = cv.Schema({ def to_code(config): - di = yield cg.gpio_pin_expression(config[CONF_DATA_PIN]) - dcki = yield cg.gpio_pin_expression(config[CONF_CLOCK_PIN]) - var = cg.new_Pvariable(config[CONF_ID], di, dcki) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + di = yield cg.gpio_pin_expression(config[CONF_DATA_PIN]) + cg.add(var.set_pin_di(di)) + dcki = yield cg.gpio_pin_expression(config[CONF_CLOCK_PIN]) + cg.add(var.set_pin_dcki(dcki)) + cg.add(var.set_num_channels(config[CONF_NUM_CHANNELS])) cg.add(var.set_num_chips(config[CONF_NUM_CHIPS])) cg.add(var.set_bit_depth(config[CONF_BIT_DEPTH])) diff --git a/esphome/components/my9231/my9231.h b/esphome/components/my9231/my9231.h index f8f013ead..d7c479c06 100644 --- a/esphome/components/my9231/my9231.h +++ b/esphome/components/my9231/my9231.h @@ -10,12 +10,8 @@ namespace my9231 { class MY9231OutputComponent : public Component { public: class Channel; - /** Construct the component. - * - * @param pin_di The pin which DI is connected to. - * @param pin_dcki The pin which DCKI is connected to. - */ - MY9231OutputComponent(GPIOPin *pin_di, GPIOPin *pin_dcki) : pin_di_(pin_di), pin_dcki_(pin_dcki) {} + void set_pin_di(GPIOPin *pin_di) { pin_di_ = pin_di; } + void set_pin_dcki(GPIOPin *pin_dcki) { pin_dcki_ = pin_dcki; } void set_num_channels(uint16_t num_channels) { this->num_channels_ = num_channels; } void set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; } @@ -31,7 +27,8 @@ class MY9231OutputComponent : public Component { class Channel : public output::FloatOutput { public: - Channel(MY9231OutputComponent *parent, uint8_t channel) : parent_(parent), channel_(channel) {} + void set_parent(MY9231OutputComponent *parent) { parent_ = parent; } + void set_channel(uint8_t channel) { channel_ = channel; } protected: void write_state(float state) override { diff --git a/esphome/components/my9231/output.py b/esphome/components/my9231/output.py index 21f462302..62460faec 100644 --- a/esphome/components/my9231/output.py +++ b/esphome/components/my9231/output.py @@ -10,14 +10,17 @@ Channel = MY9231OutputComponent.class_('Channel', output.FloatOutput) CONF_MY9231_ID = 'my9231_id' CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.GenerateID(CONF_MY9231_ID): cv.use_variable_id(MY9231OutputComponent), + cv.GenerateID(CONF_MY9231_ID): cv.use_id(MY9231OutputComponent), - cv.Required(CONF_ID): cv.declare_variable_id(Channel), + cv.Required(CONF_ID): cv.declare_id(Channel), cv.Required(CONF_CHANNEL): cv.All(cv.int_, cv.Range(min=0, max=65535)), }).extend(cv.COMPONENT_SCHEMA) def to_code(config): - my9231 = yield cg.get_variable(config[CONF_MY9231_ID]) - var = cg.new_Pvariable(config[CONF_ID], my9231, config[CONF_CHANNEL]) + var = cg.new_Pvariable(config[CONF_ID]) yield output.register_output(var, config) + + parent = yield cg.get_variable(config[CONF_MY9231_ID]) + cg.add(var.set_parent(parent)) + cg.add(var.set_channel(config[CONF_CHANNEL])) diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index fcadd82a0..060aeb064 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -127,8 +127,8 @@ def validate(config): raise cv.Invalid("Must specify at least one of 'pin' or 'clock_pin'+'data_pin'") -CONFIG_SCHEMA = cv.nameable(light.ADDRESSABLE_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(NeoPixelBusLightOutputBase), +CONFIG_SCHEMA = cv.All(light.ADDRESSABLE_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(NeoPixelBusLightOutputBase), cv.Optional(CONF_TYPE, default='GRB'): validate_type, cv.Optional(CONF_VARIANT, default='800KBPS'): validate_variant, @@ -139,7 +139,7 @@ CONFIG_SCHEMA = cv.nameable(light.ADDRESSABLE_LIGHT_SCHEMA.extend({ cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, - cv.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(power_supply.PowerSupply), + cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), }).extend(cv.COMPONENT_SCHEMA), validate, validate_method_pin) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py new file mode 100644 index 000000000..4486d62e1 --- /dev/null +++ b/esphome/components/network/__init__.py @@ -0,0 +1 @@ +# Dummy package to allow components to depend on network diff --git a/esphome/components/nextion/binary_sensor.py b/esphome/components/nextion/binary_sensor.py index ef75d86c6..6003c5980 100644 --- a/esphome/components/nextion/binary_sensor.py +++ b/esphome/components/nextion/binary_sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor -from esphome.const import CONF_COMPONENT_ID, CONF_NAME, CONF_PAGE_ID, CONF_ID +from esphome.const import CONF_COMPONENT_ID, CONF_PAGE_ID, CONF_ID from . import nextion_ns from .display import Nextion @@ -11,18 +11,21 @@ CONF_NEXTION_ID = 'nextion_id' NextionTouchComponent = nextion_ns.class_('NextionTouchComponent', binary_sensor.BinarySensor) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(NextionTouchComponent), - cv.GenerateID(CONF_NEXTION_ID): cv.use_variable_id(Nextion), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(NextionTouchComponent), + cv.GenerateID(CONF_NEXTION_ID): cv.use_id(Nextion), cv.Required(CONF_PAGE_ID): cv.uint8_t, cv.Required(CONF_COMPONENT_ID): cv.uint8_t, -})) +}) def to_code(config): - hub = yield cg.get_variable(config[CONF_NEXTION_ID]) - rhs = hub.make_touch_component(config[CONF_NAME], config[CONF_PAGE_ID], - config[CONF_COMPONENT_ID]) - var = cg.Pvariable(config[CONF_ID], rhs) + var = cg.new_Pvariable(config[CONF_ID]) yield binary_sensor.register_binary_sensor(var, config) + + hub = yield cg.get_variable(config[CONF_NEXTION_ID]) + cg.add(hub.register_touch_component(var)) + + cg.add(var.set_component_id(config[CONF_COMPONENT_ID])) + cg.add(var.set_page_id(config[CONF_PAGE_ID])) diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 5955f85f7..30d751938 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import display, uart -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_UPDATE_INTERVAL +from esphome.const import CONF_ID, CONF_LAMBDA from . import nextion_ns DEPENDENCIES = ['uart'] @@ -11,9 +11,8 @@ Nextion = nextion_ns.class_('Nextion', cg.PollingComponent, uart.UARTDevice) NextionRef = Nextion.operator('ref') CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(Nextion), - cv.Optional(CONF_UPDATE_INTERVAL, default='5s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) + cv.GenerateID(): cv.declare_id(Nextion), +}).extend(cv.polling_component_schema('5s')).extend(uart.UART_DEVICE_SCHEMA) def to_code(config): diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 99ff2126c..f41a97ce7 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -263,11 +263,6 @@ void Nextion::set_nextion_rtc_time(time::ESPTime time) { void Nextion::set_backlight_brightness(uint8_t brightness) { this->send_command_printf("dim=%u", brightness); } void Nextion::set_touch_sleep_timeout(uint16_t timeout) { this->send_command_printf("thsp=%u", timeout); } -NextionTouchComponent *Nextion::make_touch_component(const std::string &name, uint8_t page_id, uint8_t component_id) { - auto *ret = new NextionTouchComponent(name, page_id, component_id); - this->touch_.push_back(ret); - return ret; -} void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; } void Nextion::set_component_text_printf(const char *component, const char *format, ...) { va_list arg; @@ -285,8 +280,6 @@ void NextionTouchComponent::process(uint8_t page_id, uint8_t component_id, bool this->publish_state(on); } } -NextionTouchComponent::NextionTouchComponent(const std::string &name, uint8_t page_id, uint8_t component_id) - : BinarySensor(name), page_id_(page_id), component_id_(component_id) {} } // namespace nextion } // namespace esphome diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 8941e745d..d8fe3762c 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -19,8 +19,6 @@ using nextion_writer_t = std::function; class Nextion : public PollingComponent, public uart::UARTDevice { public: - Nextion() : PollingComponent(0) {} - /** * Set the text of a component to a static string. * @param component The component name. @@ -189,7 +187,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) - NextionTouchComponent *make_touch_component(const std::string &name, uint8_t page_id, uint8_t component_id); + void register_touch_component(NextionTouchComponent *obj) { this->touch_.push_back(obj); } void setup() override; float get_setup_priority() const override; void update() override; @@ -222,7 +220,8 @@ class Nextion : public PollingComponent, public uart::UARTDevice { class NextionTouchComponent : public binary_sensor::BinarySensor { public: - NextionTouchComponent(const std::string &name, uint8_t page_id, uint8_t component_id); + void set_page_id(uint8_t page_id) { page_id_ = page_id; } + void set_component_id(uint8_t component_id) { component_id_ = component_id; } void process(uint8_t page_id, uint8_t component_id, bool on); protected: diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 0c202e50a..e290e57ba 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,18 +1,15 @@ -import logging - -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE from esphome.core import CORE, coroutine_with_priority -_LOGGER = logging.getLogger(__name__) +DEPENDENCIES = ['network'] ota_ns = cg.esphome_ns.namespace('ota') OTAComponent = ota_ns.class_('OTAComponent', cg.Component) - CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(OTAComponent), + cv.GenerateID(): cv.declare_id(OTAComponent), cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean, cv.SplitDefault(CONF_PORT, esp8266=8266, esp32=3232): cv.port, cv.Optional(CONF_PASSWORD, default=''): cv.string, @@ -21,10 +18,12 @@ CONFIG_SCHEMA = cv.Schema({ @coroutine_with_priority(50.0) def to_code(config): - ota = cg.new_Pvariable(config[CONF_ID], config[CONF_PORT]) - cg.add(ota.set_auth_password(config[CONF_PASSWORD])) + var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_port(config[CONF_PORT])) + cg.add(var.set_auth_password(config[CONF_PASSWORD])) + if config[CONF_SAFE_MODE]: - cg.add(ota.start_safe_mode()) + cg.add(var.start_safe_mode()) if CORE.is_esp8266: cg.add_library('Update', None) @@ -32,4 +31,4 @@ def to_code(config): cg.add_library('Hash', None) # Register at end for safe mode - yield cg.register_component(ota, config) + yield cg.register_component(var, config) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index f1b724f0b..b69393c99 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -349,8 +349,6 @@ size_t OTAComponent::wait_receive_(uint8_t *buf, size_t bytes, bool check_discon return bytes; } -OTAComponent::OTAComponent(uint16_t port) : port_(port) {} - void OTAComponent::set_auth_password(const std::string &password) { this->password_ = password; } float OTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index 1040b9e6b..e37cb7160 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -35,12 +35,6 @@ enum OTAResponseTypes { /// OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. class OTAComponent : public Component { public: - /** Construct an OTAComponent. Defaults to no authentication. - * - * @param port The port ArduinoOTA will listen on. - */ - explicit OTAComponent(uint16_t port); - /** Set a plaintext password that OTA will use for authentication. * * Warning: This password will be stored in plaintext in the ROM and can be read diff --git a/esphome/components/output/__init__.py b/esphome/components/output/__init__.py index 20c29f3df..b406f62ee 100644 --- a/esphome/components/output/__init__.py +++ b/esphome/components/output/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.automation import ACTION_REGISTRY, maybe_simple_id +from esphome import automation +from esphome.automation import maybe_simple_id from esphome.components import power_supply from esphome.const import CONF_ID, CONF_INVERTED, CONF_LEVEL, CONF_MAX_POWER, \ CONF_MIN_POWER, CONF_POWER_SUPPLY @@ -9,7 +10,7 @@ from esphome.core import CORE, coroutine IS_PLATFORM_COMPONENT = True BINARY_OUTPUT_SCHEMA = cv.Schema({ - cv.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(power_supply.PowerSupply), + cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), cv.Optional(CONF_INVERTED): cv.boolean, }) @@ -25,9 +26,9 @@ FloatOutput = output_ns.class_('FloatOutput', BinaryOutput) FloatOutputPtr = FloatOutput.operator('ptr') # Actions -TurnOffAction = output_ns.class_('TurnOffAction', cg.Action) -TurnOnAction = output_ns.class_('TurnOnAction', cg.Action) -SetLevelAction = output_ns.class_('SetLevelAction', cg.Action) +TurnOffAction = output_ns.class_('TurnOffAction', automation.Action) +TurnOnAction = output_ns.class_('TurnOnAction', automation.Action) +SetLevelAction = output_ns.class_('SetLevelAction', automation.Action) @coroutine @@ -51,38 +52,32 @@ def register_output(var, config): BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(BinaryOutput), + cv.Required(CONF_ID): cv.use_id(BinaryOutput), }) -@ACTION_REGISTRY.register('output.turn_on', BINARY_OUTPUT_ACTION_SCHEMA) +@automation.register_action('output.turn_on', TurnOnAction, BINARY_OUTPUT_ACTION_SCHEMA) def output_turn_on_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = TurnOnAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('output.turn_off', BINARY_OUTPUT_ACTION_SCHEMA) +@automation.register_action('output.turn_off', TurnOffAction, BINARY_OUTPUT_ACTION_SCHEMA) def output_turn_off_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = TurnOffAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('output.set_level', cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(FloatOutput), +@automation.register_action('output.set_level', SetLevelAction, cv.Schema({ + cv.Required(CONF_ID): cv.use_id(FloatOutput), cv.Required(CONF_LEVEL): cv.templatable(cv.percentage), })) def output_set_level_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = SetLevelAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_LEVEL], args, float) - cg.add(action.set_level(template_)) - yield action + cg.add(var.set_level(template_)) + yield var def to_code(config): diff --git a/esphome/components/output/automation.h b/esphome/components/output/automation.h index ef2c02ab4..8c8a5ab61 100644 --- a/esphome/components/output/automation.h +++ b/esphome/components/output/automation.h @@ -12,10 +12,7 @@ template class TurnOffAction : public Action { public: TurnOffAction(BinaryOutput *output) : output_(output) {} - void play(Ts... x) override { - this->output_->turn_off(); - this->play_next(x...); - } + void play(Ts... x) override { this->output_->turn_off(); } protected: BinaryOutput *output_; @@ -25,10 +22,7 @@ template class TurnOnAction : public Action { public: TurnOnAction(BinaryOutput *output) : output_(output) {} - void play(Ts... x) override { - this->output_->turn_on(); - this->play_next(x...); - } + void play(Ts... x) override { this->output_->turn_on(); } protected: BinaryOutput *output_; @@ -39,10 +33,7 @@ template class SetLevelAction : public Action { SetLevelAction(FloatOutput *output) : output_(output) {} TEMPLATABLE_VALUE(float, level) - void play(Ts... x) override { - this->output_->set_level(this->level_.value(x...)); - this->play_next(x...); - } + void play(Ts... x) override { this->output_->set_level(this->level_.value(x...)); } protected: FloatOutput *output_; diff --git a/esphome/components/output/switch/__init__.py b/esphome/components/output/switch/__init__.py index 1f1326c84..5795271f8 100644 --- a/esphome/components/output/switch/__init__.py +++ b/esphome/components/output/switch/__init__.py @@ -1,19 +1,21 @@ -from esphome.components import output, switch -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_NAME, CONF_OUTPUT +import esphome.config_validation as cv +from esphome.components import output, switch +from esphome.const import CONF_ID, CONF_OUTPUT from .. import output_ns OutputSwitch = output_ns.class_('OutputSwitch', switch.Switch, cg.Component) -CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(OutputSwitch), - cv.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput), -}).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(OutputSwitch), + cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - output_ = yield cg.get_variable(config[CONF_OUTPUT]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], output_) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield switch.register_switch(var, config) + + output_ = yield cg.get_variable(config[CONF_OUTPUT]) + cg.add(var.set_output(output_)) diff --git a/esphome/components/output/switch/output_switch.h b/esphome/components/output/switch/output_switch.h index b281718f3..fc9540fed 100644 --- a/esphome/components/output/switch/output_switch.h +++ b/esphome/components/output/switch/output_switch.h @@ -9,7 +9,7 @@ namespace output { class OutputSwitch : public switch_::Switch, public Component { public: - OutputSwitch(const std::string &name, BinaryOutput *output) : Switch(name), output_(output) {} + void set_output(BinaryOutput *output) { output_ = output; } void setup() override; float get_setup_priority() const override { return setup_priority::HARDWARE; } diff --git a/esphome/components/partition/light.py b/esphome/components/partition/light.py index 46c0e3f23..cfc709854 100644 --- a/esphome/components/partition/light.py +++ b/esphome/components/partition/light.py @@ -15,14 +15,14 @@ def validate_from_to(value): return value -CONFIG_SCHEMA = cv.nameable(light.ADDRESSABLE_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(PartitionLightOutput), +CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(PartitionLightOutput), cv.Required(CONF_SEGMENTS): cv.All(cv.ensure_list({ - cv.Required(CONF_ID): cv.use_variable_id(light.AddressableLightState), + cv.Required(CONF_ID): cv.use_id(light.AddressableLightState), cv.Required(CONF_FROM): cv.positive_int, cv.Required(CONF_TO): cv.positive_int, }, validate_from_to), cv.Length(min=1)), -})) +}) def to_code(config): diff --git a/esphome/components/pca9685/__init__.py b/esphome/components/pca9685/__init__.py index 7f902dc84..8e02bd78d 100644 --- a/esphome/components/pca9685/__init__.py +++ b/esphome/components/pca9685/__init__.py @@ -10,7 +10,7 @@ pca9685_ns = cg.esphome_ns.namespace('pca9685') PCA9685Output = pca9685_ns.class_('PCA9685Output', cg.Component, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(PCA9685Output), + cv.GenerateID(): cv.declare_id(PCA9685Output), cv.Required(CONF_FREQUENCY): cv.All(cv.frequency, cv.Range(min=23.84, max=1525.88)), }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x40)) diff --git a/esphome/components/pca9685/output.py b/esphome/components/pca9685/output.py index a1a557dde..0c6855c1d 100644 --- a/esphome/components/pca9685/output.py +++ b/esphome/components/pca9685/output.py @@ -10,8 +10,8 @@ PCA9685Channel = pca9685_ns.class_('PCA9685Channel', output.FloatOutput) CONF_PCA9685_ID = 'pca9685_id' CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_variable_id(PCA9685Channel), - cv.GenerateID(CONF_PCA9685_ID): cv.use_variable_id(PCA9685Output), + cv.Required(CONF_ID): cv.declare_id(PCA9685Channel), + cv.GenerateID(CONF_PCA9685_ID): cv.use_id(PCA9685Output), cv.Required(CONF_CHANNEL): cv.All(cv.Coerce(int), cv.Range(min=0, max=15)), }) diff --git a/esphome/components/pcf8574/__init__.py b/esphome/components/pcf8574/__init__.py index 065eb2665..ad74ac70a 100644 --- a/esphome/components/pcf8574/__init__.py +++ b/esphome/components/pcf8574/__init__.py @@ -21,7 +21,7 @@ PCF8574GPIOPin = pcf8574_ns.class_('PCF8574GPIOPin', cg.GPIOPin) CONF_PCF8574 = 'pcf8574' CONF_PCF8575 = 'pcf8575' CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_variable_id(PCF8574Component), + cv.Required(CONF_ID): cv.declare_id(PCF8574Component), cv.Optional(CONF_PCF8575, default=False): cv.boolean, }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x21)) @@ -34,15 +34,15 @@ def to_code(config): PCF8574_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_PCF8574): cv.use_variable_id(PCF8574Component), + cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.one_of(*PCF8674_GPIO_MODES, upper=True), + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(PCF8674_GPIO_MODES, upper=True), cv.Optional(CONF_INVERTED, default=False): cv.boolean, }) PCF8574_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_PCF8574): cv.use_variable_id(PCF8574Component), + cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.one_of(*PCF8674_GPIO_MODES, upper=True), + cv.Optional(CONF_MODE, default="INPUT"): cv.enum(PCF8674_GPIO_MODES, upper=True), cv.Optional(CONF_INVERTED, default=False): cv.boolean, }) @@ -50,5 +50,4 @@ PCF8574_INPUT_PIN_SCHEMA = cv.Schema({ @pins.PIN_SCHEMA_REGISTRY.register('pcf8574', (PCF8574_OUTPUT_PIN_SCHEMA, PCF8574_INPUT_PIN_SCHEMA)) def pcf8574_pin_to_code(config): parent = yield cg.get_variable(config[CONF_PCF8574]) - yield PCF8574GPIOPin.new(parent, config[CONF_NUMBER], - PCF8674_GPIO_MODES[config[CONF_MODE]], config[CONF_INVERTED]) + yield PCF8574GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED]) diff --git a/esphome/components/pmsx003/sensor.py b/esphome/components/pmsx003/sensor.py index adca5270f..a949fbb1c 100644 --- a/esphome/components/pmsx003/sensor.py +++ b/esphome/components/pmsx003/sensor.py @@ -41,21 +41,21 @@ def validate_pmsx003_sensors(value): CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(PMSX003Component), - cv.Required(CONF_TYPE): cv.one_of(*PMSX003_TYPES, upper=True), + cv.GenerateID(): cv.declare_id(PMSX003Component), + cv.Required(CONF_TYPE): cv.enum(PMSX003_TYPES, upper=True), cv.Optional(CONF_PM_1_0): - cv.nameable(sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0)), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), cv.Optional(CONF_PM_2_5): - cv.nameable(sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0)), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), cv.Optional(CONF_PM_10_0): - cv.nameable(sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0)), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), cv.Optional(CONF_TEMPERATURE): - cv.nameable(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1)), + sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), cv.Optional(CONF_HUMIDITY): - cv.nameable(sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1)), + sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), cv.Optional(CONF_FORMALDEHYDE): - cv.nameable(sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0)), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), }).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) @@ -64,7 +64,7 @@ def to_code(config): yield cg.register_component(var, config) yield uart.register_uart_device(var, config) - cg.add(var.set_type(PMSX003_TYPES[config[CONF_TYPE]])) + cg.add(var.set_type(config[CONF_TYPE])) if CONF_PM_1_0 in config: sens = yield sensor.new_sensor(config[CONF_PM_1_0]) diff --git a/esphome/components/pn532/__init__.py b/esphome/components/pn532/__init__.py index 659bb1ab5..c82d35b39 100644 --- a/esphome/components/pn532/__init__.py +++ b/esphome/components/pn532/__init__.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import spi -from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID, CONF_UPDATE_INTERVAL +from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID DEPENDENCIES = ['spi'] AUTO_LOAD = ['binary_sensor'] @@ -10,19 +10,18 @@ MULTI_CONF = True pn532_ns = cg.esphome_ns.namespace('pn532') PN532 = pn532_ns.class_('PN532', cg.PollingComponent, spi.SPIDevice) -PN532Trigger = pn532_ns.class_('PN532Trigger', cg.Trigger.template(cg.std_string)) +PN532Trigger = pn532_ns.class_('PN532Trigger', automation.Trigger.template(cg.std_string)) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(PN532), - cv.Optional(CONF_UPDATE_INTERVAL, default='1s'): cv.update_interval, + cv.GenerateID(): cv.declare_id(PN532), cv.Optional(CONF_ON_TAG): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(PN532Trigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532Trigger), }), -}).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA) +}).extend(cv.polling_component_schema('1s')).extend(spi.SPI_DEVICE_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield spi.register_spi_device(var, config) diff --git a/esphome/components/pn532/binary_sensor.py b/esphome/components/pn532/binary_sensor.py index d3bb51770..1c5e220fa 100644 --- a/esphome/components/pn532/binary_sensor.py +++ b/esphome/components/pn532/binary_sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor -from esphome.const import CONF_NAME, CONF_UID, CONF_ID +from esphome.const import CONF_UID, CONF_ID from esphome.core import HexInt from . import pn532_ns, PN532 @@ -27,16 +27,18 @@ def validate_uid(value): PN532BinarySensor = pn532_ns.class_('PN532BinarySensor', binary_sensor.BinarySensor) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(PN532BinarySensor), - cv.GenerateID(CONF_PN532_ID): cv.use_variable_id(PN532), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(PN532BinarySensor), + cv.GenerateID(CONF_PN532_ID): cv.use_id(PN532), cv.Required(CONF_UID): validate_uid, -})) +}) def to_code(config): - hub = yield cg.get_variable(config[CONF_PN532_ID]) - addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], addr) - cg.add(hub.register_tag(var)) + var = cg.new_Pvariable(config[CONF_ID]) yield binary_sensor.register_binary_sensor(var, config) + + hub = yield cg.get_variable(config[CONF_PN532_ID]) + cg.add(hub.register_tag(var)) + addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] + cg.add(var.set_uid(addr)) diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index e00e13b4a..07a41444c 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -335,7 +335,6 @@ bool PN532::wait_ready_() { return true; } -PN532::PN532(uint32_t update_interval) : PollingComponent(update_interval) {} bool PN532::is_device_msb_first() { return false; } void PN532::dump_config() { ESP_LOGCONFIG(TAG, "PN532:"); @@ -358,9 +357,6 @@ void PN532::dump_config() { } } -PN532BinarySensor::PN532BinarySensor(const std::string &name, const std::vector &uid) - : BinarySensor(name), uid_(uid) {} - bool PN532BinarySensor::process(const uint8_t *data, uint8_t len) { if (len != this->uid_.size()) return false; diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index 3d36d1c42..d349c7a15 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -13,8 +13,6 @@ class PN532Trigger; class PN532 : public PollingComponent, public spi::SPIDevice { public: - PN532(uint32_t update_interval); - void setup() override; void dump_config() override; @@ -69,7 +67,7 @@ class PN532 : public PollingComponent, public spi::SPIDevice { class PN532BinarySensor : public binary_sensor::BinarySensor { public: - PN532BinarySensor(const std::string &name, const std::vector &uid); + void set_uid(const std::vector &uid) { uid_ = uid; } bool process(const uint8_t *data, uint8_t len); diff --git a/esphome/components/power_supply/__init__.py b/esphome/components/power_supply/__init__.py index 8cf2e0d13..5646ffdc0 100644 --- a/esphome/components/power_supply/__init__.py +++ b/esphome/components/power_supply/__init__.py @@ -1,6 +1,6 @@ -from esphome import pins -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins from esphome.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN power_supply_ns = cg.esphome_ns.namespace('power_supply') @@ -8,7 +8,7 @@ PowerSupply = power_supply_ns.class_('PowerSupply', cg.Component) MULTI_CONF = True CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_variable_id(PowerSupply), + cv.Required(CONF_ID): cv.declare_id(PowerSupply), cv.Required(CONF_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_ENABLE_TIME, default='20ms'): cv.positive_time_period_milliseconds, cv.Optional(CONF_KEEP_ON_TIME, default='10s'): cv.positive_time_period_milliseconds, @@ -16,9 +16,12 @@ CONFIG_SCHEMA = cv.Schema({ def to_code(config): - pin = yield cg.gpio_pin_expression(config[CONF_PIN]) - - var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_ENABLE_TIME], - config[CONF_KEEP_ON_TIME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + + pin = yield cg.gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) + cg.add(var.set_enable_time(config[CONF_ENABLE_TIME])) + cg.add(var.set_keep_on_time(config[CONF_KEEP_ON_TIME])) + cg.add_define('USE_POWER_SUPPLY') diff --git a/esphome/components/power_supply/power_supply.cpp b/esphome/components/power_supply/power_supply.cpp index f631b82ce..5ce2e38ee 100644 --- a/esphome/components/power_supply/power_supply.cpp +++ b/esphome/components/power_supply/power_supply.cpp @@ -22,9 +22,6 @@ void PowerSupply::dump_config() { float PowerSupply::get_setup_priority() const { return setup_priority::IO; } -PowerSupply::PowerSupply(GPIOPin *pin, uint32_t enable_time, uint32_t keep_on_time) - : pin_(pin), enable_time_(enable_time), keep_on_time_(keep_on_time) {} - bool PowerSupply::is_enabled() const { return this->enabled_; } void PowerSupply::request_high_power() { diff --git a/esphome/components/power_supply/power_supply.h b/esphome/components/power_supply/power_supply.h index b7af802e5..b49da3b32 100644 --- a/esphome/components/power_supply/power_supply.h +++ b/esphome/components/power_supply/power_supply.h @@ -8,7 +8,9 @@ namespace power_supply { class PowerSupply : public Component { public: - explicit PowerSupply(GPIOPin *pin, uint32_t enable_time, uint32_t keep_on_time); + void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_enable_time(uint32_t enable_time) { enable_time_ = enable_time; } + void set_keep_on_time(uint32_t keep_on_time) { keep_on_time_ = keep_on_time; } /// Is this power supply currently on? bool is_enabled() const; diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index 8acf333f4..cf7315614 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -48,16 +48,12 @@ struct PulseCounterStorage { pulse_counter_t last_value{0}; }; -class PulseCounterSensor : public sensor::PollingSensorComponent { +class PulseCounterSensor : public sensor::Sensor, public PollingComponent { public: - explicit PulseCounterSensor(const std::string &name, GPIOPin *pin, uint32_t update_interval, - PulseCounterCountMode rising_edge_mode, PulseCounterCountMode falling_edge_mode, - uint32_t filter_us) - : sensor::PollingSensorComponent(name, update_interval), pin_(pin) { - this->storage_.rising_edge_mode = rising_edge_mode; - this->storage_.falling_edge_mode = falling_edge_mode; - this->storage_.filter_us = filter_us; - } + void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_rising_edge_mode(PulseCounterCountMode mode) { storage_.rising_edge_mode = mode; } + void set_falling_edge_mode(PulseCounterCountMode mode) { storage_.falling_edge_mode = mode; } + void set_filter_us(uint32_t filter) { storage_.filter_us = filter; } /// Unit of measurement is "pulses/min". void setup() override; diff --git a/esphome/components/pulse_counter/sensor.py b/esphome/components/pulse_counter/sensor.py index 677f41c97..6fbdb0094 100644 --- a/esphome/components/pulse_counter/sensor.py +++ b/esphome/components/pulse_counter/sensor.py @@ -1,10 +1,10 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -import esphome.config_validation as cv -import esphome.codegen as cg from esphome.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_ID, CONF_INTERNAL_FILTER, \ - CONF_NAME, CONF_PIN, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, CONF_NUMBER, \ - CONF_ACCURACY_DECIMALS, CONF_ICON, CONF_UNIT_OF_MEASUREMENT, ICON_PULSE, UNIT_PULSES_PER_MINUTE + CONF_PIN, CONF_RISING_EDGE, CONF_UPDATE_INTERVAL, CONF_NUMBER, \ + ICON_PULSE, UNIT_PULSES_PER_MINUTE from esphome.core import CORE pulse_counter_ns = cg.esphome_ns.namespace('pulse_counter') @@ -15,7 +15,7 @@ COUNT_MODES = { 'DECREMENT': PulseCounterCountMode.PULSE_COUNTER_DECREMENT, } -COUNT_MODE_SCHEMA = cv.one_of(*COUNT_MODES, upper=True) +COUNT_MODE_SCHEMA = cv.enum(COUNT_MODES, upper=True) PulseCounterSensor = pulse_counter_ns.class_('PulseCounterSensor', sensor.PollingSensorComponent) @@ -38,8 +38,8 @@ def validate_pulse_counter_pin(value): return value -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(PulseCounterSensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2).extend({ + cv.GenerateID(): cv.declare_id(PulseCounterSensor), cv.Required(CONF_PIN): validate_pulse_counter_pin, cv.Optional(CONF_COUNT_MODE, default={ CONF_RISING_EDGE: 'INCREMENT', @@ -50,20 +50,17 @@ CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ }), cv.Optional(CONF_INTERNAL_FILTER, default='13us'): validate_internal_filter, cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, - - cv.Optional(CONF_ACCURACY_DECIMALS, default=2): sensor.accuracy_decimals, - cv.Optional(CONF_ICON, default=ICON_PULSE): sensor.icon, - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PULSES_PER_MINUTE): - sensor.unit_of_measurement, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - pin = yield cg.gpio_pin_expression(config[CONF_PIN]) - count = config[CONF_COUNT_MODE] - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], pin, config[CONF_UPDATE_INTERVAL], - COUNT_MODES[count[CONF_RISING_EDGE]], - COUNT_MODES[count[CONF_FALLING_EDGE]], - config[CONF_INTERNAL_FILTER]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) + + pin = yield cg.gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) + count = config[CONF_COUNT_MODE] + cg.add(var.set_rising_edge_mode(count[CONF_RISING_EDGE])) + cg.add(var.set_falling_edge_mode(count[CONF_FALLING_EDGE])) + cg.add(var.set_filter_us(config[CONF_INTERNAL_FILTER])) diff --git a/esphome/components/pulse_width/pulse_width.h b/esphome/components/pulse_width/pulse_width.h index 7ac085e1f..9d32ce99b 100644 --- a/esphome/components/pulse_width/pulse_width.h +++ b/esphome/components/pulse_width/pulse_width.h @@ -19,6 +19,7 @@ class PulseWidthSensorStore { static void gpio_intr(PulseWidthSensorStore *arg); uint32_t get_pulse_width_us() const { return this->last_width_; } float get_pulse_width_s() const { return this->last_width_ / 1e6f; } + uint32_t get_last_rise() const { return last_rise_; } protected: ISRInternalGPIOPin *pin_; @@ -26,9 +27,8 @@ class PulseWidthSensorStore { volatile uint32_t last_rise_{0}; }; -class PulseWidthSensor : public sensor::PollingSensorComponent { +class PulseWidthSensor : public sensor::Sensor, public PollingComponent { public: - PulseWidthSensor(const std::string &name, uint32_t update_interval) : PollingSensorComponent(name, update_interval) {} void set_pin(GPIOPin *pin) { pin_ = pin; } void setup() override { this->store_.setup(this->pin_); } void dump_config() override; diff --git a/esphome/components/pulse_width/sensor.py b/esphome/components/pulse_width/sensor.py index a2d8605c7..42d1b0dba 100644 --- a/esphome/components/pulse_width/sensor.py +++ b/esphome/components/pulse_width/sensor.py @@ -2,23 +2,21 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_NAME, CONF_PIN, CONF_UPDATE_INTERVAL, UNIT_SECOND, \ - ICON_TIMER +from esphome.const import CONF_ID, CONF_PIN, UNIT_SECOND, ICON_TIMER pulse_width_ns = cg.esphome_ns.namespace('pulse_width') PulseWidthSensor = pulse_width_ns.class_('PulseWidthSensor', sensor.PollingSensorComponent) -CONFIG_SCHEMA = cv.nameable(sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 3).extend({ - cv.GenerateID(): cv.declare_variable_id(PulseWidthSensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 3).extend({ + cv.GenerateID(): cv.declare_id(PulseWidthSensor), cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) diff --git a/esphome/components/rdm6300/__init__.py b/esphome/components/rdm6300/__init__.py index 60b183083..ee5077c31 100644 --- a/esphome/components/rdm6300/__init__.py +++ b/esphome/components/rdm6300/__init__.py @@ -9,12 +9,12 @@ AUTO_LOAD = ['binary_sensor'] rdm6300_ns = cg.esphome_ns.namespace('rdm6300') RDM6300Component = rdm6300_ns.class_('RDM6300Component', cg.Component, uart.UARTDevice) -RDM6300Trigger = rdm6300_ns.class_('RDM6300Trigger', cg.Trigger.template(cg.uint32)) +RDM6300Trigger = rdm6300_ns.class_('RDM6300Trigger', automation.Trigger.template(cg.uint32)) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(RDM6300Component), + cv.GenerateID(): cv.declare_id(RDM6300Component), cv.Optional(CONF_ON_TAG): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(RDM6300Trigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RDM6300Trigger), }), }).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) diff --git a/esphome/components/rdm6300/binary_sensor.py b/esphome/components/rdm6300/binary_sensor.py index ed5e96741..81b24bed0 100644 --- a/esphome/components/rdm6300/binary_sensor.py +++ b/esphome/components/rdm6300/binary_sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor, rdm6300 -from esphome.const import CONF_UID, CONF_ID, CONF_NAME +from esphome.const import CONF_UID, CONF_ID from . import rdm6300_ns DEPENDENCIES = ['rdm6300'] @@ -9,15 +9,17 @@ DEPENDENCIES = ['rdm6300'] CONF_RDM6300_ID = 'rdm6300_id' RDM6300BinarySensor = rdm6300_ns.class_('RDM6300BinarySensor', binary_sensor.BinarySensor) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(RDM6300BinarySensor), - cv.GenerateID(CONF_RDM6300_ID): cv.use_variable_id(rdm6300.RDM6300Component), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(RDM6300BinarySensor), + cv.GenerateID(CONF_RDM6300_ID): cv.use_id(rdm6300.RDM6300Component), cv.Required(CONF_UID): cv.uint32_t, -})) +}) def to_code(config): - hub = yield cg.get_variable(config[CONF_RDM6300_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UID]) + var = cg.new_Pvariable(config[CONF_ID]) yield binary_sensor.register_binary_sensor(var, config) + + hub = yield cg.get_variable(config[CONF_RDM6300_ID]) cg.add(hub.register_card(var)) + cg.add(var.set_id(config[CONF_UID])) diff --git a/esphome/components/rdm6300/rdm6300.h b/esphome/components/rdm6300/rdm6300.h index 3f0e70af5..a67b6e7ce 100644 --- a/esphome/components/rdm6300/rdm6300.h +++ b/esphome/components/rdm6300/rdm6300.h @@ -30,7 +30,7 @@ class RDM6300Component : public Component, public uart::UARTDevice { class RDM6300BinarySensor : public binary_sensor::BinarySensor { public: - RDM6300BinarySensor(const std::string &name, uint32_t id) : BinarySensor(name), id_(id) {} + void set_id(uint32_t id) { id_ = id; } bool process(uint32_t id) { if (this->id_ == id) { diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index ac6cc8aec..cfe3ae4c5 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1,15 +1,14 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.automation import ACTION_REGISTRY -from esphome.components import binary_sensor as binary_sensor_ +from esphome.components import binary_sensor from esphome.const import CONF_DATA, CONF_ID, CONF_TRIGGER_ID, CONF_NBITS, CONF_ADDRESS, \ CONF_COMMAND, CONF_CODE, CONF_PULSE_LENGTH, CONF_SYNC, CONF_ZERO, CONF_ONE, CONF_INVERTED, \ CONF_PROTOCOL, CONF_GROUP, CONF_DEVICE, CONF_STATE, CONF_CHANNEL, CONF_FAMILY, CONF_REPEAT, \ - CONF_WAIT_TIME, CONF_TIMES + CONF_WAIT_TIME, CONF_TIMES, CONF_TYPE_ID from esphome.core import coroutine from esphome.py_compat import string_types, text_type -from esphome.util import ServiceRegistry +from esphome.util import Registry, SimpleRegistry AUTO_LOAD = ['binary_sensor'] @@ -20,10 +19,11 @@ ns = remote_base_ns = cg.esphome_ns.namespace('remote_base') RemoteProtocol = ns.class_('RemoteProtocol') RemoteReceiverListener = ns.class_('RemoteReceiverListener') RemoteReceiverBinarySensorBase = ns.class_('RemoteReceiverBinarySensorBase', - binary_sensor_.BinarySensor, cg.Component) -RemoteReceiverTrigger = ns.class_('RemoteReceiverTrigger', cg.Trigger, RemoteReceiverListener) + binary_sensor.BinarySensor, cg.Component) +RemoteReceiverTrigger = ns.class_('RemoteReceiverTrigger', automation.Trigger, + RemoteReceiverListener) RemoteTransmitterDumper = ns.class_('RemoteTransmitterDumper') -RemoteTransmitterActionBase = ns.class_('RemoteTransmitterActionBase', cg.Action) +RemoteTransmitterActionBase = ns.class_('RemoteTransmitterActionBase', automation.Action) RemoteReceiverBase = ns.class_('RemoteReceiverBase') RemoteTransmitterBase = ns.class_('RemoteTransmitterBase') @@ -44,32 +44,13 @@ def register_listener(var, config): def register_binary_sensor(name, type, schema): - if not isinstance(schema, cv.Schema): - schema = cv.Schema(schema) - validator = schema.extend({ - cv.GenerateID(): cv.declare_variable_id(type), - cv.GenerateID(CONF_RECEIVER_ID): cv.use_variable_id(RemoteReceiverBase), - }) - registerer = BINARY_SENSOR_REGISTRY.register(name, validator) - - def decorator(func): - @coroutine - def new_func(config): - var = cg.new_Pvariable(config[CONF_ID]) - yield cg.register_component(var, config) - yield register_listener(var, config) - yield coroutine(func)(var, config) - yield var - - return registerer(new_func) - - return decorator + return BINARY_SENSOR_REGISTRY.register(name, type, schema) def register_trigger(name, type, data_type): validator = automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(type), - cv.GenerateID(CONF_RECEIVER_ID): cv.use_variable_id(RemoteReceiverBase), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(type), + cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), }) registerer = TRIGGER_REGISTRY.register('on_{}'.format(name), validator) @@ -88,18 +69,12 @@ def register_trigger(name, type, data_type): def register_dumper(name, type): - validator = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(type), - cv.GenerateID(CONF_RECEIVER_ID): cv.use_variable_id(RemoteReceiverBase), - }) - registerer = DUMPER_REGISTRY.register(name, validator) + registerer = DUMPER_REGISTRY.register(name, type, {}) def decorator(func): @coroutine - def new_func(config): - var = cg.new_Pvariable(config[CONF_ID]) - receiver = yield cg.get_variable(config[CONF_RECEIVER_ID]) - cg.add(receiver.register_dumper(var)) + def new_func(config, dumper_id): + var = cg.new_Pvariable(dumper_id) yield coroutine(func)(var, config) yield var @@ -110,22 +85,21 @@ def register_dumper(name, type): def register_action(name, type_, schema): validator = templatize(schema).extend({ - cv.GenerateID(): cv.declare_variable_id(type_), - cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_variable_id(RemoteTransmitterBase), + cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase), cv.Optional(CONF_REPEAT): cv.Schema({ cv.Required(CONF_TIMES): cv.templatable(cv.positive_int), cv.Optional(CONF_WAIT_TIME, default='10ms'): cv.templatable(cv.positive_time_period_milliseconds), }), }) - registerer = ACTION_REGISTRY.register('remote_transmitter.transmit_{}'.format(name), validator) + registerer = automation.register_action('remote_transmitter.transmit_{}'.format(name), + type_, validator) def decorator(func): @coroutine def new_func(config, action_id, template_arg, args): transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID]) - type = type_.template(template_arg) - var = cg.Pvariable(action_id, type.new(), type=type) + var = cg.new_Pvariable(action_id, template_arg) cg.add(var.set_parent(transmitter)) if CONF_REPEAT in config: conf = config[CONF_REPEAT] @@ -143,29 +117,27 @@ def register_action(name, type_, schema): def declare_protocol(name): data = ns.struct('{}Data'.format(name)) - protocol = ns.class_('{}Protocol'.format(name)) - binary_sensor = ns.class_('{}BinarySensor'.format(name), RemoteReceiverBinarySensorBase) + binary_sensor_ = ns.class_('{}BinarySensor'.format(name), RemoteReceiverBinarySensorBase) trigger = ns.class_('{}Trigger'.format(name), RemoteReceiverTrigger) action = ns.class_('{}Action'.format(name), RemoteTransmitterActionBase) dumper = ns.class_('{}Dumper'.format(name), RemoteTransmitterDumper) - return data, protocol, binary_sensor, trigger, action, dumper + return data, binary_sensor_, trigger, action, dumper -BINARY_SENSOR_REGISTRY = ServiceRegistry() -TRIGGER_REGISTRY = ServiceRegistry() -DUMPER_REGISTRY = ServiceRegistry() +BINARY_SENSOR_REGISTRY = Registry(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), +})) +validate_binary_sensor = cv.validate_registry_entry('remote receiver', BINARY_SENSOR_REGISTRY) +TRIGGER_REGISTRY = SimpleRegistry() +DUMPER_REGISTRY = Registry({ + cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), +}) def validate_dumpers(value): if isinstance(value, string_types) and value.lower() == 'all': return validate_dumpers(list(DUMPER_REGISTRY.keys())) - return cv.validate_registry('dumper', DUMPER_REGISTRY, [])(value) - - -def validate_binary_sensor(base_schema): - validator = cv.validate_registry_entry('remote receiver', BINARY_SENSOR_REGISTRY, - cv.extract_keys(base_schema)) - return validator + return cv.validate_registry('dumper', DUMPER_REGISTRY)(value) def validate_triggers(base_schema): @@ -173,7 +145,7 @@ def validate_triggers(base_schema): def validator(config): added_keys = {} - for key, (valid, _) in TRIGGER_REGISTRY.items(): + for key, (_, valid) in TRIGGER_REGISTRY.items(): added_keys[cv.Optional(key)] = valid new_schema = base_schema.extend(added_keys) return new_schema(config) @@ -182,8 +154,14 @@ def validate_triggers(base_schema): @coroutine -def build_binary_sensor(config): - var = yield cg.build_registry_entry(BINARY_SENSOR_REGISTRY, config) +def build_binary_sensor(full_config): + registry_entry, config = cg.extract_registry_entry_config(BINARY_SENSOR_REGISTRY, full_config) + type_id = full_config[CONF_TYPE_ID] + builder = registry_entry.coroutine_fun + var = cg.new_Pvariable(type_id) + yield cg.register_component(var, full_config) + yield register_listener(var, full_config) + yield builder(var, config) yield var @@ -191,17 +169,23 @@ def build_binary_sensor(config): def build_triggers(full_config): for key in TRIGGER_REGISTRY: for config in full_config.get(key, []): - func = TRIGGER_REGISTRY[key][1] + func = TRIGGER_REGISTRY[key][0] yield func(config) @coroutine def build_dumpers(config): - yield cg.build_registry_list(DUMPER_REGISTRY, config) + dumpers = [] + for conf in config: + dumper = yield cg.build_registry_entry(DUMPER_REGISTRY, conf) + receiver = yield cg.get_variable(conf[CONF_RECEIVER_ID]) + cg.add(receiver.register_dumper(dumper)) + dumpers.append(dumper) + yield dumpers # JVC -JVCData, JVCProtocol, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol('JVC') +JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol('JVC') JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t}) @@ -230,7 +214,7 @@ def jvc_action(var, config, args): # LG -LGData, LGProtocol, LGBinarySensor, LGTrigger, LGAction, LGDumper = declare_protocol('LG') +LGData, LGBinarySensor, LGTrigger, LGAction, LGDumper = declare_protocol('LG') LG_SCHEMA = cv.Schema({ cv.Required(CONF_DATA): cv.hex_uint32_t, cv.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True), @@ -265,7 +249,7 @@ def lg_action(var, config, args): # NEC -NECData, NECProtocol, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol('NEC') +NECData, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol('NEC') NEC_SCHEMA = cv.Schema({ cv.Required(CONF_ADDRESS): cv.hex_uint16_t, cv.Required(CONF_COMMAND): cv.hex_uint16_t, @@ -300,8 +284,7 @@ def nec_action(var, config, args): # Sony -SonyData, SonyProtocol, SonyBinarySensor, SonyTrigger, SonyAction, SonyDumper = declare_protocol( - 'Sony') +SonyData, SonyBinarySensor, SonyTrigger, SonyAction, SonyDumper = declare_protocol('Sony') SONY_SCHEMA = cv.Schema({ cv.Required(CONF_DATA): cv.hex_uint32_t, cv.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True), @@ -349,12 +332,12 @@ def validate_raw_alternating(value): return value -RawData, RawProtocol, RawBinarySensor, RawTrigger, RawAction, RawDumper = declare_protocol('Raw') +RawData, RawBinarySensor, RawTrigger, RawAction, RawDumper = declare_protocol('Raw') CONF_CODE_STORAGE_ID = 'code_storage_id' RAW_SCHEMA = cv.Schema({ cv.Required(CONF_CODE): cv.All([cv.Any(cv.int_, cv.time_period_microseconds)], cv.Length(min=1), validate_raw_alternating), - cv.GenerateID(CONF_CODE_STORAGE_ID): cv.declare_variable_id(cg.int32), + cv.GenerateID(CONF_CODE_STORAGE_ID): cv.declare_id(cg.int32), }) @@ -389,7 +372,7 @@ def raw_action(var, config, args): # RC5 -RC5Data, RC5Protocol, RC5BinarySensor, RC5Trigger, RC5Action, RC5Dumper = declare_protocol('RC5') +RC5Data, RC5BinarySensor, RC5Trigger, RC5Action, RC5Dumper = declare_protocol('RC5') RC5_SCHEMA = cv.Schema({ cv.Required(CONF_ADDRESS): cv.All(cv.hex_int, cv.Range(min=0, max=0x1F)), cv.Required(CONF_COMMAND): cv.All(cv.hex_int, cv.Range(min=0, max=0x3F)), @@ -591,7 +574,7 @@ def rc_switch_dumper(var, config): # Samsung -(SamsungData, SamsungProtocol, SamsungBinarySensor, SamsungTrigger, SamsungAction, +(SamsungData, SamsungBinarySensor, SamsungTrigger, SamsungAction, SamsungDumper) = declare_protocol('Samsung') SAMSUNG_SCHEMA = cv.Schema({ cv.Required(CONF_DATA): cv.hex_uint32_t, @@ -623,7 +606,7 @@ def samsung_action(var, config, args): # Panasonic -(PanasonicData, PanasonicProtocol, PanasonicBinarySensor, PanasonicTrigger, PanasonicAction, +(PanasonicData, PanasonicBinarySensor, PanasonicTrigger, PanasonicAction, PanasonicDumper) = declare_protocol('Panasonic') PANASONIC_SCHEMA = cv.Schema({ cv.Required(CONF_ADDRESS): cv.hex_uint16_t, diff --git a/esphome/components/remote_base/raw_protocol.cpp b/esphome/components/remote_base/raw_protocol.cpp index eb4d8d180..4cbd37d47 100644 --- a/esphome/components/remote_base/raw_protocol.cpp +++ b/esphome/components/remote_base/raw_protocol.cpp @@ -11,7 +11,7 @@ void RawDumper::dump(RemoteReceiveData src) { uint32_t buffer_offset = 0; buffer_offset += sprintf(buffer, "Received Raw: "); - for (int32_t i = 0; i < src.size(); i++) { + for (int32_t i = 0; i < src.size() - 1; i++) { const int32_t value = src[i]; const uint32_t remaining_length = sizeof(buffer) - buffer_offset; int written; diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index ddbf679b0..6ad63879a 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -310,7 +310,6 @@ template class RemoteTransmitterActionBase : public Actionsend_times_.value_or(x..., 1)); call.set_send_wait(this->send_wait_.value_or(x..., 0)); call.perform(); - this->play_next(x...); } virtual void encode(RemoteTransmitData *dst, Ts... x) = 0; diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 0553da5b4..5742876e3 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -13,7 +13,7 @@ RemoteReceiverComponent = remote_receiver_ns.class_('RemoteReceiverComponent', MULTI_CONF = True CONFIG_SCHEMA = remote_base.validate_triggers(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(RemoteReceiverComponent), + cv.GenerateID(): cv.declare_id(RemoteReceiverComponent), cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), cv.Optional(CONF_DUMP, default=[]): remote_base.validate_dumpers, diff --git a/esphome/components/remote_receiver/binary_sensor.py b/esphome/components/remote_receiver/binary_sensor.py index c89005767..7be64e5a5 100644 --- a/esphome/components/remote_receiver/binary_sensor.py +++ b/esphome/components/remote_receiver/binary_sensor.py @@ -1,15 +1,10 @@ -from esphome.components import binary_sensor, remote_base -import esphome.config_validation as cv import esphome.codegen as cg +from esphome.components import binary_sensor, remote_base from esphome.const import CONF_NAME DEPENDENCIES = ['remote_receiver'] - -BASE_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({}, extra=cv.ALLOW_EXTRA) - - -CONFIG_SCHEMA = cv.nameable(cv.All(BASE_SCHEMA, remote_base.validate_binary_sensor(BASE_SCHEMA))) +CONFIG_SCHEMA = remote_base.validate_binary_sensor def to_code(config): diff --git a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp index 85c708a5e..cafbc34d6 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp @@ -13,17 +13,20 @@ void ICACHE_RAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverC const uint32_t now = micros(); // If the lhs is 1 (rising edge) we should write to an uneven index and vice versa const uint32_t next = (arg->buffer_write_at + 1) % arg->buffer_size; - if (uint32_t(arg->pin->digital_read()) != next % 2) + const bool level = arg->pin->digital_read(); + if (level != next % 2) return; + + // If next is buffer_read, we have hit an overflow + if (next == arg->buffer_read_at) + return; + const uint32_t last_change = arg->buffer[arg->buffer_write_at]; - if (now - last_change <= arg->filter_us) + const uint32_t time_since_change = now - last_change; + if (time_since_change <= arg->filter_us) return; arg->buffer[arg->buffer_write_at = next] = now; - - if (next == arg->buffer_read_at) { - arg->overflow = true; - } } void RemoteReceiverComponent::setup() { @@ -39,15 +42,16 @@ void RemoteReceiverComponent::setup() { // Make sure divisible by two. This way, we know that every 0bxxx0 index is a space and every 0bxxx1 index is a mark s.buffer_size++; } + s.buffer = new uint32_t[s.buffer_size]; + void *buf = (void *) s.buffer; + memset(buf, 0, s.buffer_size * sizeof(uint32_t)); + // First index is a space. if (this->pin_->digital_read()) { s.buffer_write_at = s.buffer_read_at = 1; - s.buffer[1] = 0; - s.buffer[0] = 0; } else { s.buffer_write_at = s.buffer_read_at = 0; - s.buffer[0] = 0; } this->pin_->attach_interrupt(RemoteReceiverComponentStore::gpio_intr, &this->store_, CHANGE); } @@ -66,12 +70,6 @@ void RemoteReceiverComponent::dump_config() { void RemoteReceiverComponent::loop() { auto &s = this->store_; - if (s.overflow) { - s.buffer_read_at = s.buffer_write_at; - s.overflow = false; - ESP_LOGW(TAG, "Data is coming in too fast! Try increasing the buffer size."); - return; - } // copy write at to local variables, as it's volatile const uint32_t write_at = s.buffer_write_at; @@ -82,7 +80,6 @@ void RemoteReceiverComponent::loop() { const uint32_t now = micros(); if (now - s.buffer[write_at] < this->idle_us_) // The last change was fewer than the configured idle time ago. - // TODO: Handle case when loop() is not called quickly enough to catch idle return; ESP_LOGVV(TAG, "read_at=%u write_at=%u dist=%u now=%u end=%u", s.buffer_read_at, write_at, dist, now, diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index 90be20f9e..5e217de60 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -12,7 +12,7 @@ RemoteTransmitterComponent = remote_transmitter_ns.class_('RemoteTransmitterComp MULTI_CONF = True CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(RemoteTransmitterComponent), + cv.GenerateID(): cv.declare_id(RemoteTransmitterComponent), cv.Required(CONF_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All(cv.percentage_int, cv.Range(min=1, max=100)), }).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 6e5ef4617..a746d4392 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -36,7 +36,6 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, pu std::vector rmt_temp_; #endif uint8_t carrier_duty_percent_{50}; - remote_base::RemoteTransmitData temp_; }; } // namespace remote_transmitter diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index 61d5bbb57..7704f1d9a 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -63,12 +63,10 @@ void RemoteTransmitterComponent::space_(uint32_t usec) { delay_microseconds_accurate(usec); } void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { + ESP_LOGD(TAG, "Sending remote code..."); + uint32_t on_time, off_time; + this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); for (uint32_t i = 0; i < send_times; i++) { - uint32_t on_time, off_time; - this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); - ESP_LOGD(TAG, "Sending remote code..."); - - ESP.wdtFeed(); disable_interrupts(); for (int32_t item : this->temp_.get_data()) { if (item > 0) { diff --git a/esphome/components/restart/restart_switch.h b/esphome/components/restart/restart_switch.h index c1bcf9e99..7f1902ab5 100644 --- a/esphome/components/restart/restart_switch.h +++ b/esphome/components/restart/restart_switch.h @@ -8,8 +8,6 @@ namespace restart { class RestartSwitch : public switch_::Switch, public Component { public: - explicit RestartSwitch(const std::string &name) : switch_::Switch(name) {} - void dump_config() override; protected: diff --git a/esphome/components/restart/switch.py b/esphome/components/restart/switch.py index 0042feb5b..9517302d3 100644 --- a/esphome/components/restart/switch.py +++ b/esphome/components/restart/switch.py @@ -1,20 +1,20 @@ -from esphome.components import switch -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_INVERTED, CONF_NAME, CONF_ICON, ICON_RESTART +import esphome.config_validation as cv +from esphome.components import switch +from esphome.const import CONF_ID, CONF_INVERTED, CONF_ICON, ICON_RESTART restart_ns = cg.esphome_ns.namespace('restart') RestartSwitch = restart_ns.class_('RestartSwitch', switch.Switch, cg.Component) -CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(RestartSwitch), +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(RestartSwitch), cv.Optional(CONF_INVERTED): cv.invalid("Restart switches do not support inverted mode!"), cv.Optional(CONF_ICON, default=ICON_RESTART): switch.icon, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield switch.register_switch(var, config) diff --git a/esphome/components/rgb/light.py b/esphome/components/rgb/light.py index 16932596b..6bece1766 100644 --- a/esphome/components/rgb/light.py +++ b/esphome/components/rgb/light.py @@ -6,17 +6,21 @@ from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_OUTPUT_ID rgb_ns = cg.esphome_ns.namespace('rgb') RGBLightOutput = rgb_ns.class_('RGBLightOutput', light.LightOutput) -CONFIG_SCHEMA = cv.nameable(light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(RGBLightOutput), - cv.Required(CONF_RED): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput), -})) +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBLightOutput), + cv.Required(CONF_RED): cv.use_id(output.FloatOutput), + cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), + cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), +}) def to_code(config): - red = yield cg.get_variable(config[CONF_RED]) - green = yield cg.get_variable(config[CONF_GREEN]) - blue = yield cg.get_variable(config[CONF_BLUE]) - var = cg.new_Pvariable(config[CONF_OUTPUT_ID], red, green, blue) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) yield light.register_light(var, config) + + red = yield cg.get_variable(config[CONF_RED]) + cg.add(var.set_red(red)) + green = yield cg.get_variable(config[CONF_GREEN]) + cg.add(var.set_green(green)) + blue = yield cg.get_variable(config[CONF_BLUE]) + cg.add(var.set_blue(blue)) diff --git a/esphome/components/rgb/rgb_light_output.h b/esphome/components/rgb/rgb_light_output.h index b6b102200..e612c80f7 100644 --- a/esphome/components/rgb/rgb_light_output.h +++ b/esphome/components/rgb/rgb_light_output.h @@ -9,8 +9,9 @@ namespace rgb { class RGBLightOutput : public light::LightOutput { public: - RGBLightOutput(output::FloatOutput *red, output::FloatOutput *green, output::FloatOutput *blue) - : red_(red), green_(green), blue_(blue) {} + void set_red(output::FloatOutput *red) { red_ = red; } + void set_green(output::FloatOutput *green) { green_ = green; } + void set_blue(output::FloatOutput *blue) { blue_ = blue; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); traits.set_supports_brightness(true); diff --git a/esphome/components/rgbw/light.py b/esphome/components/rgbw/light.py index 9a4baea60..75d6082e5 100644 --- a/esphome/components/rgbw/light.py +++ b/esphome/components/rgbw/light.py @@ -6,19 +6,24 @@ from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_OUTPUT_ID, CONF_ rgbw_ns = cg.esphome_ns.namespace('rgbw') RGBWLightOutput = rgbw_ns.class_('RGBWLightOutput', light.LightOutput) -CONFIG_SCHEMA = cv.nameable(light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(RGBWLightOutput), - cv.Required(CONF_RED): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_WHITE): cv.use_variable_id(output.FloatOutput), -})) +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBWLightOutput), + cv.Required(CONF_RED): cv.use_id(output.FloatOutput), + cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), + cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), + cv.Required(CONF_WHITE): cv.use_id(output.FloatOutput), +}) def to_code(config): - red = yield cg.get_variable(config[CONF_RED]) - green = yield cg.get_variable(config[CONF_GREEN]) - blue = yield cg.get_variable(config[CONF_BLUE]) - white = yield cg.get_variable(config[CONF_WHITE]) - var = cg.new_Pvariable(config[CONF_OUTPUT_ID], red, green, blue, white) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) yield light.register_light(var, config) + + red = yield cg.get_variable(config[CONF_RED]) + cg.add(var.set_red(red)) + green = yield cg.get_variable(config[CONF_GREEN]) + cg.add(var.set_green(green)) + blue = yield cg.get_variable(config[CONF_BLUE]) + cg.add(var.set_blue(blue)) + white = yield cg.get_variable(config[CONF_WHITE]) + cg.add(var.set_white(white)) diff --git a/esphome/components/rgbw/rgbw_light_output.h b/esphome/components/rgbw/rgbw_light_output.h index 92582e0b5..b58c7f9d5 100644 --- a/esphome/components/rgbw/rgbw_light_output.h +++ b/esphome/components/rgbw/rgbw_light_output.h @@ -9,9 +9,10 @@ namespace rgbw { class RGBWLightOutput : public light::LightOutput { public: - RGBWLightOutput(output::FloatOutput *red, output::FloatOutput *green, output::FloatOutput *blue, - output::FloatOutput *white) - : red_(red), green_(green), blue_(blue), white_(white) {} + void set_red(output::FloatOutput *red) { red_ = red; } + void set_green(output::FloatOutput *green) { green_ = green; } + void set_blue(output::FloatOutput *blue) { blue_ = blue; } + void set_white(output::FloatOutput *white) { white_ = white; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); traits.set_supports_brightness(true); diff --git a/esphome/components/rgbww/light.py b/esphome/components/rgbww/light.py index 4c458f42e..143542f49 100644 --- a/esphome/components/rgbww/light.py +++ b/esphome/components/rgbww/light.py @@ -8,25 +8,33 @@ from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_OUTPUT_ID, CONF_ rgbww_ns = cg.esphome_ns.namespace('rgbww') RGBWWLightOutput = rgbww_ns.class_('RGBWWLightOutput', light.LightOutput) -CONFIG_SCHEMA = cv.nameable(light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(RGBWWLightOutput), - cv.Required(CONF_RED): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_GREEN): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_BLUE): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_COLD_WHITE): cv.use_variable_id(output.FloatOutput), - cv.Required(CONF_WARM_WHITE): cv.use_variable_id(output.FloatOutput), +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBWWLightOutput), + cv.Required(CONF_RED): cv.use_id(output.FloatOutput), + cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), + cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), + cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, -})) +}) def to_code(config): - red = yield cg.get_variable(config[CONF_RED]) - green = yield cg.get_variable(config[CONF_GREEN]) - blue = yield cg.get_variable(config[CONF_BLUE]) - cwhite = yield cg.get_variable(config[CONF_COLD_WHITE]) - wwhite = yield cg.get_variable(config[CONF_WARM_WHITE]) - var = cg.new_Pvariable(config[CONF_OUTPUT_ID], red, green, blue, cwhite, wwhite, - config[CONF_COLD_WHITE_COLOR_TEMPERATURE], - config[CONF_WARM_WHITE_COLOR_TEMPERATURE]) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) yield light.register_light(var, config) + + red = yield cg.get_variable(config[CONF_RED]) + cg.add(var.set_red(red)) + green = yield cg.get_variable(config[CONF_GREEN]) + cg.add(var.set_green(green)) + blue = yield cg.get_variable(config[CONF_BLUE]) + cg.add(var.set_blue(blue)) + + cwhite = yield cg.get_variable(config[CONF_COLD_WHITE]) + cg.add(var.set_cold_white(cwhite)) + cg.add(var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE])) + + wwhite = yield cg.get_variable(config[CONF_WARM_WHITE]) + cg.add(var.set_warm_white(wwhite)) + cg.add(var.set_warm_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE])) diff --git a/esphome/components/rgbww/rgbww_light_output.h b/esphome/components/rgbww/rgbww_light_output.h index d6693e453..dc1f09e09 100644 --- a/esphome/components/rgbww/rgbww_light_output.h +++ b/esphome/components/rgbww/rgbww_light_output.h @@ -9,16 +9,13 @@ namespace rgbww { class RGBWWLightOutput : public light::LightOutput { public: - RGBWWLightOutput(output::FloatOutput *red, output::FloatOutput *green, output::FloatOutput *blue, - output::FloatOutput *cold_white, output::FloatOutput *warm_white, float cold_white_temperature, - float warm_white_temperature) - : red_(red), - green_(green), - blue_(blue), - cold_white_(cold_white), - warm_white_(warm_white), - cold_white_temperature_(cold_white_temperature), - warm_white_temperature_(warm_white_temperature) {} + void set_red(output::FloatOutput *red) { red_ = red; } + void set_green(output::FloatOutput *green) { green_ = green; } + void set_blue(output::FloatOutput *blue) { blue_ = blue; } + void set_cold_white(output::FloatOutput *cold_white) { cold_white_ = cold_white; } + void set_warm_white(output::FloatOutput *warm_white) { warm_white_ = warm_white; } + void set_cold_white_temperature(float cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; } + void set_warm_white_temperature(float warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); traits.set_supports_brightness(true); diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index d97d61cf9..d88329d06 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -96,8 +96,6 @@ void ICACHE_RAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensor arg->state = new_state; } -RotaryEncoderSensor::RotaryEncoderSensor(const std::string &name, GPIOPin *pin_a, GPIOPin *pin_b) - : Sensor(name), Component(), pin_a_(pin_a), pin_b_(pin_b) {} void RotaryEncoderSensor::setup() { ESP_LOGCONFIG(TAG, "Setting up Rotary Encoder '%s'...", this->name_.c_str()); diff --git a/esphome/components/rotary_encoder/rotary_encoder.h b/esphome/components/rotary_encoder/rotary_encoder.h index b0239d01b..b627a4e57 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.h +++ b/esphome/components/rotary_encoder/rotary_encoder.h @@ -31,7 +31,8 @@ struct RotaryEncoderSensorStore { class RotaryEncoderSensor : public sensor::Sensor, public Component { public: - RotaryEncoderSensor(const std::string &name, GPIOPin *pin_a, GPIOPin *pin_b); + void set_pin_a(GPIOPin *pin_a) { pin_a_ = pin_a; } + void set_pin_b(GPIOPin *pin_b) { pin_b_ = pin_b; } /** Set the resolution of the rotary encoder. * diff --git a/esphome/components/rotary_encoder/sensor.py b/esphome/components/rotary_encoder/sensor.py index ca4db7b84..3eb2a5ca4 100644 --- a/esphome/components/rotary_encoder/sensor.py +++ b/esphome/components/rotary_encoder/sensor.py @@ -2,7 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_NAME, CONF_RESOLUTION, CONF_MIN_VALUE, CONF_MAX_VALUE +from esphome.const import CONF_ID, CONF_RESOLUTION, CONF_MIN_VALUE, CONF_MAX_VALUE, UNIT_STEPS, \ + ICON_ROTATE_RIGHT rotary_encoder_ns = cg.esphome_ns.namespace('rotary_encoder') RotaryEncoderResolution = rotary_encoder_ns.enum('RotaryEncoderResolution') @@ -29,32 +30,32 @@ def validate_min_max_value(config): return config -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(RotaryEncoderSensor), +CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_STEPS, ICON_ROTATE_RIGHT, 0).extend({ + cv.GenerateID(): cv.declare_id(RotaryEncoderSensor), cv.Required(CONF_PIN_A): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), cv.Required(CONF_PIN_B): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), cv.Optional(CONF_PIN_RESET): pins.internal_gpio_input_pin_schema, - cv.Optional(CONF_RESOLUTION, default=1): cv.one_of(*RESOLUTIONS, int=True), + cv.Optional(CONF_RESOLUTION, default=1): cv.enum(RESOLUTIONS, int=True), cv.Optional(CONF_MIN_VALUE): cv.int_, cv.Optional(CONF_MAX_VALUE): cv.int_, }).extend(cv.COMPONENT_SCHEMA), validate_min_max_value) def to_code(config): - pin_a = yield cg.gpio_pin_expression(config[CONF_PIN_A]) - pin_b = yield cg.gpio_pin_expression(config[CONF_PIN_B]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], pin_a, pin_b) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) + pin_a = yield cg.gpio_pin_expression(config[CONF_PIN_A]) + cg.add(var.set_pin_a(pin_a)) + pin_b = yield cg.gpio_pin_expression(config[CONF_PIN_B]) + cg.add(var.set_pin_a(pin_b)) if CONF_PIN_RESET in config: pin_i = yield cg.gpio_pin_expression(config[CONF_PIN_RESET]) cg.add(var.set_reset_pin(pin_i)) - if CONF_RESOLUTION in config: - resolution = RESOLUTIONS[config[CONF_RESOLUTION]] - cg.add(var.set_resolution(resolution)) + cg.add(var.set_resolution(config[CONF_RESOLUTION])) if CONF_MIN_VALUE in config: cg.add(var.set_min_value(config[CONF_MIN_VALUE])) if CONF_MAX_VALUE in config: diff --git a/esphome/components/script/__init__.py b/esphome/components/script/__init__.py index 06017f19e..e45300930 100644 --- a/esphome/components/script/__init__.py +++ b/esphome/components/script/__init__.py @@ -1,16 +1,16 @@ -from esphome import automation -from esphome.automation import ACTION_REGISTRY, maybe_simple_id -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.automation import maybe_simple_id from esphome.const import CONF_ID script_ns = cg.esphome_ns.namespace('script') -Script = script_ns.class_('Script', cg.Trigger.template()) -ScriptExecuteAction = script_ns.class_('ScriptExecuteAction', cg.Action) -ScriptStopAction = script_ns.class_('ScriptStopAction', cg.Action) +Script = script_ns.class_('Script', automation.Trigger.template()) +ScriptExecuteAction = script_ns.class_('ScriptExecuteAction', automation.Action) +ScriptStopAction = script_ns.class_('ScriptStopAction', automation.Action) CONFIG_SCHEMA = automation.validate_automation({ - cv.Required(CONF_ID): cv.declare_variable_id(Script), + cv.Required(CONF_ID): cv.declare_id(Script), }) @@ -20,21 +20,17 @@ def to_code(config): yield automation.build_automation(trigger, [], conf) -@ACTION_REGISTRY.register('script.execute', maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(Script), +@automation.register_action('script.execute', ScriptExecuteAction, maybe_simple_id({ + cv.Required(CONF_ID): cv.use_id(Script), })) def script_execute_action_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ScriptExecuteAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('script.stop', maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(Script) +@automation.register_action('script.stop', ScriptStopAction, maybe_simple_id({ + cv.Required(CONF_ID): cv.use_id(Script) })) def script_stop_action_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ScriptStopAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/script/script.h b/esphome/components/script/script.h index bac275e37..9452aa0d2 100644 --- a/esphome/components/script/script.h +++ b/esphome/components/script/script.h @@ -14,10 +14,7 @@ template class ScriptExecuteAction : public Action { public: ScriptExecuteAction(Script *script) : script_(script) {} - void play(Ts... x) override { - this->script_->trigger(); - this->play_next(x...); - } + void play(Ts... x) override { this->script_->trigger(); } protected: Script *script_; @@ -27,10 +24,7 @@ template class ScriptStopAction : public Action { public: ScriptStopAction(Script *script) : script_(script) {} - void play(Ts... x) override { - this->script_->stop(); - this->play_next(x...); - } + void play(Ts... x) override { this->script_->stop(); } protected: Script *script_; diff --git a/esphome/components/sds011/sds011.h b/esphome/components/sds011/sds011.h index 5075ae384..83f89df23 100644 --- a/esphome/components/sds011/sds011.h +++ b/esphome/components/sds011/sds011.h @@ -22,6 +22,8 @@ class SDS011Component : public Component, public uart::UARTDevice { float get_setup_priority() const override; + void set_update_interval(uint32_t val) { /* ignore */ + } void set_update_interval_min(uint8_t update_interval_min); protected: diff --git a/esphome/components/sds011/sensor.py b/esphome/components/sds011/sensor.py index da852e94f..5b34b2dbb 100644 --- a/esphome/components/sds011/sensor.py +++ b/esphome/components/sds011/sensor.py @@ -25,12 +25,12 @@ def validate_sds011_rx_mode(value): CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(SDS011Component), + cv.GenerateID(): cv.declare_id(SDS011Component), cv.Optional(CONF_PM_2_5): - cv.nameable(sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1)), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1), cv.Optional(CONF_PM_10_0): - cv.nameable(sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1)), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1), cv.Optional(CONF_RX_ONLY, default=False): cv.boolean, cv.Optional(CONF_UPDATE_INTERVAL, default='0min'): cv.positive_time_period_minutes, diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 72d5701f3..ff1d056a6 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -3,18 +3,15 @@ import math import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.automation import CONDITION_REGISTRY from esphome.components import mqtt from esphome.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \ - CONF_CALIBRATE_LINEAR, CONF_DEBOUNCE, CONF_DELTA, CONF_EXPIRE_AFTER, \ - CONF_FILTERS, CONF_FROM, \ - CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_LAMBDA, CONF_MULTIPLY, CONF_OFFSET, \ - CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, CONF_OR, \ - CONF_SEND_EVERY, CONF_SEND_FIRST_AT, CONF_THROTTLE, CONF_TO, CONF_TRIGGER_ID, \ + CONF_EXPIRE_AFTER, CONF_FILTERS, CONF_FROM, CONF_ICON, CONF_ID, CONF_INTERNAL, \ + CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, \ + CONF_SEND_EVERY, CONF_SEND_FIRST_AT, CONF_TO, CONF_TRIGGER_ID, \ CONF_UNIT_OF_MEASUREMENT, \ - CONF_WINDOW_SIZE, CONF_VALUE, CONF_HEARTBEAT, CONF_NAME, CONF_MQTT_ID + CONF_WINDOW_SIZE, CONF_NAME, CONF_MQTT_ID from esphome.core import CORE, coroutine -from esphome.util import ServiceRegistry +from esphome.util import Registry IS_PLATFORM_COMPONENT = True @@ -28,8 +25,8 @@ def validate_send_first_at(value): return value -FILTER_REGISTRY = ServiceRegistry() -validate_filters = cv.validate_registry('filter', FILTER_REGISTRY, [CONF_ID]) +FILTER_REGISTRY = Registry() +validate_filters = cv.validate_registry('filter', FILTER_REGISTRY) def validate_datapoint(value): @@ -57,11 +54,12 @@ SensorPtr = Sensor.operator('ptr') PollingSensorComponent = sensor_ns.class_('PollingSensorComponent', cg.PollingComponent, Sensor) # Triggers -SensorStateTrigger = sensor_ns.class_('SensorStateTrigger', cg.Trigger.template(cg.float_)) -SensorRawStateTrigger = sensor_ns.class_('SensorRawStateTrigger', cg.Trigger.template(cg.float_)) -ValueRangeTrigger = sensor_ns.class_('ValueRangeTrigger', cg.Trigger.template(cg.float_), +SensorStateTrigger = sensor_ns.class_('SensorStateTrigger', automation.Trigger.template(cg.float_)) +SensorRawStateTrigger = sensor_ns.class_('SensorRawStateTrigger', + automation.Trigger.template(cg.float_)) +ValueRangeTrigger = sensor_ns.class_('ValueRangeTrigger', automation.Trigger.template(cg.float_), cg.Component) -SensorPublishAction = sensor_ns.class_('SensorPublishAction', cg.Action) +SensorPublishAction = sensor_ns.class_('SensorPublishAction', automation.Action) # Filters Filter = sensor_ns.class_('Filter') @@ -84,8 +82,8 @@ accuracy_decimals = cv.int_ icon = cv.icon SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTSensorComponent), - cv.GenerateID(): cv.declare_variable_id(Sensor), + cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTSensorComponent), + cv.GenerateID(): cv.declare_id(Sensor), cv.Optional(CONF_UNIT_OF_MEASUREMENT): unit_of_measurement, cv.Optional(CONF_ICON): icon, cv.Optional(CONF_ACCURACY_DECIMALS): accuracy_decimals, @@ -93,13 +91,13 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ cv.Any(None, cv.positive_time_period_milliseconds)), cv.Optional(CONF_FILTERS): validate_filters, cv.Optional(CONF_ON_VALUE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorStateTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorStateTrigger), }), cv.Optional(CONF_ON_RAW_VALUE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SensorRawStateTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorRawStateTrigger), }), cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ValueRangeTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger), cv.Optional(CONF_ABOVE): cv.float_, cv.Optional(CONF_BELOW): cv.float_, }, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)), @@ -115,117 +113,87 @@ def sensor_schema(unit_of_measurement_, icon_, accuracy_decimals_): }) -@FILTER_REGISTRY.register(CONF_OFFSET, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(OffsetFilter), - cv.Required(CONF_VALUE): cv.float_, -}))) -def offset_filter_to_code(config): - yield cg.new_Pvariable(config[CONF_ID], config[CONF_VALUE]) +@FILTER_REGISTRY.register('offset', OffsetFilter, cv.float_) +def offset_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register(CONF_MULTIPLY, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(MultiplyFilter), - cv.Required(CONF_VALUE): cv.float_, -}))) -def multiply_filter_to_code(config): - yield cg.new_Pvariable(config[CONF_ID], config[CONF_VALUE]) +@FILTER_REGISTRY.register('multiply', MultiplyFilter, cv.float_) +def multiply_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register('filter_out', cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(FilterOutValueFilter), - cv.Required(CONF_VALUE): cv.float_, -}))) -def filter_out_filter_to_code(config): - yield cg.new_Pvariable(config[CONF_ID], config[CONF_VALUE]) +@FILTER_REGISTRY.register('filter_out', FilterOutValueFilter, cv.float_) +def filter_out_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register('sliding_window_moving_average', cv.All(cv.Schema({ - cv.GenerateID(): - cv.declare_variable_id(SlidingWindowMovingAverageFilter), +SLIDING_AVERAGE_SCHEMA = cv.All(cv.Schema({ cv.Optional(CONF_WINDOW_SIZE, default=15): cv.positive_not_null_int, cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, -}), validate_send_first_at)) -def sliding_window_moving_average_filter_to_code(config): - yield cg.new_Pvariable(config[CONF_ID], config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY], +}), validate_send_first_at) + + +@FILTER_REGISTRY.register('sliding_window_moving_average', SlidingWindowMovingAverageFilter, + SLIDING_AVERAGE_SCHEMA) +def sliding_window_moving_average_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY], config[CONF_SEND_FIRST_AT]) -@FILTER_REGISTRY.register('exponential_moving_average', cv.Schema({ - cv.GenerateID(): - cv.declare_variable_id(ExponentialMovingAverageFilter), +@FILTER_REGISTRY.register('exponential_moving_average', ExponentialMovingAverageFilter, cv.Schema({ cv.Optional(CONF_ALPHA, default=0.1): cv.positive_float, cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, })) -def exponential_moving_average_filter_to_code(config): - yield cg.new_Pvariable(config[CONF_ID], config[CONF_ALPHA], config[CONF_SEND_EVERY]) +def exponential_moving_average_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config[CONF_ALPHA], config[CONF_SEND_EVERY]) -@FILTER_REGISTRY.register(CONF_LAMBDA, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(LambdaFilter), - cv.Required(CONF_VALUE): cv.lambda_, -}))) -def lambda_filter_to_code(config): - lambda_ = yield cg.process_lambda(config[CONF_VALUE], [(float, 'x')], +@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.lambda_) +def lambda_filter_to_code(config, filter_id): + lambda_ = yield cg.process_lambda(config, [(float, 'x')], return_type=cg.optional.template(float)) - yield cg.new_Pvariable(config[CONF_ID], lambda_) + yield cg.new_Pvariable(filter_id, lambda_) -@FILTER_REGISTRY.register(CONF_DELTA, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DeltaFilter), - cv.Required(CONF_VALUE): cv.float_, -}))) -def delta_filter_to_code(config): - yield cg.new_Pvariable(config[CONF_ID], config[CONF_VALUE]) +@FILTER_REGISTRY.register('delta', DeltaFilter, cv.float_) +def delta_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register(CONF_OR, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(OrFilter), - cv.Required(CONF_VALUE): validate_filters, -}))) -def or_filter_to_code(config): - filters = yield build_filters(config[CONF_VALUE]) - yield cg.new_Pvariable(config[CONF_ID], filters) +@FILTER_REGISTRY.register('or', OrFilter, validate_filters) +def or_filter_to_code(config, filter_id): + filters = yield build_filters(config) + yield cg.new_Pvariable(filter_id, filters) -@FILTER_REGISTRY.register(CONF_THROTTLE, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(ThrottleFilter), - cv.Required(CONF_VALUE): cv.positive_time_period_milliseconds, -}))) -def throttle_filter_to_code(config): - yield cg.new_Pvariable(config[CONF_ID], config[CONF_VALUE]) +@FILTER_REGISTRY.register('throttle', ThrottleFilter, cv.positive_time_period_milliseconds) +def throttle_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register(CONF_HEARTBEAT, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(HeartbeatFilter), - cv.Required(CONF_VALUE): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA))) -def heartbeat_filter_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_VALUE]) - yield cg.register_component(var, config) +@FILTER_REGISTRY.register('heartbeat', HeartbeatFilter, cv.positive_time_period_milliseconds) +def heartbeat_filter_to_code(config, filter_id): + var = cg.new_Pvariable(filter_id, config) + yield cg.register_component(var, {}) yield var -@FILTER_REGISTRY.register(CONF_DEBOUNCE, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(DebounceFilter), - cv.Required(CONF_VALUE): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA))) -def debounce_filter_to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_VALUE]) - yield cg.register_component(var, config) +@FILTER_REGISTRY.register('debounce', DebounceFilter, cv.positive_time_period_milliseconds) +def debounce_filter_to_code(config, filter_id): + var = cg.new_Pvariable(filter_id, config) + yield cg.register_component(var, {}) yield var -@FILTER_REGISTRY.register(CONF_CALIBRATE_LINEAR, cv.maybe_simple_value(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(CalibrateLinearFilter), - cv.Required(CONF_VALUE): cv.All( - cv.ensure_list(validate_datapoint), cv.Length(min=2)), -}).extend(cv.COMPONENT_SCHEMA))) -def calibrate_linear_filter_to_code(config): - x = [conf[CONF_FROM] for conf in config[CONF_VALUE]] - y = [conf[CONF_TO] for conf in config[CONF_VALUE]] +@FILTER_REGISTRY.register('calibrate_linear', CalibrateLinearFilter, cv.All( + cv.ensure_list(validate_datapoint), cv.Length(min=2))) +def calibrate_linear_filter_to_code(config, filter_id): + x = [conf[CONF_FROM] for conf in config] + y = [conf[CONF_TO] for conf in config] k, b = fit_linear(x, y) - yield cg.new_Pvariable(config[CONF_ID], k, b) + yield cg.new_Pvariable(filter_id, k, b) @coroutine @@ -235,6 +203,7 @@ def build_filters(config): @coroutine def setup_sensor_core_(var, config): + cg.add(var.set_name(config[CONF_NAME])) if CONF_INTERNAL in config: cg.add(var.set_internal(config[CONF_INTERNAL])) if CONF_UNIT_OF_MEASUREMENT in config: @@ -285,32 +254,30 @@ def register_sensor(var, config): @coroutine def new_sensor(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield register_sensor(var, config) yield var -CONF_SENSOR_IN_RANGE = 'sensor.in_range' SENSOR_IN_RANGE_CONDITION_SCHEMA = cv.All({ - cv.Required(CONF_ID): cv.use_variable_id(Sensor), + cv.Required(CONF_ID): cv.use_id(Sensor), cv.Optional(CONF_ABOVE): cv.float_, cv.Optional(CONF_BELOW): cv.float_, }, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)) -@CONDITION_REGISTRY.register(CONF_SENSOR_IN_RANGE, SENSOR_IN_RANGE_CONDITION_SCHEMA) +@automation.register_condition('sensor.in_range', SensorInRangeCondition, + SENSOR_IN_RANGE_CONDITION_SCHEMA) def sensor_in_range_to_code(config, condition_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - rhs = var.make_sensor_in_range_condition(template_arg) - type = SensorInRangeCondition.template(template_arg) - cond = cg.Pvariable(condition_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(condition_id, template_arg, paren) if CONF_ABOVE in config: - cg.add(cond.set_min(config[CONF_ABOVE])) + cg.add(var.set_min(config[CONF_ABOVE])) if CONF_BELOW in config: - cg.add(cond.set_max(config[CONF_BELOW])) + cg.add(var.set_max(config[CONF_BELOW])) - yield cond + yield var def _mean(xs): diff --git a/esphome/components/sensor/automation.h b/esphome/components/sensor/automation.h index 158b99357..079077dba 100644 --- a/esphome/components/sensor/automation.h +++ b/esphome/components/sensor/automation.h @@ -25,10 +25,7 @@ template class SensorPublishAction : public Action { public: SensorPublishAction(Sensor *sensor) : sensor_(sensor) {} TEMPLATABLE_VALUE(float, state) - void play(Ts... x) override { - this->sensor_->publish_state(this->state_.value(x...)); - this->play_next(x...); - } + void play(Ts... x) override { this->sensor_->publish_state(this->state_.value(x...)); } protected: Sensor *sensor_; diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 7cb6e1268..4923a1c09 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -163,10 +163,10 @@ optional DeltaFilter::new_value(float value) { // OrFilter OrFilter::OrFilter(std::vector filters) : filters_(std::move(filters)), phi_(this) {} -OrFilter::PhiNode::PhiNode(OrFilter *parent) : parent_(parent) {} +OrFilter::PhiNode::PhiNode(OrFilter *or_parent) : or_parent_(or_parent) {} optional OrFilter::PhiNode::new_value(float value) { - this->parent_->output(value); + this->or_parent_->output(value); return {}; } diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 753a9ce1c..6bd22be23 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -222,11 +222,11 @@ class OrFilter : public Filter { protected: class PhiNode : public Filter { public: - PhiNode(OrFilter *parent); + PhiNode(OrFilter *or_parent); optional new_value(float value) override; protected: - OrFilter *parent_; + OrFilter *or_parent_; }; std::vector filters_; diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index c9710b7d2..1c7c85439 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -26,8 +26,8 @@ namespace sensor { */ class Sensor : public Nameable { public: - explicit Sensor(const std::string &name); explicit Sensor(); + explicit Sensor(const std::string &name); /** Manually set the unit of measurement of this sensor. By default the sensor's default defined by * unit_of_measurement() is used. diff --git a/esphome/components/servo/__init__.py b/esphome/components/servo/__init__.py index c16bff047..92243ee18 100644 --- a/esphome/components/servo/__init__.py +++ b/esphome/components/servo/__init__.py @@ -1,19 +1,20 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.automation import ACTION_REGISTRY, maybe_simple_id +from esphome import automation +from esphome.automation import maybe_simple_id from esphome.components.output import FloatOutput from esphome.const import CONF_ID, CONF_IDLE_LEVEL, CONF_MAX_LEVEL, CONF_MIN_LEVEL, CONF_OUTPUT, \ CONF_LEVEL servo_ns = cg.esphome_ns.namespace('servo') Servo = servo_ns.class_('Servo', cg.Component) -ServoWriteAction = servo_ns.class_('ServoWriteAction', cg.Action) -ServoDetachAction = servo_ns.class_('ServoDetachAction', cg.Action) +ServoWriteAction = servo_ns.class_('ServoWriteAction', automation.Action) +ServoDetachAction = servo_ns.class_('ServoDetachAction', automation.Action) MULTI_CONF = True CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_variable_id(Servo), - cv.Required(CONF_OUTPUT): cv.use_variable_id(FloatOutput), + cv.Required(CONF_ID): cv.declare_id(Servo), + cv.Required(CONF_OUTPUT): cv.use_id(FloatOutput), cv.Optional(CONF_MIN_LEVEL, default='3%'): cv.percentage, cv.Optional(CONF_IDLE_LEVEL, default='7.5%'): cv.percentage, cv.Optional(CONF_MAX_LEVEL, default='12%'): cv.percentage, @@ -21,34 +22,31 @@ CONFIG_SCHEMA = cv.Schema({ def to_code(config): - out = yield cg.get_variable(config[CONF_OUTPUT]) - var = cg.new_Pvariable(config[CONF_ID], out) - cg.register_component(var, config) + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + out = yield cg.get_variable(config[CONF_OUTPUT]) + cg.add(var.set_output(out)) cg.add(var.set_min_level(config[CONF_MIN_LEVEL])) cg.add(var.set_idle_level(config[CONF_IDLE_LEVEL])) cg.add(var.set_max_level(config[CONF_MAX_LEVEL])) -@ACTION_REGISTRY.register('servo.write', cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(Servo), +@automation.register_action('servo.write', ServoWriteAction, cv.Schema({ + cv.Required(CONF_ID): cv.use_id(Servo), cv.Required(CONF_LEVEL): cv.templatable(cv.possibly_negative_percentage), })) def servo_write_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ServoWriteAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_LEVEL], args, float) - cg.add(action.set_value(template_)) - yield action + cg.add(var.set_value(template_)) + yield var -@ACTION_REGISTRY.register('servo.detach', maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(Servo), +@automation.register_action('servo.detach', ServoDetachAction, maybe_simple_id({ + cv.Required(CONF_ID): cv.use_id(Servo), })) def servo_detach_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ServoDetachAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index cfe71c50f..2cb9ca817 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -13,7 +13,7 @@ extern uint32_t global_servo_id; class Servo : public Component { public: - Servo(output::FloatOutput *output) : output_(output) {} + void set_output(output::FloatOutput *output) { output_ = output; } void write(float value) { value = clamp(value, -1.0f, 1.0f); @@ -63,10 +63,7 @@ template class ServoWriteAction : public Action { public: ServoWriteAction(Servo *servo) : servo_(servo) {} TEMPLATABLE_VALUE(float, value) - void play(Ts... x) override { - this->servo_->write(this->value_.value(x...)); - this->play_next(x...); - } + void play(Ts... x) override { this->servo_->write(this->value_.value(x...)); } protected: Servo *servo_; @@ -75,10 +72,7 @@ template class ServoWriteAction : public Action { template class ServoDetachAction : public Action { public: ServoDetachAction(Servo *servo) : servo_(servo) {} - void play(Ts... x) override { - this->servo_->detach(); - this->play_next(x...); - } + void play(Ts... x) override { this->servo_->detach(); } protected: Servo *servo_; diff --git a/esphome/components/sht3xd/sensor.py b/esphome/components/sht3xd/sensor.py index e6ed633da..9bbdc47ee 100644 --- a/esphome/components/sht3xd/sensor.py +++ b/esphome/components/sht3xd/sensor.py @@ -1,9 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, \ - ICON_WATER_PERCENT, ICON_THERMOMETER, UNIT_CELSIUS, \ - UNIT_PERCENT +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, ICON_WATER_PERCENT, \ + ICON_THERMOMETER, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -11,17 +10,14 @@ sht3xd_ns = cg.esphome_ns.namespace('sht3xd') SHT3XDComponent = sht3xd_ns.class_('SHT3XDComponent', cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(SHT3XDComponent), - cv.Required(CONF_TEMPERATURE): - cv.nameable(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1)), - cv.Required(CONF_HUMIDITY): - cv.nameable(sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1)), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x44)) + cv.GenerateID(): cv.declare_id(SHT3XDComponent), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x44)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/sht3xd/sht3xd.h b/esphome/components/sht3xd/sht3xd.h index fa8e7589f..709f8aebe 100644 --- a/esphome/components/sht3xd/sht3xd.h +++ b/esphome/components/sht3xd/sht3xd.h @@ -10,8 +10,6 @@ namespace sht3xd { /// This class implements support for the SHT3x-DIS family of temperature+humidity i2c sensors. class SHT3XDComponent : public PollingComponent, public i2c::I2CDevice { public: - SHT3XDComponent(uint32_t update_interval) : PollingComponent(update_interval) {} - void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } diff --git a/esphome/components/shutdown/shutdown_switch.h b/esphome/components/shutdown/shutdown_switch.h index 18d6213ed..6aa64ff06 100644 --- a/esphome/components/shutdown/shutdown_switch.h +++ b/esphome/components/shutdown/shutdown_switch.h @@ -8,8 +8,6 @@ namespace shutdown { class ShutdownSwitch : public switch_::Switch, public Component { public: - explicit ShutdownSwitch(const std::string &name) : switch_::Switch(name) {} - void dump_config() override; protected: diff --git a/esphome/components/shutdown/switch.py b/esphome/components/shutdown/switch.py index 0d2b3b369..9826f9bbe 100644 --- a/esphome/components/shutdown/switch.py +++ b/esphome/components/shutdown/switch.py @@ -1,21 +1,21 @@ -from esphome.components import switch -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_INVERTED, CONF_NAME, CONF_ICON, ICON_POWER +import esphome.config_validation as cv +from esphome.components import switch +from esphome.const import CONF_ID, CONF_INVERTED, CONF_ICON, ICON_POWER shutdown_ns = cg.esphome_ns.namespace('shutdown') ShutdownSwitch = shutdown_ns.class_('ShutdownSwitch', switch.Switch, cg.Component) -CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(ShutdownSwitch), +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(ShutdownSwitch), cv.Optional(CONF_INVERTED): cv.invalid("Shutdown switches do not support inverted mode!"), cv.Optional(CONF_ICON, default=ICON_POWER): switch.icon -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield switch.register_switch(var, config) diff --git a/esphome/components/sntp/sntp_component.h b/esphome/components/sntp/sntp_component.h index bcd2b12a2..785f458d6 100644 --- a/esphome/components/sntp/sntp_component.h +++ b/esphome/components/sntp/sntp_component.h @@ -14,8 +14,6 @@ namespace sntp { /// \see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html class SNTPComponent : public time::RealTimeClock { public: - SNTPComponent() : time::RealTimeClock() {} - void setup() override; void dump_config() override; /// Change the servers used by SNTP for timekeeping diff --git a/esphome/components/sntp/time.py b/esphome/components/sntp/time.py index 8132ed18a..798600075 100644 --- a/esphome/components/sntp/time.py +++ b/esphome/components/sntp/time.py @@ -4,6 +4,7 @@ import esphome.codegen as cg from esphome.const import CONF_ID, CONF_SERVERS +DEPENDENCIES = ['network'] sntp_ns = cg.esphome_ns.namespace('sntp') SNTPComponent = sntp_ns.class_('SNTPComponent', time_.RealTimeClock) @@ -11,7 +12,7 @@ SNTPComponent = sntp_ns.class_('SNTPComponent', time_.RealTimeClock) DEFAULT_SERVERS = ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org"] CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(SNTPComponent), + cv.GenerateID(): cv.declare_id(SNTPComponent), cv.Optional(CONF_SERVERS, default=DEFAULT_SERVERS): cv.All(cv.ensure_list(cv.domain), cv.Length(min=1, max=3)), }).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/speed/fan/__init__.py b/esphome/components/speed/fan/__init__.py index e992bfdbe..ae6d09dfa 100644 --- a/esphome/components/speed/fan/__init__.py +++ b/esphome/components/speed/fan/__init__.py @@ -7,16 +7,16 @@ from .. import speed_ns SpeedFan = speed_ns.class_('SpeedFan', cg.Component) -CONFIG_SCHEMA = cv.nameable(fan.FAN_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_variable_id(SpeedFan), - cv.Required(CONF_OUTPUT): cv.use_variable_id(output.FloatOutput), - cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(output.BinaryOutput), +CONFIG_SCHEMA = fan.FAN_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpeedFan), + cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput), + cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), cv.Optional(CONF_SPEED, default={}): cv.Schema({ cv.Optional(CONF_LOW, default=0.33): cv.percentage, cv.Optional(CONF_MEDIUM, default=0.66): cv.percentage, cv.Optional(CONF_HIGH, default=1.0): cv.percentage, }), -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 1721014f6..1f0fc9127 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -11,7 +11,7 @@ SPIDevice = spi_ns.class_('SPIDevice') MULTI_CONF = True CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(SPIComponent), + cv.GenerateID(): cv.declare_id(SPIComponent), cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema, @@ -19,21 +19,23 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + clk = yield cg.gpio_pin_expression(config[CONF_CLK_PIN]) - miso = mosi = cg.nullptr + cg.add(var.set_clk(clk)) if CONF_MISO_PIN in config: miso = yield cg.gpio_pin_expression(config[CONF_MISO_PIN]) + cg.add(var.set_miso(miso)) if CONF_MOSI_PIN in config: mosi = yield cg.gpio_pin_expression(config[CONF_MOSI_PIN]) - - var = cg.new_Pvariable(config[CONF_ID], clk, miso, mosi) - yield cg.register_component(var, config) + cg.add(var.set_mosi(mosi)) cg.add_global(spi_ns.using) SPI_DEVICE_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), + cv.GenerateID(CONF_SPI_ID): cv.use_id(SPIComponent), cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, }) diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 9e053f0cd..600e1a0cd 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -8,7 +8,9 @@ namespace spi { class SPIComponent : public Component { public: - SPIComponent(GPIOPin *clk, GPIOPin *miso, GPIOPin *mosi) : clk_(clk), miso_(miso), mosi_(mosi) {} + void set_clk(GPIOPin *clk) { clk_ = clk; } + void set_miso(GPIOPin *miso) { miso_ = miso; } + void set_mosi(GPIOPin *mosi) { mosi_ = mosi; } void setup() override; @@ -30,8 +32,8 @@ class SPIComponent : public Component { protected: GPIOPin *clk_; - GPIOPin *miso_; - GPIOPin *mosi_; + GPIOPin *miso_{nullptr}; + GPIOPin *mosi_{nullptr}; GPIOPin *active_cs_{nullptr}; bool msb_first_{true}; bool high_speed_{false}; diff --git a/esphome/components/ssd1306_base/__init__.py b/esphome/components/ssd1306_base/__init__.py index 39cc95f46..0a678452b 100644 --- a/esphome/components/ssd1306_base/__init__.py +++ b/esphome/components/ssd1306_base/__init__.py @@ -2,9 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display -from esphome.components.spi import SPIComponent -from esphome.const import CONF_EXTERNAL_VCC, CONF_LAMBDA, \ - CONF_MODEL, CONF_RESET_PIN, CONF_SPI_ID, CONF_UPDATE_INTERVAL +from esphome.const import CONF_EXTERNAL_VCC, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN from esphome.core import coroutine ssd1306_base_ns = cg.esphome_ns.namespace('ssd1306_base') @@ -22,22 +20,21 @@ MODELS = { 'SH1106_64X48': SSD1306Model.SH1106_MODEL_64_48, } -SSD1306_MODEL = cv.one_of(*MODELS, upper=True, space="_") +SSD1306_MODEL = cv.enum(MODELS, upper=True, space="_") SSD1306_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), cv.Required(CONF_MODEL): SSD1306_MODEL, cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, - cv.Optional(CONF_UPDATE_INTERVAL, default='1s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA) +}).extend(cv.polling_component_schema('1s')) @coroutine def setup_ssd1036(var, config): yield cg.register_component(var, config) yield display.register_display(var, config) - cg.add(var.set_model(MODELS[config[CONF_MODEL]])) + + cg.add(var.set_model(config[CONF_MODEL])) if CONF_RESET_PIN in config: reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) cg.add(var.set_reset_pin(reset)) diff --git a/esphome/components/ssd1306_base/ssd1306_base.h b/esphome/components/ssd1306_base/ssd1306_base.h index bb76f740f..66c12ec93 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.h +++ b/esphome/components/ssd1306_base/ssd1306_base.h @@ -20,8 +20,6 @@ enum SSD1306Model { class SSD1306 : public PollingComponent, public display::DisplayBuffer { public: - SSD1306() : PollingComponent(0) {} - void setup() override; void display(); diff --git a/esphome/components/ssd1306_i2c/display.py b/esphome/components/ssd1306_i2c/display.py index c6674e495..eaa656f26 100644 --- a/esphome/components/ssd1306_i2c/display.py +++ b/esphome/components/ssd1306_i2c/display.py @@ -10,7 +10,7 @@ ssd1306_i2c = cg.esphome_ns.namespace('ssd1306_i2c') I2CSSD1306 = ssd1306_i2c.class_('I2CSSD1306', ssd1306_base.SSD1306, i2c.I2CDevice) CONFIG_SCHEMA = cv.All(ssd1306_base.SSD1306_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(I2CSSD1306), + cv.GenerateID(): cv.declare_id(I2CSSD1306), }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x3C)), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.h b/esphome/components/ssd1306_i2c/ssd1306_i2c.h index 69f0297fc..e3f21fe74 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.h +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.h @@ -9,8 +9,6 @@ namespace ssd1306_i2c { class I2CSSD1306 : public ssd1306_base::SSD1306, public i2c::I2CDevice { public: - I2CSSD1306() = default; - void setup() override; void dump_config() override; diff --git a/esphome/components/ssd1306_spi/display.py b/esphome/components/ssd1306_spi/display.py index 2dbfd4a27..65f0df1c5 100644 --- a/esphome/components/ssd1306_spi/display.py +++ b/esphome/components/ssd1306_spi/display.py @@ -11,15 +11,16 @@ ssd1306_spi = cg.esphome_ns.namespace('ssd1306_spi') SPISSD1306 = ssd1306_spi.class_('SPISSD1306', ssd1306_base.SSD1306, spi.SPIDevice) CONFIG_SCHEMA = cv.All(ssd1306_base.SSD1306_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(SPISSD1306), + cv.GenerateID(): cv.declare_id(SPISSD1306), cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, }).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) def to_code(config): - dc = yield cg.gpio_pin_expression(config[CONF_DC_PIN]) - var = cg.new_Pvariable(config[CONF_ID], dc) - + var = cg.new_Pvariable(config[CONF_ID]) yield ssd1306_base.setup_ssd1036(var, config) yield spi.register_spi_device(var, config) + + dc = yield cg.gpio_pin_expression(config[CONF_DC_PIN]) + cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/ssd1306_spi/ssd1306_spi.h b/esphome/components/ssd1306_spi/ssd1306_spi.h index 48e0036cb..5d0640bd8 100644 --- a/esphome/components/ssd1306_spi/ssd1306_spi.h +++ b/esphome/components/ssd1306_spi/ssd1306_spi.h @@ -9,7 +9,7 @@ namespace ssd1306_spi { class SPISSD1306 : public ssd1306_base::SSD1306, public spi::SPIDevice { public: - SPISSD1306(GPIOPin *dc_pin) : dc_pin_(dc_pin) {} + void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } void setup() override; diff --git a/esphome/components/status/binary_sensor.py b/esphome/components/status/binary_sensor.py index 9650d29c2..9963461ce 100644 --- a/esphome/components/status/binary_sensor.py +++ b/esphome/components/status/binary_sensor.py @@ -1,20 +1,20 @@ -from esphome.components import binary_sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_NAME, CONF_DEVICE_CLASS, DEVICE_CLASS_CONNECTIVITY +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_ID, CONF_DEVICE_CLASS, DEVICE_CLASS_CONNECTIVITY status_ns = cg.esphome_ns.namespace('status') StatusBinarySensor = status_ns.class_('StatusBinarySensor', binary_sensor.BinarySensor, cg.Component) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(StatusBinarySensor), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(StatusBinarySensor), cv.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_CONNECTIVITY): binary_sensor.device_class, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield binary_sensor.register_binary_sensor(var, config) diff --git a/esphome/components/status/status_binary_sensor.cpp b/esphome/components/status/status_binary_sensor.cpp index 2ea5844a6..5485c6ebc 100644 --- a/esphome/components/status/status_binary_sensor.cpp +++ b/esphome/components/status/status_binary_sensor.cpp @@ -14,7 +14,6 @@ namespace status { static const char *TAG = "status"; -StatusBinarySensor::StatusBinarySensor(const std::string &name) : BinarySensor(name) {} void StatusBinarySensor::loop() { bool status = network_is_connected(); #ifdef USE_MQTT diff --git a/esphome/components/status/status_binary_sensor.h b/esphome/components/status/status_binary_sensor.h index 1a0561799..fa121291a 100644 --- a/esphome/components/status/status_binary_sensor.h +++ b/esphome/components/status/status_binary_sensor.h @@ -8,9 +8,6 @@ namespace status { class StatusBinarySensor : public binary_sensor::BinarySensor, public Component { public: - /// Construct the status binary sensor - explicit StatusBinarySensor(const std::string &name); - void loop() override; void setup() override; diff --git a/esphome/components/status_led/__init__.py b/esphome/components/status_led/__init__.py index 4b45ac28d..26105b5e9 100644 --- a/esphome/components/status_led/__init__.py +++ b/esphome/components/status_led/__init__.py @@ -8,7 +8,7 @@ status_led_ns = cg.esphome_ns.namespace('status_led') StatusLED = status_led_ns.class_('StatusLED', cg.Component) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(StatusLED), + cv.GenerateID(): cv.declare_id(StatusLED), cv.Required(CONF_PIN): pins.gpio_output_pin_schema, }).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/stepper/__init__.py b/esphome/components/stepper/__init__.py index 2fbd3cc71..723ad5c1a 100644 --- a/esphome/components/stepper/__init__.py +++ b/esphome/components/stepper/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.automation import ACTION_REGISTRY +from esphome import automation from esphome.const import CONF_ACCELERATION, CONF_DECELERATION, CONF_ID, CONF_MAX_SPEED, \ CONF_POSITION, CONF_TARGET from esphome.core import CORE, coroutine @@ -11,8 +11,8 @@ IS_PLATFORM_COMPONENT = True stepper_ns = cg.esphome_ns.namespace('stepper') Stepper = stepper_ns.class_('Stepper') -SetTargetAction = stepper_ns.class_('SetTargetAction', cg.Action) -ReportPositionAction = stepper_ns.class_('ReportPositionAction', cg.Action) +SetTargetAction = stepper_ns.class_('SetTargetAction', automation.Action) +ReportPositionAction = stepper_ns.class_('ReportPositionAction', automation.Action) def validate_acceleration(value): @@ -79,32 +79,28 @@ def register_stepper(var, config): yield setup_stepper_core_(var, config) -@ACTION_REGISTRY.register('stepper.set_target', cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(Stepper), +@automation.register_action('stepper.set_target', SetTargetAction, cv.Schema({ + cv.Required(CONF_ID): cv.use_id(Stepper), cv.Required(CONF_TARGET): cv.templatable(cv.int_), })) def stepper_set_target_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = SetTargetAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_TARGET], args, cg.int32) - cg.add(action.set_target(template_)) - yield action + cg.add(var.set_target(template_)) + yield var -@ACTION_REGISTRY.register('stepper.report_position', cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(Stepper), +@automation.register_action('stepper.report_position', ReportPositionAction, cv.Schema({ + cv.Required(CONF_ID): cv.use_id(Stepper), cv.Required(CONF_POSITION): cv.templatable(cv.int_), })) def stepper_report_position_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ReportPositionAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_POSITION], args, cg.int32) - cg.add(action.set_position(template_)) - yield action + cg.add(var.set_position(template_)) + yield var def to_code(config): diff --git a/esphome/components/stepper/stepper.h b/esphome/components/stepper/stepper.h index 4583017ae..568cbf3d2 100644 --- a/esphome/components/stepper/stepper.h +++ b/esphome/components/stepper/stepper.h @@ -42,10 +42,7 @@ template class SetTargetAction : public Action { TEMPLATABLE_VALUE(int32_t, target) - void play(Ts... x) override { - this->parent_->set_target(this->target_.value(x...)); - this->play_next(x...); - } + void play(Ts... x) override { this->parent_->set_target(this->target_.value(x...)); } protected: Stepper *parent_; @@ -57,10 +54,7 @@ template class ReportPositionAction : public Action { TEMPLATABLE_VALUE(int32_t, position) - void play(Ts... x) override { - this->parent_->report_position(this->position_.value(x...)); - this->play_next(x...); - } + void play(Ts... x) override { this->parent_->report_position(this->position_.value(x...)); } protected: Stepper *parent_; diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index d63738a17..433a3066e 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -1,9 +1,8 @@ import logging import re -from esphome import core import esphome.config_validation as cv -from esphome.core import EsphomeError +from esphome import core from esphome.py_compat import string_types _LOGGER = logging.getLogger(__name__) @@ -105,30 +104,24 @@ def _substitute_item(substitutions, item, path): def do_substitution_pass(config): if CONF_SUBSTITUTIONS not in config: - return config + return substitutions = config[CONF_SUBSTITUTIONS] - if not isinstance(substitutions, dict): - raise EsphomeError(u"Substitutions must be a key to value mapping, got {}" - u"".format(type(substitutions))) + with cv.prepend_path('substitutions'): + if not isinstance(substitutions, dict): + raise cv.Invalid(u"Substitutions must be a key to value mapping, got {}" + u"".format(type(substitutions))) - key = '' - try: replace_keys = [] for key, value in substitutions.items(): - sub = validate_substitution_key(key) - if sub != key: - replace_keys.append((key, sub)) - substitutions[key] = cv.string_strict(value) + with cv.prepend_path(key): + sub = validate_substitution_key(key) + if sub != key: + replace_keys.append((key, sub)) + substitutions[key] = cv.string_strict(value) for old, new in replace_keys: substitutions[new] = substitutions[old] del substitutions[old] - except cv.Invalid as err: - err.path.append(key) - - raise EsphomeError(u"Error while parsing substitutions: {}".format(err)) config[CONF_SUBSTITUTIONS] = substitutions _substitute_item(substitutions, config, []) - - return config diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index f472474a8..a83845d14 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -1,10 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.automation import ACTION_REGISTRY, CONDITION_REGISTRY, Condition, maybe_simple_id +from esphome.automation import Condition, maybe_simple_id from esphome.components import mqtt from esphome.const import CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_INVERTED, CONF_ON_TURN_OFF, \ - CONF_ON_TURN_ON, CONF_TRIGGER_ID, CONF_MQTT_ID + CONF_ON_TURN_ON, CONF_TRIGGER_ID, CONF_MQTT_ID, CONF_NAME from esphome.core import CORE, coroutine IS_PLATFORM_COMPONENT = True @@ -13,32 +13,34 @@ switch_ns = cg.esphome_ns.namespace('switch_') Switch = switch_ns.class_('Switch', cg.Nameable) SwitchPtr = Switch.operator('ptr') -ToggleAction = switch_ns.class_('ToggleAction', cg.Action) -TurnOffAction = switch_ns.class_('TurnOffAction', cg.Action) -TurnOnAction = switch_ns.class_('TurnOnAction', cg.Action) -SwitchPublishAction = switch_ns.class_('SwitchPublishAction', cg.Action) +ToggleAction = switch_ns.class_('ToggleAction', automation.Action) +TurnOffAction = switch_ns.class_('TurnOffAction', automation.Action) +TurnOnAction = switch_ns.class_('TurnOnAction', automation.Action) +SwitchPublishAction = switch_ns.class_('SwitchPublishAction', automation.Action) SwitchCondition = switch_ns.class_('SwitchCondition', Condition) -SwitchTurnOnTrigger = switch_ns.class_('SwitchTurnOnTrigger', cg.Trigger.template()) -SwitchTurnOffTrigger = switch_ns.class_('SwitchTurnOffTrigger', cg.Trigger.template()) +SwitchTurnOnTrigger = switch_ns.class_('SwitchTurnOnTrigger', automation.Trigger.template()) +SwitchTurnOffTrigger = switch_ns.class_('SwitchTurnOffTrigger', automation.Trigger.template()) icon = cv.icon SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTSwitchComponent), + cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTSwitchComponent), cv.Optional(CONF_ICON): icon, cv.Optional(CONF_INVERTED): cv.boolean, cv.Optional(CONF_ON_TURN_ON): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SwitchTurnOnTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOnTrigger), }), cv.Optional(CONF_ON_TURN_OFF): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(SwitchTurnOffTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOffTrigger), }), }) +@coroutine def setup_switch_core_(var, config): + cg.add(var.set_name(config[CONF_NAME])) if CONF_INTERNAL in config: cg.add(var.set_internal(config[CONF_INTERNAL])) if CONF_ICON in config: @@ -66,49 +68,30 @@ def register_switch(var, config): SWITCH_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_variable_id(Switch), + cv.Required(CONF_ID): cv.use_id(Switch), }) -@ACTION_REGISTRY.register('switch.toggle', SWITCH_ACTION_SCHEMA) +@automation.register_action('switch.toggle', ToggleAction, SWITCH_ACTION_SCHEMA) +@automation.register_action('switch.turn_off', TurnOffAction, SWITCH_ACTION_SCHEMA) +@automation.register_action('switch.turn_on', TurnOnAction, SWITCH_ACTION_SCHEMA) def switch_toggle_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = ToggleAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) -@ACTION_REGISTRY.register('switch.turn_off', SWITCH_ACTION_SCHEMA) -def switch_turn_off_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = TurnOffAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) - - -@ACTION_REGISTRY.register('switch.turn_on', SWITCH_ACTION_SCHEMA) -def switch_turn_on_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = TurnOnAction.template(template_arg) - rhs = type.new(var) - yield cg.Pvariable(action_id, rhs, type=type) - - -@CONDITION_REGISTRY.register('switch.is_on', SWITCH_ACTION_SCHEMA) +@automation.register_condition('switch.is_on', SwitchCondition, SWITCH_ACTION_SCHEMA) def switch_is_on_to_code(config, condition_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = SwitchCondition.template(template_arg) - rhs = type.new(var, True) - yield cg.Pvariable(condition_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(condition_id, template_arg, paren) -@CONDITION_REGISTRY.register('switch.is_off', SWITCH_ACTION_SCHEMA) +@automation.register_condition('switch.is_off', SwitchCondition, SWITCH_ACTION_SCHEMA) def switch_is_off_to_code(config, condition_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = SwitchCondition.template(template_arg) - rhs = type.new(var, False) - yield cg.Pvariable(condition_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(condition_id, template_arg, paren) def to_code(config): + cg.add_global(switch_ns.using) cg.add_define('USE_SWITCH') diff --git a/esphome/components/switch/automation.h b/esphome/components/switch/automation.h index d6e27c816..90bdabf0f 100644 --- a/esphome/components/switch/automation.h +++ b/esphome/components/switch/automation.h @@ -11,10 +11,7 @@ template class TurnOnAction : public Action { public: explicit TurnOnAction(Switch *a_switch) : switch_(a_switch) {} - void play(Ts... x) override { - this->switch_->turn_on(); - this->play_next(x...); - } + void play(Ts... x) override { this->switch_->turn_on(); } protected: Switch *switch_; @@ -24,10 +21,7 @@ template class TurnOffAction : public Action { public: explicit TurnOffAction(Switch *a_switch) : switch_(a_switch) {} - void play(Ts... x) override { - this->switch_->turn_off(); - this->play_next(x...); - } + void play(Ts... x) override { this->switch_->turn_off(); } protected: Switch *switch_; @@ -37,10 +31,7 @@ template class ToggleAction : public Action { public: explicit ToggleAction(Switch *a_switch) : switch_(a_switch) {} - void play(Ts... x) override { - this->switch_->toggle(); - this->play_next(x...); - } + void play(Ts... x) override { this->switch_->toggle(); } protected: Switch *switch_; @@ -82,10 +73,7 @@ template class SwitchPublishAction : public Action { public: SwitchPublishAction(Switch *a_switch) : switch_(a_switch) {} TEMPLATABLE_VALUE(bool, state) - void play(Ts... x) override { - this->switch_->publish_state(this->state_.value(x...)); - this->play_next(x...); - } + void play(Ts... x) override { this->switch_->publish_state(this->state_.value(x...)); } protected: Switch *switch_; diff --git a/esphome/components/tcs34725/sensor.py b/esphome/components/tcs34725/sensor.py index 88344bf72..478fe4aba 100644 --- a/esphome/components/tcs34725/sensor.py +++ b/esphome/components/tcs34725/sensor.py @@ -3,7 +3,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_COLOR_TEMPERATURE, CONF_GAIN, CONF_ID, \ - CONF_ILLUMINANCE, CONF_INTEGRATION_TIME, CONF_UPDATE_INTERVAL, ICON_LIGHTBULB, \ + CONF_ILLUMINANCE, CONF_INTEGRATION_TIME, ICON_LIGHTBULB, \ UNIT_PERCENT, ICON_THERMOMETER, UNIT_KELVIN, ICON_BRIGHTNESS_5, UNIT_LUX DEPENDENCIES = ['i2c'] @@ -39,27 +39,26 @@ color_temperature_schema = sensor.sensor_schema(UNIT_KELVIN, ICON_THERMOMETER, 1 illuminance_schema = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(TCS34725Component), - cv.Optional(CONF_RED_CHANNEL): cv.nameable(color_channel_schema), - cv.Optional(CONF_GREEN_CHANNEL): cv.nameable(color_channel_schema), - cv.Optional(CONF_BLUE_CHANNEL): cv.nameable(color_channel_schema), - cv.Optional(CONF_CLEAR_CHANNEL): cv.nameable(color_channel_schema), - cv.Optional(CONF_ILLUMINANCE): cv.nameable(illuminance_schema), - cv.Optional(CONF_COLOR_TEMPERATURE): cv.nameable(color_temperature_schema), + cv.GenerateID(): cv.declare_id(TCS34725Component), + cv.Optional(CONF_RED_CHANNEL): color_channel_schema, + cv.Optional(CONF_GREEN_CHANNEL): color_channel_schema, + cv.Optional(CONF_BLUE_CHANNEL): color_channel_schema, + cv.Optional(CONF_CLEAR_CHANNEL): color_channel_schema, + cv.Optional(CONF_ILLUMINANCE): illuminance_schema, + cv.Optional(CONF_COLOR_TEMPERATURE): color_temperature_schema, cv.Optional(CONF_INTEGRATION_TIME, default='2.4ms'): - cv.one_of(*TCS34725_INTEGRATION_TIMES, lower=True), - cv.Optional(CONF_GAIN, default='1X'): cv.All(cv.Upper, cv.one_of(*TCS34725_GAINS), upper=True), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x29)) + cv.enum(TCS34725_INTEGRATION_TIMES, lower=True), + cv.Optional(CONF_GAIN, default='1X'): cv.enum(TCS34725_GAINS, upper=True), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x29)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) - cg.add(var.set_integration_time(TCS34725_INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]])) - cg.add(var.set_gain(TCS34725_GAINS[config[CONF_GAIN]])) + cg.add(var.set_integration_time(config[CONF_INTEGRATION_TIME])) + cg.add(var.set_gain(config[CONF_GAIN])) if CONF_RED_CHANNEL in config: sens = yield sensor.new_sensor(config[CONF_RED_CHANNEL]) diff --git a/esphome/components/tcs34725/tcs34725.h b/esphome/components/tcs34725/tcs34725.h index 2347cbfcf..b914db0eb 100644 --- a/esphome/components/tcs34725/tcs34725.h +++ b/esphome/components/tcs34725/tcs34725.h @@ -25,8 +25,6 @@ enum TCS34725Gain { class TCS34725Component : public PollingComponent, public i2c::I2CDevice { public: - TCS34725Component(uint32_t update_interval) : PollingComponent(update_interval) {} - void set_integration_time(TCS34725IntegrationTime integration_time); void set_gain(TCS34725Gain gain); diff --git a/esphome/components/template/binary_sensor/__init__.py b/esphome/components/template/binary_sensor/__init__.py index 7fa530f50..9c1fb549e 100644 --- a/esphome/components/template/binary_sensor/__init__.py +++ b/esphome/components/template/binary_sensor/__init__.py @@ -1,21 +1,21 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.automation import ACTION_REGISTRY +from esphome import automation from esphome.components import binary_sensor -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE +from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE from .. import template_ns TemplateBinarySensor = template_ns.class_('TemplateBinarySensor', binary_sensor.BinarySensor, cg.Component) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TemplateBinarySensor), +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(TemplateBinarySensor), cv.Optional(CONF_LAMBDA): cv.lambda_, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield binary_sensor.register_binary_sensor(var, config) @@ -25,15 +25,15 @@ def to_code(config): cg.add(var.set_template(template_)) -@ACTION_REGISTRY.register('binary_sensor.template.publish', cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(binary_sensor.BinarySensor), - cv.Required(CONF_STATE): cv.templatable(cv.boolean), -})) +@automation.register_action('binary_sensor.template.publish', + binary_sensor.BinarySensorPublishAction, + cv.Schema({ + cv.Required(CONF_ID): cv.use_id(binary_sensor.BinarySensor), + cv.Required(CONF_STATE): cv.templatable(cv.boolean), + })) def binary_sensor_template_publish_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = binary_sensor.BinarySensorPublishAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_STATE], args, bool) - cg.add(action.set_state(template_)) - yield action + cg.add(var.set_state(template_)) + yield var diff --git a/esphome/components/template/binary_sensor/template_binary_sensor.h b/esphome/components/template/binary_sensor/template_binary_sensor.h index 35364f6be..a28929b12 100644 --- a/esphome/components/template/binary_sensor/template_binary_sensor.h +++ b/esphome/components/template/binary_sensor/template_binary_sensor.h @@ -8,8 +8,6 @@ namespace template_ { class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor { public: - explicit TemplateBinarySensor(const std::string &name) : BinarySensor(name) {} - void set_template(std::function()> &&f) { this->f_ = f; } void loop() override; diff --git a/esphome/components/template/cover/__init__.py b/esphome/components/template/cover/__init__.py index 2b47e937f..0eccb9fc7 100644 --- a/esphome/components/template/cover/__init__.py +++ b/esphome/components/template/cover/__init__.py @@ -1,14 +1,12 @@ -from esphome import automation -from esphome.automation import ACTION_REGISTRY -from esphome.components import cover -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.components import cover from esphome.const import CONF_ASSUMED_STATE, CONF_CLOSE_ACTION, CONF_CURRENT_OPERATION, CONF_ID, \ - CONF_LAMBDA, CONF_NAME, CONF_OPEN_ACTION, CONF_OPTIMISTIC, CONF_POSITION, CONF_RESTORE_MODE, \ + CONF_LAMBDA, CONF_OPEN_ACTION, CONF_OPTIMISTIC, CONF_POSITION, CONF_RESTORE_MODE, \ CONF_STATE, CONF_STOP_ACTION from .. import template_ns - TemplateCover = template_ns.class_('TemplateCover', cover.Cover) TemplateCoverRestoreMode = template_ns.enum('TemplateCoverRestoreMode') @@ -18,20 +16,20 @@ RESTORE_MODES = { 'RESTORE_AND_CALL': TemplateCoverRestoreMode.COVER_RESTORE_AND_CALL, } -CONFIG_SCHEMA = cv.nameable(cover.COVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TemplateCover), +CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(TemplateCover), cv.Optional(CONF_LAMBDA): cv.lambda_, cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_RESTORE_MODE, default='RESTORE'): cv.one_of(*RESTORE_MODES, upper=True), -}).extend(cv.COMPONENT_SCHEMA)) + cv.Optional(CONF_RESTORE_MODE, default='RESTORE'): cv.enum(RESTORE_MODES, upper=True), +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield cover.register_cover(var, config) if CONF_LAMBDA in config: @@ -47,29 +45,25 @@ def to_code(config): cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE])) - cg.add(var.set_restore_mode(RESTORE_MODES[config[CONF_RESTORE_MODE]])) + cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) -@ACTION_REGISTRY.register('cover.template.publish', cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(cover.Cover), +@automation.register_action('cover.template.publish', cover.CoverPublishAction, cv.Schema({ + cv.Required(CONF_ID): cv.use_id(cover.Cover), cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(cover.validate_cover_state), cv.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.zero_to_one_float), cv.Optional(CONF_CURRENT_OPERATION): cv.templatable(cover.validate_cover_operation), })) def cover_template_publish_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = cover.CoverPublishAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) if CONF_STATE in config: - template_ = yield cg.templatable(config[CONF_STATE], args, float, to_exp=cover.COVER_STATES) - cg.add(action.set_position(template_)) + template_ = yield cg.templatable(config[CONF_STATE], args, float) + cg.add(var.set_position(template_)) if CONF_POSITION in config: - template_ = yield cg.templatable(config[CONF_POSITION], args, float, - to_exp=cover.COVER_STATES) - cg.add(action.set_position(template_)) + template_ = yield cg.templatable(config[CONF_POSITION], args, float) + cg.add(var.set_position(template_)) if CONF_CURRENT_OPERATION in config: - template_ = yield cg.templatable(config[CONF_CURRENT_OPERATION], args, - cover.CoverOperation, to_exp=cover.COVER_OPERATIONS) - cg.add(action.set_current_operation(template_)) - yield action + template_ = yield cg.templatable(config[CONF_CURRENT_OPERATION], args, cover.CoverOperation) + cg.add(var.set_current_operation(template_)) + yield var diff --git a/esphome/components/template/cover/template_cover.cpp b/esphome/components/template/cover/template_cover.cpp index 88e93c3e9..dd1a081aa 100644 --- a/esphome/components/template/cover/template_cover.cpp +++ b/esphome/components/template/cover/template_cover.cpp @@ -8,9 +8,8 @@ using namespace esphome::cover; static const char *TAG = "template.cover"; -TemplateCover::TemplateCover(const std::string &name) - : Cover(name), - open_trigger_(new Trigger<>()), +TemplateCover::TemplateCover() + : open_trigger_(new Trigger<>()), close_trigger_(new Trigger<>), stop_trigger_(new Trigger<>()), position_trigger_(new Trigger()), diff --git a/esphome/components/template/cover/template_cover.h b/esphome/components/template/cover/template_cover.h index a96a7c16d..3b9dcea50 100644 --- a/esphome/components/template/cover/template_cover.h +++ b/esphome/components/template/cover/template_cover.h @@ -15,7 +15,7 @@ enum TemplateCoverRestoreMode { class TemplateCover : public cover::Cover, public Component { public: - explicit TemplateCover(const std::string &name); + TemplateCover(); void set_state_lambda(std::function()> &&f); Trigger<> *get_open_trigger() const; diff --git a/esphome/components/template/sensor/__init__.py b/esphome/components/template/sensor/__init__.py index 52d64e7fd..1e9fe9cde 100644 --- a/esphome/components/template/sensor/__init__.py +++ b/esphome/components/template/sensor/__init__.py @@ -1,45 +1,37 @@ - -from esphome.automation import ACTION_REGISTRY -from esphome.components import sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE, CONF_UPDATE_INTERVAL +import esphome.config_validation as cv +from esphome import automation +from esphome.components import sensor +from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE, UNIT_EMPTY, ICON_EMPTY from .. import template_ns TemplateSensor = template_ns.class_('TemplateSensor', sensor.PollingSensorComponent) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TemplateSensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({ + cv.GenerateID(): cv.declare_id(TemplateSensor), cv.Optional(CONF_LAMBDA): cv.lambda_, - cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - rhs = TemplateSensor.new(config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) - template = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(template, config) - yield sensor.register_sensor(template, config) + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield sensor.register_sensor(var, config) if CONF_LAMBDA in config: template_ = yield cg.process_lambda(config[CONF_LAMBDA], [], return_type=cg.optional.template(float)) - cg.add(template.set_template(template_)) + cg.add(var.set_template(template_)) -CONF_SENSOR_TEMPLATE_PUBLISH = 'sensor.template.publish' -SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(sensor.Sensor), - cv.Required(CONF_STATE): cv.templatable(cv.float_), -}) - - -@ACTION_REGISTRY.register(CONF_SENSOR_TEMPLATE_PUBLISH, SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA) +@automation.register_action('sensor.template.publish', sensor.SensorPublishAction, + cv.Schema({ + cv.Required(CONF_ID): cv.use_id(sensor.Sensor), + cv.Required(CONF_STATE): cv.templatable(cv.float_), + })) def sensor_template_publish_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = sensor.SensorPublishAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_STATE], args, float) - cg.add(action.set_state(template_)) - yield action + cg.add(var.set_state(template_)) + yield var diff --git a/esphome/components/template/sensor/template_sensor.cpp b/esphome/components/template/sensor/template_sensor.cpp index e9f58527a..e2d36f13a 100644 --- a/esphome/components/template/sensor/template_sensor.cpp +++ b/esphome/components/template/sensor/template_sensor.cpp @@ -6,8 +6,6 @@ namespace template_ { static const char *TAG = "template.sensor"; -TemplateSensor::TemplateSensor(const std::string &name, uint32_t update_interval) - : PollingSensorComponent(name, update_interval) {} void TemplateSensor::update() { if (!this->f_.has_value()) return; diff --git a/esphome/components/template/sensor/template_sensor.h b/esphome/components/template/sensor/template_sensor.h index da1b3a9ab..2630cb0b1 100644 --- a/esphome/components/template/sensor/template_sensor.h +++ b/esphome/components/template/sensor/template_sensor.h @@ -6,10 +6,8 @@ namespace esphome { namespace template_ { -class TemplateSensor : public sensor::PollingSensorComponent { +class TemplateSensor : public sensor::Sensor, public PollingComponent { public: - TemplateSensor(const std::string &name, uint32_t update_interval); - void set_template(std::function()> &&f); void update() override; diff --git a/esphome/components/template/switch/__init__.py b/esphome/components/template/switch/__init__.py index 407af9892..ac27770d4 100644 --- a/esphome/components/template/switch/__init__.py +++ b/esphome/components/template/switch/__init__.py @@ -1,27 +1,26 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.automation import ACTION_REGISTRY from esphome.components import switch -from esphome.const import CONF_ASSUMED_STATE, CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_OPTIMISTIC, \ +from esphome.const import CONF_ASSUMED_STATE, CONF_ID, CONF_LAMBDA, CONF_OPTIMISTIC, \ CONF_RESTORE_STATE, CONF_STATE, CONF_TURN_OFF_ACTION, CONF_TURN_ON_ACTION from .. import template_ns TemplateSwitch = template_ns.class_('TemplateSwitch', switch.Switch, cg.Component) -CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TemplateSwitch), +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(TemplateSwitch), cv.Optional(CONF_LAMBDA): cv.lambda_, cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, cv.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_RESTORE_STATE, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield switch.register_switch(var, config) @@ -40,15 +39,13 @@ def to_code(config): cg.add(var.set_restore_state(config[CONF_RESTORE_STATE])) -@ACTION_REGISTRY.register('switch.template.publish', cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(switch.Switch), +@automation.register_action('switch.template.publish', switch.SwitchPublishAction, cv.Schema({ + cv.Required(CONF_ID): cv.use_id(switch.Switch), cv.Required(CONF_STATE): cv.templatable(cv.boolean), })) def switch_template_publish_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = switch.SwitchPublishAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_STATE], args, bool) - cg.add(action.set_state(template_)) - yield action + cg.add(var.set_state(template_)) + yield var diff --git a/esphome/components/template/switch/template_switch.cpp b/esphome/components/template/switch/template_switch.cpp index 2c86e156a..5868b3099 100644 --- a/esphome/components/template/switch/template_switch.cpp +++ b/esphome/components/template/switch/template_switch.cpp @@ -6,8 +6,8 @@ namespace template_ { static const char *TAG = "template.switch"; -TemplateSwitch::TemplateSwitch(const std::string &name) - : switch_::Switch(name), Component(), turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {} +TemplateSwitch::TemplateSwitch() : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {} + void TemplateSwitch::loop() { if (!this->f_.has_value()) return; diff --git a/esphome/components/template/switch/template_switch.h b/esphome/components/template/switch/template_switch.h index 558a28c4e..ef9b56745 100644 --- a/esphome/components/template/switch/template_switch.h +++ b/esphome/components/template/switch/template_switch.h @@ -9,7 +9,7 @@ namespace template_ { class TemplateSwitch : public switch_::Switch, public Component { public: - explicit TemplateSwitch(const std::string &name); + TemplateSwitch(); void setup() override; void dump_config() override; diff --git a/esphome/components/template/text_sensor/__init__.py b/esphome/components/template/text_sensor/__init__.py index 684c1b5bb..23b71a397 100644 --- a/esphome/components/template/text_sensor/__init__.py +++ b/esphome/components/template/text_sensor/__init__.py @@ -1,24 +1,22 @@ -from esphome.automation import ACTION_REGISTRY +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation from esphome.components import text_sensor from esphome.components.text_sensor import TextSensorPublishAction -import esphome.config_validation as cv -import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE, CONF_UPDATE_INTERVAL +from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE from .. import template_ns - TemplateTextSensor = template_ns.class_('TemplateTextSensor', text_sensor.TextSensor, cg.PollingComponent) -CONFIG_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TemplateTextSensor), +CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(TemplateTextSensor), cv.Optional(CONF_LAMBDA): cv.lambda_, - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield text_sensor.register_text_sensor(var, config) @@ -28,15 +26,13 @@ def to_code(config): cg.add(var.set_template(template_)) -@ACTION_REGISTRY.register('text_sensor.template.publish', cv.Schema({ - cv.Required(CONF_ID): cv.use_variable_id(text_sensor.TextSensor), +@automation.register_action('text_sensor.template.publish', TextSensorPublishAction, cv.Schema({ + cv.Required(CONF_ID): cv.use_id(text_sensor.TextSensor), cv.Required(CONF_STATE): cv.templatable(cv.string_strict), })) def text_sensor_template_publish_to_code(config, action_id, template_arg, args): - var = yield cg.get_variable(config[CONF_ID]) - type = TextSensorPublishAction.template(template_arg) - rhs = type.new(var) - action = cg.Pvariable(action_id, rhs, type=type) + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_STATE], args, cg.std_string) - cg.add(action.set_state(template_)) - yield action + cg.add(var.set_state(template_)) + yield var diff --git a/esphome/components/template/text_sensor/template_text_sensor.cpp b/esphome/components/template/text_sensor/template_text_sensor.cpp index 029de6c25..e06c70d95 100644 --- a/esphome/components/template/text_sensor/template_text_sensor.cpp +++ b/esphome/components/template/text_sensor/template_text_sensor.cpp @@ -6,8 +6,6 @@ namespace template_ { static const char *TAG = "template.text_sensor"; -TemplateTextSensor::TemplateTextSensor(const std::string &name, uint32_t update_interval) - : TextSensor(name), PollingComponent(update_interval) {} void TemplateTextSensor::update() { if (!this->f_.has_value()) return; diff --git a/esphome/components/template/text_sensor/template_text_sensor.h b/esphome/components/template/text_sensor/template_text_sensor.h index 12bf643f5..07a2bd96f 100644 --- a/esphome/components/template/text_sensor/template_text_sensor.h +++ b/esphome/components/template/text_sensor/template_text_sensor.h @@ -9,8 +9,6 @@ namespace template_ { class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent { public: - TemplateTextSensor(const std::string &name, uint32_t update_interval); - void set_template(std::function()> &&f); void update() override; diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index eeba18fbe..742ac20e0 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import automation from esphome.components import mqtt from esphome.const import CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_ON_VALUE, \ - CONF_TRIGGER_ID, CONF_MQTT_ID + CONF_TRIGGER_ID, CONF_MQTT_ID, CONF_NAME from esphome.core import CORE, coroutine IS_PLATFORM_COMPONENT = True @@ -14,22 +14,23 @@ TextSensor = text_sensor_ns.class_('TextSensor', cg.Nameable) TextSensorPtr = TextSensor.operator('ptr') TextSensorStateTrigger = text_sensor_ns.class_('TextSensorStateTrigger', - cg.Trigger.template(cg.std_string)) -TextSensorPublishAction = text_sensor_ns.class_('TextSensorPublishAction', cg.Action) + automation.Trigger.template(cg.std_string)) +TextSensorPublishAction = text_sensor_ns.class_('TextSensorPublishAction', automation.Action) icon = cv.icon TEXT_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTTextSensor), + cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTTextSensor), cv.Optional(CONF_ICON): icon, cv.Optional(CONF_ON_VALUE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(TextSensorStateTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TextSensorStateTrigger), }), }) @coroutine def setup_text_sensor_core_(var, config): + cg.add(var.set_name(config[CONF_NAME])) if CONF_INTERNAL in config: cg.add(var.set_internal(config[CONF_INTERNAL])) if CONF_ICON in config: diff --git a/esphome/components/text_sensor/automation.h b/esphome/components/text_sensor/automation.h index 7e9eed949..3e87aa2d1 100644 --- a/esphome/components/text_sensor/automation.h +++ b/esphome/components/text_sensor/automation.h @@ -18,10 +18,7 @@ template class TextSensorPublishAction : public Action { public: TextSensorPublishAction(TextSensor *sensor) : sensor_(sensor) {} TEMPLATABLE_VALUE(std::string, state) - void play(Ts... x) override { - this->sensor_->publish_state(this->state_.value(x...)); - this->play_next(x...); - } + void play(Ts... x) override { this->sensor_->publish_state(this->state_.value(x...)); } protected: TextSensor *sensor_; diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index 901cc2678..719f0b0d6 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -19,8 +19,8 @@ namespace text_sensor { class TextSensor : public Nameable { public: - explicit TextSensor(const std::string &name); explicit TextSensor(); + explicit TextSensor(const std::string &name); void publish_state(std::string state); diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index 87640fbc1..9fbfd95b6 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -21,7 +21,7 @@ IS_PLATFORM_COMPONENT = True time_ns = cg.esphome_ns.namespace('time') RealTimeClock = time_ns.class_('RealTimeClock', cg.Component) -CronTrigger = time_ns.class_('CronTrigger', cg.Trigger.template(), cg.Component) +CronTrigger = time_ns.class_('CronTrigger', automation.Trigger.template(), cg.Component) ESPTime = time_ns.struct('ESPTime') @@ -252,7 +252,7 @@ def validate_tz(value): TIME_SCHEMA = cv.Schema({ cv.Optional(CONF_TIMEZONE, default=detect_tz): validate_tz, cv.Optional(CONF_ON_TIME): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(CronTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CronTrigger), cv.Optional(CONF_SECONDS): validate_cron_seconds, cv.Optional(CONF_MINUTES): validate_cron_minutes, cv.Optional(CONF_HOURS): validate_cron_hours, diff --git a/esphome/components/time_based/cover.py b/esphome/components/time_based/cover.py index 2e72e9be3..85f606e6c 100644 --- a/esphome/components/time_based/cover.py +++ b/esphome/components/time_based/cover.py @@ -1,16 +1,15 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome import automation from esphome.components import cover -import esphome.config_validation as cv -import esphome.codegen as cg -from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, CONF_ID, CONF_NAME, \ - CONF_OPEN_ACTION, CONF_OPEN_DURATION, CONF_STOP_ACTION - +from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, CONF_ID, CONF_OPEN_ACTION, \ + CONF_OPEN_DURATION, CONF_STOP_ACTION time_based_ns = cg.esphome_ns.namespace('time_based') TimeBasedCover = time_based_ns.class_('TimeBasedCover', cover.Cover, cg.Component) -CONFIG_SCHEMA = cv.nameable(cover.COVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TimeBasedCover), +CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(TimeBasedCover), cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), @@ -18,11 +17,11 @@ CONFIG_SCHEMA = cv.nameable(cover.COVER_SCHEMA.extend({ cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield cover.register_cover(var, config) diff --git a/esphome/components/time_based/time_based_cover.h b/esphome/components/time_based/time_based_cover.h index d7fb0a011..60819d797 100644 --- a/esphome/components/time_based/time_based_cover.h +++ b/esphome/components/time_based/time_based_cover.h @@ -9,7 +9,6 @@ namespace time_based { class TimeBasedCover : public cover::Cover, public Component { public: - TimeBasedCover(const std::string &name) : cover::Cover(name) {} void setup() override; void loop() override; void dump_config() override; diff --git a/esphome/components/total_daily_energy/sensor.py b/esphome/components/total_daily_energy/sensor.py index 0e5291a9a..7fde9f558 100644 --- a/esphome/components/total_daily_energy/sensor.py +++ b/esphome/components/total_daily_energy/sensor.py @@ -1,7 +1,7 @@ -from esphome.components import sensor, time -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_NAME, CONF_TIME_ID +import esphome.config_validation as cv +from esphome.components import sensor, time +from esphome.const import CONF_ID, CONF_TIME_ID DEPENDENCIES = ['time'] @@ -9,17 +9,20 @@ CONF_POWER_ID = 'power_id' total_daily_energy_ns = cg.esphome_ns.namespace('total_daily_energy') TotalDailyEnergy = total_daily_energy_ns.class_('TotalDailyEnergy', sensor.Sensor, cg.Component) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TotalDailyEnergy), - cv.GenerateID(CONF_TIME_ID): cv.use_variable_id(time.RealTimeClock), - cv.Required(CONF_POWER_ID): cv.use_variable_id(sensor.Sensor), -}).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(TotalDailyEnergy), + cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), + cv.Required(CONF_POWER_ID): cv.use_id(sensor.Sensor), +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - time_ = yield cg.get_variable(config[CONF_TIME_ID]) - sens = yield cg.get_variable(config[CONF_POWER_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], time_, sens) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) + + sens = yield cg.get_variable(config[CONF_POWER_ID]) + cg.add(var.set_parent(sens)) + time_ = yield cg.get_variable(config[CONF_TIME_ID]) + cg.add(var.set_time(time_)) diff --git a/esphome/components/total_daily_energy/total_daily_energy.h b/esphome/components/total_daily_energy/total_daily_energy.h index 1e54c7972..ae44125ff 100644 --- a/esphome/components/total_daily_energy/total_daily_energy.h +++ b/esphome/components/total_daily_energy/total_daily_energy.h @@ -10,8 +10,8 @@ namespace total_daily_energy { class TotalDailyEnergy : public sensor::Sensor, public Component { public: - TotalDailyEnergy(const std::string &name, time::RealTimeClock *time, Sensor *parent) - : Sensor(name), time_(time), parent_(parent) {} + void set_time(time::RealTimeClock *time) { time_ = time; } + void set_parent(Sensor *parent) { parent_ = parent; } void setup() override; void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } diff --git a/esphome/components/tsl2561/sensor.py b/esphome/components/tsl2561/sensor.py index 6708b99f3..5171884a1 100644 --- a/esphome/components/tsl2561/sensor.py +++ b/esphome/components/tsl2561/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_GAIN, CONF_ID, CONF_INTEGRATION_TIME, CONF_UPDATE_INTERVAL, CONF_NAME +from esphome.const import CONF_GAIN, CONF_ID, CONF_INTEGRATION_TIME, UNIT_LUX, ICON_BRIGHTNESS_5 DEPENDENCIES = ['i2c'] @@ -24,26 +24,25 @@ CONF_IS_CS_PACKAGE = 'is_cs_package' def validate_integration_time(value): value = cv.positive_time_period_milliseconds(value).total_milliseconds - return cv.one_of(*INTEGRATION_TIMES, int=True)(value) + return cv.enum(INTEGRATION_TIMES, int=True)(value) TSL2561Sensor = tsl2561_ns.class_('TSL2561Sensor', sensor.PollingSensorComponent, i2c.I2CDevice) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TSL2561Sensor), +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ + cv.GenerateID(): cv.declare_id(TSL2561Sensor), cv.Optional(CONF_INTEGRATION_TIME, default=402): validate_integration_time, - cv.Optional(CONF_GAIN, default='1X'): cv.one_of(*GAINS, upper=True), + cv.Optional(CONF_GAIN, default='1X'): cv.enum(GAINS, upper=True), cv.Optional(CONF_IS_CS_PACKAGE, default=False): cv.boolean, - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x39))) +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) yield sensor.register_sensor(var, config) - cg.add(var.set_integration_time(INTEGRATION_TIMES[config[CONF_INTEGRATION_TIME]])) - cg.add(var.set_gain(GAINS[config[CONF_GAIN]])) + cg.add(var.set_integration_time(config[CONF_INTEGRATION_TIME])) + cg.add(var.set_gain(config[CONF_GAIN])) cg.add(var.set_is_cs_package(config[CONF_IS_CS_PACKAGE])) diff --git a/esphome/components/tsl2561/tsl2561.cpp b/esphome/components/tsl2561/tsl2561.cpp index 54047cff5..ffd39a54b 100644 --- a/esphome/components/tsl2561/tsl2561.cpp +++ b/esphome/components/tsl2561/tsl2561.cpp @@ -159,8 +159,6 @@ bool TSL2561Sensor::tsl2561_read_uint(uint8_t a_register, uint16_t *value) { bool TSL2561Sensor::tsl2561_read_byte(uint8_t a_register, uint8_t *value) { return this->read_byte(a_register | TSL2561_COMMAND_BIT, value); } -TSL2561Sensor::TSL2561Sensor(const std::string &name, uint32_t update_interval) - : PollingSensorComponent(name, update_interval) {} } // namespace tsl2561 } // namespace esphome diff --git a/esphome/components/tsl2561/tsl2561.h b/esphome/components/tsl2561/tsl2561.h index 8b4f05217..c54f41fb8 100644 --- a/esphome/components/tsl2561/tsl2561.h +++ b/esphome/components/tsl2561/tsl2561.h @@ -27,10 +27,8 @@ enum TSL2561Gain { }; /// This class includes support for the TSL2561 i2c ambient light sensor. -class TSL2561Sensor : public sensor::PollingSensorComponent, public i2c::I2CDevice { +class TSL2561Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { public: - TSL2561Sensor(const std::string &name, uint32_t update_interval); - /** Set the time that sensor values should be accumulated for. * * Longer means more accurate, but also mean more power consumption. diff --git a/esphome/components/ttp229_lsf/__init__.py b/esphome/components/ttp229_lsf/__init__.py index 849df358f..8b26102c6 100644 --- a/esphome/components/ttp229_lsf/__init__.py +++ b/esphome/components/ttp229_lsf/__init__.py @@ -7,12 +7,12 @@ DEPENDENCIES = ['i2c'] AUTO_LOAD = ['binary_sensor'] CONF_TTP229_ID = 'ttp229_id' -ttp229_ns = cg.esphome_ns.namespace('ttp229') +ttp229_lsf_ns = cg.esphome_ns.namespace('ttp229_lsf') -TTP229LSFComponent = ttp229_ns.class_('TTP229LSFComponent', cg.Component) +TTP229LSFComponent = ttp229_lsf_ns.class_('TTP229LSFComponent', cg.Component, i2c.I2CDevice) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(TTP229LSFComponent), + cv.GenerateID(): cv.declare_id(TTP229LSFComponent), }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x57)) diff --git a/esphome/components/ttp229_lsf/binary_sensor.py b/esphome/components/ttp229_lsf/binary_sensor.py index 793d2beda..da2f8d271 100644 --- a/esphome/components/ttp229_lsf/binary_sensor.py +++ b/esphome/components/ttp229_lsf/binary_sensor.py @@ -1,21 +1,23 @@ -from esphome.components import binary_sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_CHANNEL, CONF_NAME, CONF_ID -from . import ttp229_ns, TTP229LSFComponent, CONF_TTP229_ID +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_CHANNEL, CONF_ID +from . import ttp229_lsf_ns, TTP229LSFComponent, CONF_TTP229_ID DEPENDENCIES = ['ttp229_lsf'] -TTP229Channel = ttp229_ns.class_('TTP229Channel', binary_sensor.BinarySensor) +TTP229Channel = ttp229_lsf_ns.class_('TTP229Channel', binary_sensor.BinarySensor) -CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(TTP229Channel), - cv.GenerateID(CONF_TTP229_ID): cv.use_variable_id(TTP229LSFComponent), - cv.Required(CONF_CHANNEL): cv.All(cv.Coerce(int), cv.Range(min=0, max=15)) -})) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(TTP229Channel), + cv.GenerateID(CONF_TTP229_ID): cv.use_id(TTP229LSFComponent), + cv.Required(CONF_CHANNEL): cv.All(cv.int_, cv.Range(min=0, max=15)) +}) def to_code(config): - hub = yield cg.get_variable(config[CONF_TTP229_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_CHANNEL]) + var = cg.new_Pvariable(config[CONF_ID]) yield binary_sensor.register_binary_sensor(var, config) + + cg.add(var.set_channel(config[CONF_CHANNEL])) + hub = yield cg.get_variable(config[CONF_TTP229_ID]) cg.add(hub.register_channel(var)) diff --git a/esphome/components/ttp229_lsf/ttp229.cpp b/esphome/components/ttp229_lsf/ttp229_lsf.cpp similarity index 88% rename from esphome/components/ttp229_lsf/ttp229.cpp rename to esphome/components/ttp229_lsf/ttp229_lsf.cpp index 2051cec86..2bd5f8556 100644 --- a/esphome/components/ttp229_lsf/ttp229.cpp +++ b/esphome/components/ttp229_lsf/ttp229_lsf.cpp @@ -1,10 +1,10 @@ -#include "ttp229.h" +#include "ttp229_lsf.h" #include "esphome/core/log.h" namespace esphome { -namespace ttp229 { +namespace ttp229_lsf { -static const char *TAG = "ttp229"; +static const char *TAG = "ttp229_lsf"; void TTP229LSFComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up ttp229..."); @@ -39,5 +39,5 @@ void TTP229LSFComponent::loop() { } } -} // namespace ttp229 +} // namespace ttp229_lsf } // namespace esphome diff --git a/esphome/components/ttp229_lsf/ttp229.h b/esphome/components/ttp229_lsf/ttp229_lsf.h similarity index 84% rename from esphome/components/ttp229_lsf/ttp229.h rename to esphome/components/ttp229_lsf/ttp229_lsf.h index c212c5457..2064d9b65 100644 --- a/esphome/components/ttp229_lsf/ttp229.h +++ b/esphome/components/ttp229_lsf/ttp229_lsf.h @@ -5,15 +5,15 @@ #include "esphome/components/i2c/i2c.h" namespace esphome { -namespace ttp229 { +namespace ttp229_lsf { class TTP229Channel : public binary_sensor::BinarySensor { public: - TTP229Channel(const std::string &name, int channel) : BinarySensor(name), channel_(channel) {} + void set_channel(uint8_t channel) { channel_ = channel; } void process(uint16_t data) { this->publish_state(data & (1 << this->channel_)); } protected: - int channel_; + uint8_t channel_; }; class TTP229LSFComponent : public Component, public i2c::I2CDevice { @@ -32,5 +32,5 @@ class TTP229LSFComponent : public Component, public i2c::I2CDevice { } error_code_{NONE}; }; -} // namespace ttp229 +} // namespace ttp229_lsf } // namespace esphome diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index 9bffc9170..ef959b69e 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -1,6 +1,6 @@ -from esphome import pins -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN, CONF_UART_ID from esphome.core import CORE, coroutine @@ -18,29 +18,36 @@ def validate_rx_pin(value): CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(UARTComponent), + cv.GenerateID(): cv.declare_id(UARTComponent), + cv.Required(CONF_BAUD_RATE): cv.All(cv.int_, cv.Range(min=1, max=115200)), cv.Optional(CONF_TX_PIN): pins.output_pin, cv.Optional(CONF_RX_PIN): validate_rx_pin, - cv.Required(CONF_BAUD_RATE): cv.positive_int, }).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_BAUD_RATE]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + cg.add(var.set_baud_rate(config[CONF_BAUD_RATE])) + if CONF_TX_PIN in config: cg.add(var.set_tx_pin(config[CONF_TX_PIN])) if CONF_RX_PIN in config: cg.add(var.set_rx_pin(config[CONF_RX_PIN])) +# A schema to use for all UART devices, all UART integrations must extend this! UART_DEVICE_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent), + cv.GenerateID(CONF_UART_ID): cv.use_id(UARTComponent), }) @coroutine def register_uart_device(var, config): + """Register a UART device, setting up all the internal values. + + This is a coroutine, you need to await it with a 'yield' expression! + """ parent = yield cg.get_variable(config[CONF_UART_ID]) cg.add(var.set_uart_parent(parent)) diff --git a/esphome/components/uart/switch/__init__.py b/esphome/components/uart/switch/__init__.py index 3fc14229a..35e7877cc 100644 --- a/esphome/components/uart/switch/__init__.py +++ b/esphome/components/uart/switch/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch, uart -from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED, CONF_NAME +from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED from esphome.core import HexInt from esphome.py_compat import text_type, binary_type, char_to_byte from .. import uart_ns @@ -21,18 +21,20 @@ def validate_data(value): raise cv.Invalid("data must either be a string wrapped in quotes or a list of bytes") -CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(UARTSwitch), +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(UARTSwitch), cv.Required(CONF_DATA): validate_data, cv.Optional(CONF_INVERTED): cv.invalid("UART switches do not support inverted mode!"), -}).extend(uart.UART_DEVICE_SCHEMA)) +}).extend(uart.UART_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) def to_code(config): - data = config[CONF_DATA] - if isinstance(data, binary_type): - data = [HexInt(char_to_byte(x)) for x in data] - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], data) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield switch.register_switch(var, config) yield uart.register_uart_device(var, config) + + data = config[CONF_DATA] + if isinstance(data, binary_type): + data = [HexInt(char_to_byte(x)) for x in data] + cg.add(var.set_data(data)) diff --git a/esphome/components/uart/switch/uart_switch.cpp b/esphome/components/uart/switch/uart_switch.cpp index 73640f96d..9974ee117 100644 --- a/esphome/components/uart/switch/uart_switch.cpp +++ b/esphome/components/uart/switch/uart_switch.cpp @@ -6,9 +6,6 @@ namespace uart { static const char *TAG = "uart.switch"; -UARTSwitch::UARTSwitch(const std::string &name, const std::vector &data) - : switch_::Switch(name), data_(data) {} - void UARTSwitch::write_state(bool state) { if (!state) { this->publish_state(false); diff --git a/esphome/components/uart/switch/uart_switch.h b/esphome/components/uart/switch/uart_switch.h index e628b4920..c8a1b0d8c 100644 --- a/esphome/components/uart/switch/uart_switch.h +++ b/esphome/components/uart/switch/uart_switch.h @@ -9,7 +9,7 @@ namespace uart { class UARTSwitch : public switch_::Switch, public UARTDevice, public Component { public: - UARTSwitch(const std::string &name, const std::vector &data); + void set_data(const std::vector &data) { data_ = data; } void dump_config() override; diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index 9bfecc343..005536de4 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -40,7 +40,7 @@ class ESP8266SoftwareSerial { class UARTComponent : public Component, public Stream { public: - UARTComponent(uint32_t baud_rate) : baud_rate_(baud_rate) {} + void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } void setup() override; diff --git a/esphome/components/uln2003/stepper.py b/esphome/components/uln2003/stepper.py index 82a57941d..278fcf67e 100644 --- a/esphome/components/uln2003/stepper.py +++ b/esphome/components/uln2003/stepper.py @@ -1,11 +1,10 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome import pins from esphome.components import stepper -import esphome.config_validation as cv -import esphome.codegen as cg from esphome.const import CONF_ID, CONF_PIN_A, CONF_PIN_B, CONF_PIN_C, CONF_PIN_D, \ CONF_SLEEP_WHEN_DONE, CONF_STEP_MODE - uln2003_ns = cg.esphome_ns.namespace('uln2003') ULN2003StepMode = uln2003_ns.enum('ULN2003StepMode') @@ -18,24 +17,29 @@ STEP_MODES = { ULN2003 = uln2003_ns.class_('ULN2003', stepper.Stepper, cg.Component) CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_variable_id(ULN2003), + cv.Required(CONF_ID): cv.declare_id(ULN2003), cv.Required(CONF_PIN_A): pins.gpio_output_pin_schema, cv.Required(CONF_PIN_B): pins.gpio_output_pin_schema, cv.Required(CONF_PIN_C): pins.gpio_output_pin_schema, cv.Required(CONF_PIN_D): pins.gpio_output_pin_schema, cv.Optional(CONF_SLEEP_WHEN_DONE, default=False): cv.boolean, - cv.Optional(CONF_STEP_MODE, default='FULL_STEP'): cv.one_of(*STEP_MODES, upper=True, space='_') + cv.Optional(CONF_STEP_MODE, default='FULL_STEP'): cv.enum(STEP_MODES, upper=True, space='_') }).extend(cv.COMPONENT_SCHEMA) def to_code(config): - pin_a = yield cg.gpio_pin_expression(config[CONF_PIN_A]) - pin_b = yield cg.gpio_pin_expression(config[CONF_PIN_B]) - pin_c = yield cg.gpio_pin_expression(config[CONF_PIN_C]) - pin_d = yield cg.gpio_pin_expression(config[CONF_PIN_D]) - var = cg.new_Pvariable(config[CONF_ID], pin_a, pin_b, pin_c, pin_d) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield stepper.register_stepper(var, config) + pin_a = yield cg.gpio_pin_expression(config[CONF_PIN_A]) + cg.add(var.set_pin_a(pin_a)) + pin_b = yield cg.gpio_pin_expression(config[CONF_PIN_B]) + cg.add(var.set_pin_b(pin_b)) + pin_c = yield cg.gpio_pin_expression(config[CONF_PIN_C]) + cg.add(var.set_pin_c(pin_c)) + pin_d = yield cg.gpio_pin_expression(config[CONF_PIN_D]) + cg.add(var.set_pin_d(pin_d)) + cg.add(var.set_sleep_when_done(config[CONF_SLEEP_WHEN_DONE])) - cg.add(var.set_step_mode(STEP_MODES[config[CONF_STEP_MODE]])) + cg.add(var.set_step_mode(config[CONF_STEP_MODE])) diff --git a/esphome/components/uln2003/uln2003.h b/esphome/components/uln2003/uln2003.h index cdd8dd5d8..4bcf1e88e 100644 --- a/esphome/components/uln2003/uln2003.h +++ b/esphome/components/uln2003/uln2003.h @@ -15,8 +15,10 @@ enum ULN2003StepMode { class ULN2003 : public stepper::Stepper, public Component { public: - ULN2003(GPIOPin *pin_a, GPIOPin *pin_b, GPIOPin *pin_c, GPIOPin *pin_d) - : pin_a_(pin_a), pin_b_(pin_b), pin_c_(pin_c), pin_d_(pin_d) {} + void set_pin_a(GPIOPin *pin_a) { pin_a_ = pin_a; } + void set_pin_b(GPIOPin *pin_b) { pin_b_ = pin_b; } + void set_pin_c(GPIOPin *pin_c) { pin_c_ = pin_c; } + void set_pin_d(GPIOPin *pin_d) { pin_d_ = pin_d; } void setup() override; void loop() override; diff --git a/esphome/components/ultrasonic/sensor.py b/esphome/components/ultrasonic/sensor.py index b02b9f3f8..fa3934853 100644 --- a/esphome/components/ultrasonic/sensor.py +++ b/esphome/components/ultrasonic/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ECHO_PIN, CONF_ID, CONF_NAME, CONF_TRIGGER_PIN, \ - CONF_UPDATE_INTERVAL, CONF_TIMEOUT, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL +from esphome.const import CONF_ECHO_PIN, CONF_ID, CONF_TRIGGER_PIN, \ + CONF_TIMEOUT, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL CONF_PULSE_TIME = 'pulse_time' @@ -11,30 +11,30 @@ ultrasonic_ns = cg.esphome_ns.namespace('ultrasonic') UltrasonicSensorComponent = ultrasonic_ns.class_('UltrasonicSensorComponent', sensor.PollingSensorComponent) -CONFIG_SCHEMA = cv.nameable( - sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ - cv.GenerateID(): cv.declare_variable_id(UltrasonicSensorComponent), - cv.Required(CONF_TRIGGER_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema, +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ + cv.GenerateID(): cv.declare_id(UltrasonicSensorComponent), + cv.Required(CONF_TRIGGER_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema, - cv.Optional(CONF_TIMEOUT, default='2m'): cv.distance, - cv.Optional(CONF_PULSE_TIME, default='10us'): cv.positive_time_period_microseconds, - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, + cv.Optional(CONF_TIMEOUT, default='2m'): cv.distance, + cv.Optional(CONF_PULSE_TIME, default='10us'): cv.positive_time_period_microseconds, - cv.Optional('timeout_meter'): cv.invalid("The timeout_meter option has been renamed " - "to 'timeout' in 1.12."), - cv.Optional('timeout_time'): cv.invalid("The timeout_time option has been removed. Please " - "use 'timeout' in 1.12."), - })) + cv.Optional('timeout_meter'): cv.invalid("The timeout_meter option has been renamed " + "to 'timeout' in 1.12."), + cv.Optional('timeout_time'): cv.invalid("The timeout_time option has been removed. Please " + "use 'timeout' in 1.12."), +}).extend(cv.polling_component_schema('60s')) def to_code(config): - trigger = yield cg.gpio_pin_expression(config[CONF_TRIGGER_PIN]) - echo = yield cg.gpio_pin_expression(config[CONF_ECHO_PIN]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], trigger, echo, - config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) + trigger = yield cg.gpio_pin_expression(config[CONF_TRIGGER_PIN]) + cg.add(var.set_trigger_pin(trigger)) + echo = yield cg.gpio_pin_expression(config[CONF_ECHO_PIN]) + cg.add(var.set_echo_pin(echo)) + cg.add(var.set_timeout_us(config[CONF_TIMEOUT] / (0.000343 / 2))) cg.add(var.set_pulse_time_us(config[CONF_PULSE_TIME])) diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.cpp b/esphome/components/ultrasonic/ultrasonic_sensor.cpp index 6ff575971..5d4cd48bc 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.cpp +++ b/esphome/components/ultrasonic/ultrasonic_sensor.cpp @@ -6,9 +6,6 @@ namespace ultrasonic { static const char *TAG = "ultrasonic.sensor"; -UltrasonicSensorComponent::UltrasonicSensorComponent(const std::string &name, GPIOPin *trigger_pin, GPIOPin *echo_pin, - uint32_t update_interval) - : PollingSensorComponent(name, update_interval), trigger_pin_(trigger_pin), echo_pin_(echo_pin) {} void UltrasonicSensorComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Ultrasonic Sensor..."); this->trigger_pin_->setup(); diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.h b/esphome/components/ultrasonic/ultrasonic_sensor.h index 749768c64..633c1b17f 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.h +++ b/esphome/components/ultrasonic/ultrasonic_sensor.h @@ -7,15 +7,10 @@ namespace esphome { namespace ultrasonic { -class UltrasonicSensorComponent : public sensor::PollingSensorComponent { +class UltrasonicSensorComponent : public sensor::Sensor, public PollingComponent { public: - /** Construct the ultrasonic sensor with the specified trigger pin and echo pin. - * - * @param trigger_pin The trigger pin where pulses are sent to. - * @param echo_pin The echo pin where the echo is listened for. - * @param update_interval The interval in ms the sensor should check for new values. - */ - UltrasonicSensorComponent(const std::string &name, GPIOPin *trigger_pin, GPIOPin *echo_pin, uint32_t update_interval); + void set_trigger_pin(GPIOPin *trigger_pin) { trigger_pin_ = trigger_pin; } + void set_echo_pin(GPIOPin *echo_pin) { echo_pin_ = echo_pin; } /// Set the timeout for waiting for the echo in µs. void set_timeout_us(uint32_t timeout_us); diff --git a/esphome/components/uptime/sensor.py b/esphome/components/uptime/sensor.py index 33c5305cf..e4f78c641 100644 --- a/esphome/components/uptime/sensor.py +++ b/esphome/components/uptime/sensor.py @@ -1,24 +1,17 @@ -from esphome.components import sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_UNIT_OF_MEASUREMENT, \ - UNIT_SECOND, ICON_TIMER, CONF_ICON, CONF_ACCURACY_DECIMALS +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.const import CONF_ID, UNIT_SECOND, ICON_TIMER uptime_ns = cg.esphome_ns.namespace('uptime') UptimeSensor = uptime_ns.class_('UptimeSensor', sensor.PollingSensorComponent) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(UptimeSensor), - - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_SECOND): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_TIMER): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals, -}).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 0).extend({ + cv.GenerateID(): cv.declare_id(UptimeSensor), +}).extend(cv.polling_component_schema('60s')) def to_code(config): - rhs = UptimeSensor.new(config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) - uptime = cg.Pvariable(config[CONF_ID], rhs) - yield cg.register_component(uptime, config) - yield sensor.register_sensor(uptime, config) + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield sensor.register_sensor(var, config) diff --git a/esphome/components/uptime/uptime_sensor.cpp b/esphome/components/uptime/uptime_sensor.cpp index 0ae794f2b..a66ca0636 100644 --- a/esphome/components/uptime/uptime_sensor.cpp +++ b/esphome/components/uptime/uptime_sensor.cpp @@ -7,8 +7,6 @@ namespace uptime { static const char *TAG = "uptime.sensor"; -UptimeSensor::UptimeSensor(const std::string &name, uint32_t update_interval) - : sensor::PollingSensorComponent(name, update_interval) {} void UptimeSensor::update() { const uint32_t ms = millis(); const uint64_t ms_mask = (1ULL << 32) - 1ULL; diff --git a/esphome/components/uptime/uptime_sensor.h b/esphome/components/uptime/uptime_sensor.h index 68e030af8..184022503 100644 --- a/esphome/components/uptime/uptime_sensor.h +++ b/esphome/components/uptime/uptime_sensor.h @@ -6,10 +6,8 @@ namespace esphome { namespace uptime { -class UptimeSensor : public sensor::PollingSensorComponent { +class UptimeSensor : public sensor::Sensor, public PollingComponent { public: - explicit UptimeSensor(const std::string &name, uint32_t update_interval); - void update() override; float get_setup_priority() const override; diff --git a/esphome/components/version/text_sensor.py b/esphome/components/version/text_sensor.py index 482058132..21044bb89 100644 --- a/esphome/components/version/text_sensor.py +++ b/esphome/components/version/text_sensor.py @@ -1,18 +1,18 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import text_sensor -from esphome.const import CONF_ID, CONF_NAME, CONF_ICON, ICON_NEW_BOX +from esphome.const import CONF_ID, CONF_ICON, ICON_NEW_BOX version_ns = cg.esphome_ns.namespace('version') VersionTextSensor = version_ns.class_('VersionTextSensor', text_sensor.TextSensor, cg.Component) -CONFIG_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(VersionTextSensor), +CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(VersionTextSensor), cv.Optional(CONF_ICON, default=ICON_NEW_BOX): text_sensor.icon -}).extend(cv.COMPONENT_SCHEMA)) +}).extend(cv.COMPONENT_SCHEMA) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) + var = cg.new_Pvariable(config[CONF_ID]) yield text_sensor.register_text_sensor(var, config) yield cg.register_component(var, config) diff --git a/esphome/components/version/version_text_sensor.cpp b/esphome/components/version/version_text_sensor.cpp index 140778eb5..1e59deff9 100644 --- a/esphome/components/version/version_text_sensor.cpp +++ b/esphome/components/version/version_text_sensor.cpp @@ -9,7 +9,6 @@ static const char *TAG = "version.text_sensor"; void VersionTextSensor::setup() { this->publish_state(ESPHOME_VERSION " " + App.get_compilation_time()); } float VersionTextSensor::get_setup_priority() const { return setup_priority::DATA; } -VersionTextSensor::VersionTextSensor(const std::string &name) : text_sensor::TextSensor(name) {} std::string VersionTextSensor::unique_id() { return get_mac_address() + "-version"; } void VersionTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Version Text Sensor", this); } diff --git a/esphome/components/version/version_text_sensor.h b/esphome/components/version/version_text_sensor.h index 87e8412be..cc798939e 100644 --- a/esphome/components/version/version_text_sensor.h +++ b/esphome/components/version/version_text_sensor.h @@ -8,7 +8,6 @@ namespace version { class VersionTextSensor : public text_sensor::TextSensor, public Component { public: - explicit VersionTextSensor(const std::string &name); void setup() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index a980b2bac..77a0bbc68 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import display, spi from esphome.const import CONF_BUSY_PIN, CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, \ - CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_PAGES, CONF_RESET_PIN, CONF_UPDATE_INTERVAL + CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_PAGES, CONF_RESET_PIN DEPENDENCIES = ['spi'] @@ -38,27 +38,24 @@ def validate_full_update_every_only_type_a(value): CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(WaveshareEPaper), + cv.GenerateID(): cv.declare_id(WaveshareEPaper), cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True), cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema, cv.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t, - cv.Optional(CONF_UPDATE_INTERVAL, default='1s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA), +}).extend(cv.polling_component_schema('1s')).extend(spi.SPI_DEVICE_SCHEMA), validate_full_update_every_only_type_a, cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) def to_code(config): - dc = yield cg.gpio_pin_expression(config[CONF_DC_PIN]) - model_type, model = MODELS[config[CONF_MODEL]] if model_type == 'a': - rhs = WaveshareEPaperTypeA.new(dc, model) + rhs = WaveshareEPaperTypeA.new(model) var = cg.Pvariable(config[CONF_ID], rhs, type=WaveshareEPaperTypeA) elif model_type == 'b': - rhs = model.new(dc) + rhs = model.new() var = cg.Pvariable(config[CONF_ID], rhs, type=model) else: raise NotImplementedError() @@ -67,6 +64,9 @@ def to_code(config): yield display.register_display(var, config) yield spi.register_spi_device(var, config) + dc = yield cg.gpio_pin_expression(config[CONF_DC_PIN]) + cg.add(var.set_dc_pin(dc)) + if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 860a71b04..392134f52 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -110,7 +110,6 @@ void HOT WaveshareEPaper::draw_absolute_pixel_internal(int x, int y, int color) this->buffer_[pos] &= ~(0x80 >> subpos); } uint32_t WaveshareEPaper::get_buffer_length_() { return this->get_width_internal() * this->get_height_internal() / 8u; } -WaveshareEPaper::WaveshareEPaper(GPIOPin *dc_pin) : PollingComponent(0), dc_pin_(dc_pin) {} bool WaveshareEPaper::is_device_high_speed() { return true; } void WaveshareEPaper::start_command_() { this->dc_pin_->digital_write(false); @@ -246,8 +245,7 @@ void WaveshareEPaperTypeA::write_lut_(const uint8_t *lut) { for (uint8_t i = 0; i < 30; i++) this->data(lut[i]); } -WaveshareEPaperTypeA::WaveshareEPaperTypeA(GPIOPin *dc_pin, WaveshareEPaperTypeAModel model) - : WaveshareEPaper(dc_pin), model_(model) {} +WaveshareEPaperTypeA::WaveshareEPaperTypeA(WaveshareEPaperTypeAModel model) : model_(model) {} void WaveshareEPaperTypeA::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } @@ -414,7 +412,6 @@ void HOT WaveshareEPaper2P7In::display() { } int WaveshareEPaper2P7In::get_width_internal() { return 176; } int WaveshareEPaper2P7In::get_height_internal() { return 264; } -WaveshareEPaper2P7In::WaveshareEPaper2P7In(GPIOPin *dc_pin) : WaveshareEPaper(dc_pin) {} void WaveshareEPaper2P7In::dump_config() { LOG_DISPLAY("", "Waveshare E-Paper", this); ESP_LOGCONFIG(TAG, " Model: 2.7in"); @@ -523,7 +520,6 @@ void HOT WaveshareEPaper4P2In::display() { int WaveshareEPaper4P2In::get_width_internal() { return 400; } int WaveshareEPaper4P2In::get_height_internal() { return 300; } bool WaveshareEPaper4P2In::is_device_high_speed() { return false; } -WaveshareEPaper4P2In::WaveshareEPaper4P2In(GPIOPin *dc_pin) : WaveshareEPaper(dc_pin) {} void WaveshareEPaper4P2In::dump_config() { LOG_DISPLAY("", "Waveshare E-Paper", this); ESP_LOGCONFIG(TAG, " Model: 4.2in"); @@ -609,7 +605,6 @@ void HOT WaveshareEPaper7P5In::display() { } int WaveshareEPaper7P5In::get_width_internal() { return 640; } int WaveshareEPaper7P5In::get_height_internal() { return 384; } -WaveshareEPaper7P5In::WaveshareEPaper7P5In(GPIOPin *dc_pin) : WaveshareEPaper(dc_pin) {} void WaveshareEPaper7P5In::dump_config() { LOG_DISPLAY("", "Waveshare E-Paper", this); ESP_LOGCONFIG(TAG, " Model: 7.5in"); diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 31e6eb819..49bb21bc7 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -9,7 +9,7 @@ namespace waveshare_epaper { class WaveshareEPaper : public PollingComponent, public spi::SPIDevice, public display::DisplayBuffer { public: - WaveshareEPaper(GPIOPin *dc_pin); + void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } float get_setup_priority() const override; void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } void set_busy_pin(GPIOPin *busy) { this->busy_pin_ = busy; } @@ -53,7 +53,7 @@ enum WaveshareEPaperTypeAModel { class WaveshareEPaperTypeA : public WaveshareEPaper { public: - WaveshareEPaperTypeA(GPIOPin *dc_pin, WaveshareEPaperTypeAModel model); + WaveshareEPaperTypeA(WaveshareEPaperTypeAModel model); void setup() override; @@ -83,7 +83,6 @@ enum WaveshareEPaperTypeBModel { class WaveshareEPaper2P7In : public WaveshareEPaper { public: - WaveshareEPaper2P7In(GPIOPin *dc_pin); void setup() override; void display() override; @@ -98,7 +97,6 @@ class WaveshareEPaper2P7In : public WaveshareEPaper { class WaveshareEPaper4P2In : public WaveshareEPaper { public: - WaveshareEPaper4P2In(GPIOPin *dc_pin); void setup() override; void display() override; @@ -115,7 +113,6 @@ class WaveshareEPaper4P2In : public WaveshareEPaper { class WaveshareEPaper7P5In : public WaveshareEPaper { public: - WaveshareEPaper7P5In(GPIOPin *dc_pin); void setup() override; void display() override; diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 615c27612..206fc2c73 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -3,13 +3,14 @@ import esphome.config_validation as cv from esphome.const import CONF_CSS_URL, CONF_ID, CONF_JS_URL, CONF_PORT from esphome.core import CORE, coroutine_with_priority +DEPENDENCIES = ['network'] AUTO_LOAD = ['json'] web_server_ns = cg.esphome_ns.namespace('web_server') WebServer = web_server_ns.class_('WebServer', cg.Component, cg.Controller) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(WebServer), + cv.GenerateID(): cv.declare_id(WebServer), cv.Optional(CONF_PORT, default=80): cv.port, cv.Optional(CONF_CSS_URL, default="https://esphome.io/_static/webserver-v1.min.css"): cv.string, cv.Optional(CONF_JS_URL, default="https://esphome.io/_static/webserver-v1.min.js"): cv.string, @@ -18,10 +19,12 @@ CONFIG_SCHEMA = cv.Schema({ @coroutine_with_priority(40.0) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_PORT]) + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + + cg.add(var.set_port(config[CONF_PORT])) cg.add(var.set_css_url(config[CONF_CSS_URL])) cg.add(var.set_js_url(config[CONF_JS_URL])) - yield cg.register_component(var, config) if CORE.is_esp32: cg.add_library('FS', None) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index c43511e51..882af4b99 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -61,8 +61,6 @@ UrlMatch match_url(const std::string &url, bool only_domain = false) { return match; } -WebServer::WebServer(uint16_t port) : port_(port) {} - void WebServer::set_css_url(const char *css_url) { this->css_url_ = css_url; } void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; } diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 72db3e36c..7840a81dc 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -28,8 +28,7 @@ struct UrlMatch { */ class WebServer : public Controller, public Component, public AsyncWebHandler { public: - /// Initialize the web server with the specified port - explicit WebServer(uint16_t port); + void set_port(uint16_t port) { port_ = port; } /** Set the URL to the CSS that's sent to each client. Defaults to * https://esphome.io/_static/webserver-v1.min.css diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 8be00a6c2..9ac8b74ef 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -1,12 +1,15 @@ -from esphome.automation import CONDITION_REGISTRY, Condition -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.automation import Condition from esphome.const import CONF_AP, CONF_BSSID, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_DOMAIN, \ CONF_FAST_CONNECT, CONF_GATEWAY, CONF_HIDDEN, CONF_ID, CONF_MANUAL_IP, CONF_NETWORKS, \ CONF_PASSWORD, CONF_POWER_SAVE_MODE, CONF_REBOOT_TIMEOUT, CONF_SSID, CONF_STATIC_IP, \ CONF_SUBNET, CONF_USE_ADDRESS from esphome.core import CORE, HexInt, coroutine_with_priority +AUTO_LOAD = ['network'] + wifi_ns = cg.esphome_ns.namespace('wifi') IPAddress = cg.global_ns.class_('IPAddress') ManualIP = wifi_ns.struct('ManualIP') @@ -54,7 +57,7 @@ STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({ }) WIFI_NETWORK_BASE = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(WiFiAP), + cv.GenerateID(): cv.declare_id(WiFiAP), cv.Optional(CONF_SSID): cv.ssid, cv.Optional(CONF_PASSWORD): validate_password, cv.Optional(CONF_CHANNEL): validate_channel, @@ -106,7 +109,7 @@ def validate(config): CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(WiFiComponent), + cv.GenerateID(): cv.declare_id(WiFiComponent), cv.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA), cv.Optional(CONF_SSID): cv.ssid, @@ -116,8 +119,7 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ cv.Optional(CONF_AP): WIFI_NETWORK_AP, cv.Optional(CONF_DOMAIN, default='.local'): cv.domain_name, cv.Optional(CONF_REBOOT_TIMEOUT, default='5min'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_POWER_SAVE_MODE, default='NONE'): - cv.one_of(*WIFI_POWER_SAVE_MODES, upper=True), + cv.Optional(CONF_POWER_SAVE_MODE, default='NONE'): cv.enum(WIFI_POWER_SAVE_MODES, upper=True), cv.Optional(CONF_FAST_CONNECT, default=False): cv.boolean, cv.Optional(CONF_USE_ADDRESS): cv.string_strict, @@ -175,7 +177,7 @@ def to_code(config): cg.add(wifi.set_ap(wifi_network(config[CONF_AP], config.get(CONF_MANUAL_IP)))) cg.add(wifi.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) - cg.add(wifi.set_power_save_mode(WIFI_POWER_SAVE_MODES[config[CONF_POWER_SAVE_MODE]])) + cg.add(wifi.set_power_save_mode(config[CONF_POWER_SAVE_MODE])) cg.add(wifi.set_fast_connect(config[CONF_FAST_CONNECT])) if CORE.is_esp8266: @@ -187,8 +189,6 @@ def to_code(config): yield cg.register_component(wifi, config) -@CONDITION_REGISTRY.register('wifi.connected', cv.Schema({})) +@automation.register_condition('wifi.connected', WiFiConnectedCondition, cv.Schema({})) def wifi_connected_to_code(config, condition_id, template_arg, args): - rhs = WiFiConnectedCondition.new(template_arg) - type = WiFiConnectedCondition.template(template_arg) - yield cg.Pvariable(condition_id, rhs, type=type) + yield cg.new_Pvariable(condition_id, template_arg) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index ffea4982b..28d911606 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -297,7 +297,7 @@ void WiFiComponent::check_scanning_finished() { } std::stable_sort(this->scan_result_.begin(), this->scan_result_.end(), - [this](const WiFiScanResult &a, const WiFiScanResult &b) { + [](const WiFiScanResult &a, const WiFiScanResult &b) { if (a.get_matches() && !b.get_matches()) return true; if (!a.get_matches() && b.get_matches()) diff --git a/esphome/components/wifi_info/text_sensor.py b/esphome/components/wifi_info/text_sensor.py index 56a073ba3..81ee78784 100644 --- a/esphome/components/wifi_info/text_sensor.py +++ b/esphome/components/wifi_info/text_sensor.py @@ -1,7 +1,7 @@ -from esphome.components import text_sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_BSSID, CONF_ID, CONF_IP_ADDRESS, CONF_NAME, CONF_SSID +import esphome.config_validation as cv +from esphome.components import text_sensor +from esphome.const import CONF_BSSID, CONF_ID, CONF_IP_ADDRESS, CONF_SSID from esphome.core import coroutine DEPENDENCIES = ['wifi'] @@ -12,15 +12,15 @@ SSIDWiFiInfo = wifi_info_ns.class_('SSIDWiFiInfo', text_sensor.TextSensor, cg.Co BSSIDWiFiInfo = wifi_info_ns.class_('BSSIDWiFiInfo', text_sensor.TextSensor, cg.Component) CONFIG_SCHEMA = cv.Schema({ - cv.Optional(CONF_IP_ADDRESS): cv.nameable(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(IPAddressWiFiInfo), - })), - cv.Optional(CONF_SSID): cv.nameable(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(SSIDWiFiInfo), - })), - cv.Optional(CONF_BSSID): cv.nameable(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(BSSIDWiFiInfo), - })), + cv.Optional(CONF_IP_ADDRESS): text_sensor.TEXT_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(IPAddressWiFiInfo), + }), + cv.Optional(CONF_SSID): text_sensor.TEXT_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(SSIDWiFiInfo), + }), + cv.Optional(CONF_BSSID): text_sensor.TEXT_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(BSSIDWiFiInfo), + }), }) @@ -28,7 +28,7 @@ CONFIG_SCHEMA = cv.Schema({ def setup_conf(config, key): if key in config: conf = config[key] - var = cg.new_Pvariable(conf[CONF_ID], conf[CONF_NAME]) + var = cg.new_Pvariable(conf[CONF_ID]) yield cg.register_component(var, conf) yield text_sensor.register_text_sensor(var, conf) diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index c3685db06..13e632bde 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -9,7 +9,6 @@ namespace wifi_info { class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor { public: - IPAddressWiFiInfo(const std::string &name) : TextSensor(name) {} void loop() override { IPAddress ip = WiFi.localIP(); if (ip != this->last_ip_) { @@ -25,7 +24,6 @@ class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor { class SSIDWiFiInfo : public Component, public text_sensor::TextSensor { public: - SSIDWiFiInfo(const std::string &name) : TextSensor(name) {} void loop() override { String ssid = WiFi.SSID(); if (this->last_ssid_ != ssid.c_str()) { @@ -41,7 +39,6 @@ class SSIDWiFiInfo : public Component, public text_sensor::TextSensor { class BSSIDWiFiInfo : public Component, public text_sensor::TextSensor { public: - BSSIDWiFiInfo(const std::string &name) : TextSensor(name) {} void loop() override { uint8_t *bssid = WiFi.BSSID(); if (memcmp(bssid, this->last_bssid_.data(), 6) != 0) { diff --git a/esphome/components/wifi_signal/sensor.py b/esphome/components/wifi_signal/sensor.py index 48134919c..e53daede1 100644 --- a/esphome/components/wifi_signal/sensor.py +++ b/esphome/components/wifi_signal/sensor.py @@ -1,24 +1,18 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, CONF_ICON, \ - CONF_UNIT_OF_MEASUREMENT, CONF_ACCURACY_DECIMALS, ICON_WIFI, UNIT_DECIBEL +from esphome.const import CONF_ID, ICON_WIFI, UNIT_DECIBEL DEPENDENCIES = ['wifi'] wifi_signal_ns = cg.esphome_ns.namespace('wifi_signal') WiFiSignalSensor = wifi_signal_ns.class_('WiFiSignalSensor', sensor.PollingSensorComponent) -CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_variable_id(WiFiSignalSensor), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, - - cv.Optional(CONF_ICON, default=ICON_WIFI): sensor.icon, - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_DECIBEL): sensor.unit_of_measurement, - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals, -}).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_WIFI, 0).extend({ + cv.GenerateID(): cv.declare_id(WiFiSignalSensor), +}).extend(cv.polling_component_schema('60s')) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) diff --git a/esphome/components/wifi_signal/wifi_signal_sensor.h b/esphome/components/wifi_signal/wifi_signal_sensor.h index 623d7acda..8fe108a53 100644 --- a/esphome/components/wifi_signal/wifi_signal_sensor.h +++ b/esphome/components/wifi_signal/wifi_signal_sensor.h @@ -8,11 +8,8 @@ namespace esphome { namespace wifi_signal { -class WiFiSignalSensor : public sensor::PollingSensorComponent { +class WiFiSignalSensor : public sensor::Sensor, public PollingComponent { public: - explicit WiFiSignalSensor(const std::string &name, uint32_t update_interval) - : sensor::PollingSensorComponent(name, update_interval) {} - void update() override { this->publish_state(WiFi.RSSI()); } void dump_config() override; diff --git a/esphome/components/xiaomi_ble/__init__.py b/esphome/components/xiaomi_ble/__init__.py index 3e0132d7e..ef5df12cd 100644 --- a/esphome/components/xiaomi_ble/__init__.py +++ b/esphome/components/xiaomi_ble/__init__.py @@ -10,7 +10,7 @@ xiaomi_ble_ns = cg.esphome_ns.namespace('xiaomi_ble') XiaomiListener = xiaomi_ble_ns.class_('XiaomiListener', cg.Component, ESPBTDeviceListener) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(XiaomiListener), + cv.GenerateID(): cv.declare_id(XiaomiListener), }).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_miflora/sensor.py b/esphome/components/xiaomi_miflora/sensor.py index 2b7d823c4..f6b44f923 100644 --- a/esphome/components/xiaomi_miflora/sensor.py +++ b/esphome/components/xiaomi_miflora/sensor.py @@ -4,7 +4,6 @@ from esphome.components import sensor from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ ESP_BLE_DEVICE_SCHEMA from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - CONF_UNIT_OF_MEASUREMENT, CONF_ICON, CONF_ACCURACY_DECIMALS, \ UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \ UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER @@ -16,34 +15,14 @@ xiaomi_miflora_ns = cg.esphome_ns.namespace('xiaomi_miflora') XiaomiMiflora = xiaomi_miflora_ns.class_('XiaomiMiflora', ESPBTDeviceListener, cg.Component) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(XiaomiMiflora), + cv.GenerateID(): cv.declare_id(XiaomiMiflora), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_CELSIUS): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_THERMOMETER): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=1): sensor.accuracy_decimals - })), - cv.Optional(CONF_MOISTURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PERCENT): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_WATER_PERCENT): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals - })), - cv.Optional(CONF_ILLUMINANCE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_LUX): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_BRIGHTNESS_5): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals - })), - cv.Optional(CONF_CONDUCTIVITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_MICROSIEMENS_PER_CENTIMETER): - sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_FLOWER): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals - })), - cv.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PERCENT): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_BATTERY): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals - })), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 0), + cv.Optional(CONF_CONDUCTIVITY): + sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), }).extend(ESP_BLE_DEVICE_SCHEMA) diff --git a/esphome/components/xiaomi_mijia/sensor.py b/esphome/components/xiaomi_mijia/sensor.py index 0a9046880..ef6844330 100644 --- a/esphome/components/xiaomi_mijia/sensor.py +++ b/esphome/components/xiaomi_mijia/sensor.py @@ -4,7 +4,6 @@ from esphome.components import sensor from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ ESP_BLE_DEVICE_SCHEMA from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - CONF_UNIT_OF_MEASUREMENT, CONF_ICON, CONF_ACCURACY_DECIMALS, \ UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID DEPENDENCIES = ['esp32_ble_tracker'] @@ -14,23 +13,11 @@ xiaomi_mijia_ns = cg.esphome_ns.namespace('xiaomi_mijia') XiaomiMijia = xiaomi_mijia_ns.class_('XiaomiMijia', ESPBTDeviceListener, cg.Component) CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_variable_id(XiaomiMijia), + cv.GenerateID(): cv.declare_id(XiaomiMijia), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_CELSIUS): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_THERMOMETER): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=1): sensor.accuracy_decimals - })), - cv.Optional(CONF_HUMIDITY): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PERCENT): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_WATER_PERCENT): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=1): sensor.accuracy_decimals - })), - cv.Optional(CONF_BATTERY_LEVEL): cv.nameable(sensor.SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PERCENT): sensor.unit_of_measurement, - cv.Optional(CONF_ICON, default=ICON_BATTERY): sensor.icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=0): sensor.accuracy_decimals - })), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), }).extend(ESP_BLE_DEVICE_SCHEMA) diff --git a/esphome/config.py b/esphome/config.py index 101e1d385..7dac04daf 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -6,20 +6,23 @@ import logging import re import os.path +# pylint: disable=unused-import, wrong-import-order +from contextlib import contextmanager + import voluptuous as vol from esphome import core, core_config, yaml_util from esphome.components import substitutions +from esphome.components.substitutions import CONF_SUBSTITUTIONS from esphome.const import CONF_ESPHOME, CONF_PLATFORM, ESP_PLATFORMS -from esphome.core import CORE, EsphomeError +from esphome.core import CORE, EsphomeError # noqa from esphome.helpers import color, indent from esphome.py_compat import text_type from esphome.util import safe_print, OrderedDict -# pylint: disable=unused-import, wrong-import-order from typing import List, Optional, Tuple, Union # noqa from esphome.core import ConfigType # noqa -from esphome.yaml_util import is_secret +from esphome.yaml_util import is_secret, ESPHomeDataBase from esphome.voluptuous_schema import ExtraKeysInvalid _LOGGER = logging.getLogger(__name__) @@ -162,61 +165,83 @@ def iter_components(config): ConfigPath = List[Union[str, int]] -def _path_begins_with_(path, other): # type: (ConfigPath, ConfigPath) -> bool +def _path_begins_with(path, other): # type: (ConfigPath, ConfigPath) -> bool if len(path) < len(other): return False return path[:len(other)] == other -def _path_begins_with(path, other): # type: (ConfigPath, ConfigPath) -> bool - ret = _path_begins_with_(path, other) - return ret - - class Config(OrderedDict): def __init__(self): super(Config, self).__init__() - self.errors = [] # type: List[Tuple[basestring, ConfigPath]] - self.domains = [] # type: List[Tuple[ConfigPath, basestring]] + # A list of voluptuous errors + self.errors = [] # type: List[vol.Invalid] + # A list of paths that should be fully outputted + # The values will be the paths to all "domain", for example (['logger'], 'logger') + # or (['sensor', 'ultrasonic'], 'sensor.ultrasonic') + self.output_paths = [] # type: List[Tuple[ConfigPath, unicode]] - def add_error(self, message, path): + def add_error(self, error): + # type: (vol.Invalid) -> None + if isinstance(error, vol.MultipleInvalid): + for err in error.errors: + self.add_error(err) + return + self.errors.append(error) + + @contextmanager + def catch_error(self, path=None): + path = path or [] + try: + yield + except vol.Invalid as e: + e.prepend(path) + self.add_error(e) + + def add_str_error(self, message, path): # type: (basestring, ConfigPath) -> None - if not isinstance(message, text_type): - message = text_type(message) - self.errors.append((message, path)) + self.add_error(vol.Invalid(message, path)) - def add_domain(self, path, name): - # type: (ConfigPath, basestring) -> None - self.domains.append((path, name)) + def add_output_path(self, path, domain): + # type: (ConfigPath, unicode) -> None + self.output_paths.append((path, domain)) - def remove_domain(self, path, name): - self.domains.remove((path, name)) - - def lookup_domain(self, path): - # type: (ConfigPath) -> Optional[basestring] - best_len = 0 - best_domain = None - for d_path, domain in self.domains: - if len(d_path) < best_len: - continue - if _path_begins_with(path, d_path): - best_len = len(d_path) - best_domain = domain - return best_domain + def remove_output_path(self, path, domain): + # type: (ConfigPath, unicode) -> None + self.output_paths.remove((path, domain)) def is_in_error_path(self, path): - for _, p in self.errors: - if _path_begins_with(p, path): + # type: (ConfigPath) -> bool + for err in self.errors: + if _path_begins_with(err.path, path): return True return False + def set_by_path(self, path, value): + conf = self + for key in path[:-1]: + conf = conf[key] + conf[path[-1]] = value + def get_error_for_path(self, path): - for msg, p in self.errors: - if self.nested_item_path(p) == path: - return msg + # type: (ConfigPath) -> Optional[vol.Invalid] + for err in self.errors: + if self.get_deepest_path(err.path) == path: + return err return None - def nested_item(self, path): + def get_deepest_value_for_path(self, path): + # type: (ConfigPath) -> ConfigType + data = self + for item_index in path: + try: + data = data[item_index] + except (KeyError, IndexError, TypeError): + return data + return data + + def get_nested_item(self, path): + # type: (ConfigPath) -> ConfigType data = self for item_index in path: try: @@ -225,7 +250,9 @@ class Config(OrderedDict): return {} return data - def nested_item_path(self, path): + def get_deepest_path(self, path): + # type: (ConfigPath) -> ConfigPath + """Return the path that is the deepest reachable by following path.""" data = self part = [] for item_index in path: @@ -261,9 +288,13 @@ def do_id_pass(result): # type: (Config) -> None searching_ids = [] # type: List[Tuple[core.ID, ConfigPath]] for id, path in iter_ids(result): if id.is_declaration: - if id.id is not None and any(v[0].id == id.id for v in declare_ids): - result.add_error(u"ID {} redefined!".format(id.id), path) - continue + if id.id is not None: + # Look for duplicate definitions + match = next((v for v in declare_ids if v[0].id == id.id), None) + if match is not None: + opath = u'->'.join(text_type(v) for v in match[1]) + result.add_str_error(u"ID {} redefined! Check {}".format(id.id, opath), path) + continue declare_ids.append((id, path)) else: searching_ids.append((id, path)) @@ -278,14 +309,22 @@ def do_id_pass(result): # type: (Config) -> None match = next((v[0] for v in declare_ids if v[0].id == id.id), None) if match is None: # No declared ID with this name - result.add_error("Couldn't find ID '{}'".format(id.id), path) + import difflib + error = ("Couldn't find ID '{}'. Please check you have defined " + "an ID with that name in your configuration.".format(id.id)) + # Find candidates + matches = difflib.get_close_matches(id.id, [v[0].id for v in declare_ids]) + if matches: + matches_s = ', '.join('"{}"'.format(x) for x in matches) + error += " These IDs look similar: {}.".format(matches_s) + result.add_str_error(error, path) continue if not isinstance(match.type, MockObjClass) or not isinstance(id.type, MockObjClass): continue if not match.type.inherits_from(id.type): - result.add_error("ID '{}' of type {} doesn't inherit from {}. Please double check " - "your ID is pointing to the correct value" - "".format(id.id, match.type, id.type), path) + result.add_str_error("ID '{}' of type {} doesn't inherit from {}. Please " + "double check your ID is pointing to the correct value" + "".format(id.id, match.type, id.type), path) if id.id is None and id.type is not None: for v in declare_ids: @@ -296,204 +335,185 @@ def do_id_pass(result): # type: (Config) -> None id.id = v[0].id break else: - result.add_error("Couldn't resolve ID for type '{}'".format(id.type), path) + result.add_str_error("Couldn't resolve ID for type '{}'".format(id.type), path) def validate_config(config): result = Config() - def _comp_error(ex, path): - # type: (vol.Invalid, List[basestring]) -> None - if isinstance(ex, vol.MultipleInvalid): - errors = ex.errors - else: - errors = [ex] + # 1. Load substitutions + if CONF_SUBSTITUTIONS in config: + result[CONF_SUBSTITUTIONS] = config[CONF_SUBSTITUTIONS] + result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS) + try: + substitutions.do_substitution_pass(config) + except vol.Invalid as err: + result.add_error(err) + return result - for e in errors: - path_ = path + e.path - domain = result.lookup_domain(path_) or '' - result.add_error(_format_vol_invalid(e, config, path, domain), path_) - - skip_paths = list() # type: List[ConfigPath] - - # Step 1: Load everything - result.add_domain([CONF_ESPHOME], CONF_ESPHOME) + # 2. Load partial core config result[CONF_ESPHOME] = config[CONF_ESPHOME] - config_queue = collections.deque() + result.add_output_path([CONF_ESPHOME], CONF_ESPHOME) + try: + core_config.preload_core_config(config) + except vol.Invalid as err: + result.add_error(err) + return result + # Remove temporary esphome config path again, it will be reloaded later + result.remove_output_path([CONF_ESPHOME], CONF_ESPHOME) + + # 3. Load components. + # Load components (also AUTO_LOAD) and set output paths of result + # Queue of items to load, FIFO + load_queue = collections.deque() for domain, conf in config.items(): - config_queue.append((domain, conf)) + load_queue.append((domain, conf)) - while config_queue: - domain, conf = config_queue.popleft() - domain = str(domain) - if domain == CONF_ESPHOME or domain.startswith(u'.'): - skip_paths.append([domain]) + # List of items to enter next stage + check_queue = [] # type: List[Tuple[ConfigPath, str, ConfigType, ComponentManifest]] + + # This step handles: + # - Adding output path + # - Auto Load + # - Loading configs into result + + while load_queue: + domain, conf = load_queue.popleft() + domain = text_type(domain) + if domain.startswith(u'.'): + # Ignore top-level keys starting with a dot continue - result.add_domain([domain], domain) + result.add_output_path([domain], domain) result[domain] = conf - if conf is None: - result[domain] = conf = {} component = get_component(domain) + path = [domain] if component is None: - result.add_error(u"Component not found: {}".format(domain), [domain]) - skip_paths.append([domain]) - continue - - if component.is_multi_conf and not isinstance(conf, list): - result[domain] = conf = [conf] - - success = True - for dependency in component.dependencies: - if dependency not in config: - result.add_error(u"Component {} requires component {}".format(domain, dependency), - [domain]) - success = False - if not success: - skip_paths.append([domain]) - continue - - success = True - for conflict in component.conflicts_with: - if conflict in config: - result.add_error(u"Component {} cannot be used together with component {}" - u"".format(domain, conflict), [domain]) - success = False - if not success: - skip_paths.append([domain]) + result.add_str_error(u"Component not found: {}".format(domain), path) continue + # Process AUTO_LOAD for load in component.auto_load: if load not in config: - conf = core.AutoLoad() - config[load] = conf - config_queue.append((load, conf)) - - if CORE.esp_platform not in component.esp_platforms: - result.add_error(u"Component {} doesn't support {}.".format(domain, CORE.esp_platform), - [domain]) - skip_paths.append([domain]) - continue + load_conf = core.AutoLoad() + config[load] = load_conf + load_queue.append((load, load_conf)) if not component.is_platform_component: - if component.config_schema is None and not isinstance(conf, core.AutoLoad): - result.add_error(u"Component {} cannot be loaded via YAML (no CONFIG_SCHEMA)." - u"".format(domain), [domain]) - skip_paths.append([domain]) + check_queue.append(([domain], domain, conf, component)) continue - result.remove_domain([domain], domain) + # This is a platform component, proceed to reading platform entries + # Remove this is as an output path + result.remove_output_path([domain], domain) + # Ensure conf is a list if not isinstance(conf, list) and conf: result[domain] = conf = [conf] for i, p_config in enumerate(conf): + path = [domain, i] + # Construct temporary unknown output path + p_domain = u'{}.unknown'.format(domain) + result.add_output_path(path, p_domain) + result[domain][i] = p_config if not isinstance(p_config, dict): - result.add_error(u"Platform schemas must have 'platform:' key", [domain, i]) - skip_paths.append([domain, i]) + result.add_str_error(u"Platform schemas must be key-value pairs.", path) continue p_name = p_config.get('platform') if p_name is None: - result.add_error(u"No platform specified for {}".format(domain), [domain, i]) - skip_paths.append([domain, i]) + result.add_str_error(u"No platform specified! See 'platform' key.", path) continue + # Remove temp output path and construct new one + result.remove_output_path(path, p_domain) p_domain = u'{}.{}'.format(domain, p_name) - result.add_domain([domain, i], p_domain) + result.add_output_path(path, p_domain) + # Try Load platform platform = get_platform(domain, p_name) if platform is None: - result.add_error(u"Platform not found: '{}'".format(p_domain), [domain, i]) - skip_paths.append([domain, i]) - continue - - success = True - for dependency in platform.dependencies: - if dependency not in config: - result.add_error(u"Platform {} requires component {}" - u"".format(p_domain, dependency), [domain, i]) - success = False - if not success: - skip_paths.append([domain, i]) - continue - - success = True - for conflict in platform.conflicts_with: - if conflict in config: - result.add_error(u"Platform {} cannot be used together with component {}" - u"".format(p_domain, conflict), [domain, i]) - success = False - if not success: - skip_paths.append([domain, i]) + result.add_str_error(u"Platform not found: '{}'".format(p_domain), path) continue + # Process AUTO_LOAD for load in platform.auto_load: if load not in config: - conf = core.AutoLoad() - config[load] = conf - config_queue.append((load, conf)) + load_conf = core.AutoLoad() + config[load] = load_conf + load_queue.append((load, load_conf)) - if CORE.esp_platform not in platform.esp_platforms: - result.add_error(u"Platform {} doesn't support {}." - u"".format(p_domain, CORE.esp_platform), [domain, i]) - skip_paths.append([domain, i]) - continue + check_queue.append((path, p_domain, p_config, platform)) - if platform.config_schema is None: - result.add_error(u"Platform {} cannot be loaded via YAML (no PLATFORM_SCHEMA)." - u"".format(p_domain), [domain, i]) - skip_paths.append([domain]) + # 4. Validate component metadata, including + # - Transformation (nullable, multi conf) + # - Dependencies + # - Conflicts + # - Supported ESP Platform - # Step 2: Validate configuration - try: - result[CONF_ESPHOME] = core_config.CONFIG_SCHEMA(result[CONF_ESPHOME]) - except vol.Invalid as ex: - _comp_error(ex, [CONF_ESPHOME]) + # List of items to proceed to next stage + validate_queue = [] # type: List[Tuple[ConfigPath, ConfigType, ComponentManifest]] + for path, domain, conf, comp in check_queue: + if conf is None: + result[domain] = conf = {} - for domain, conf in result.items(): - domain = str(domain) - if [domain] in skip_paths: - continue - component = get_component(domain) - - if not component.is_platform_component: - if component.config_schema is None: - continue - - if component.is_multi_conf: - for i, conf_ in enumerate(conf): - try: - validated = component.config_schema(conf_) - result[domain][i] = validated - except vol.Invalid as ex: - _comp_error(ex, [domain, i]) - else: - try: - validated = component.config_schema(conf) - result[domain] = validated - except vol.Invalid as ex: - _comp_error(ex, [domain]) - continue + success = True + for dependency in comp.dependencies: + if dependency not in config: + result.add_str_error(u"Component {} requires component {}" + u"".format(domain, dependency), path) + success = False + if not success: continue - for i, p_config in enumerate(conf): - if [domain, i] in skip_paths: - continue - p_name = p_config['platform'] - platform = get_platform(domain, p_name) + success = True + for conflict in comp.conflicts_with: + if conflict in config: + result.add_str_error(u"Component {} cannot be used together with component {}" + u"".format(domain, conflict), path) + success = False + if not success: + continue - if platform.config_schema is not None: + if CORE.esp_platform not in comp.esp_platforms: + result.add_str_error(u"Component {} doesn't support {}.".format(domain, + CORE.esp_platform), + path) + continue + + if not comp.is_platform_component and comp.config_schema is None and \ + not isinstance(conf, core.AutoLoad): + result.add_str_error(u"Component {} cannot be loaded via YAML " + u"(no CONFIG_SCHEMA).".format(domain), path) + continue + + if comp.is_multi_conf: + if not isinstance(conf, list): + result[domain] = conf = [conf] + for i, part_conf in enumerate(conf): + validate_queue.append((path + [i], part_conf, comp)) + continue + + validate_queue.append((path, conf, comp)) + + # 5. Validate configuration schema + for path, conf, comp in validate_queue: + if comp.config_schema is None: + continue + with result.catch_error(path): + if comp.is_platform: # Remove 'platform' key for validation - input_conf = OrderedDict(p_config) + input_conf = OrderedDict(conf) platform_val = input_conf.pop('platform') - try: - p_validated = platform.config_schema(input_conf) - except vol.Invalid as ex: - _comp_error(ex, [domain, i]) - continue - if not isinstance(p_validated, OrderedDict): - p_validated = OrderedDict(p_validated) - p_validated['platform'] = platform_val - p_validated.move_to_end('platform', last=False) - result[domain][i] = p_validated + validated = comp.config_schema(input_conf) + # Ensure result is OrderedDict so we can call move_to_end + if not isinstance(validated, OrderedDict): + validated = OrderedDict(validated) + validated['platform'] = platform_val + validated.move_to_end('platform', last=False) + result.set_by_path(path, validated) + else: + validated = comp.config_schema(conf) + result.set_by_path(path, validated) + # 6. If no validation errors, check IDs if not result.errors: # Only parse IDs if no validation error. Otherwise # user gets confusing messages @@ -511,9 +531,6 @@ def _nested_getitem(data, path): def humanize_error(config, validation_error): - offending_item_summary = _nested_getitem(config, validation_error.path) - if isinstance(offending_item_summary, dict): - offending_item_summary = None validation_error = text_type(validation_error) m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error) if m is not None: @@ -521,19 +538,26 @@ def humanize_error(config, validation_error): validation_error = validation_error.strip() if not validation_error.endswith(u'.'): validation_error += u'.' - if offending_item_summary is None or is_secret(offending_item_summary): - return validation_error - - return u"{} Got '{}'".format(validation_error, offending_item_summary) + return validation_error -def _format_vol_invalid(ex, config, path, domain): - # type: (vol.Invalid, ConfigType, ConfigPath, basestring) -> unicode +def _get_parent_name(path, config): + if not path: + return '' + for domain_path, domain in config.output_paths: + if _path_begins_with(path, domain_path): + if len(path) > len(domain_path): + # Sub-item + break + return domain + return path[-1] + + +def _format_vol_invalid(ex, config): + # type: (vol.Invalid, Config) -> unicode message = u'' - try: - paren = ex.path[-2] - except IndexError: - paren = domain + + paren = _get_parent_name(ex.path[:-1], config) if isinstance(ex, ExtraKeysInvalid): if ex.candidates: @@ -547,20 +571,26 @@ def _format_vol_invalid(ex, config, path, domain): elif u'required key not provided' in ex.error_message: message += u"'{}' is a required option for [{}].".format(ex.path[-1], paren) else: - message += humanize_error(_nested_getitem(config, path), ex) + message += humanize_error(config, ex) return message -def load_config(): +class InvalidYAMLError(EsphomeError): + def __init__(self, path, base_exc): + message = u"Invalid YAML at {}. Please see YAML syntax reference or use an " \ + u"online YAML syntax validator. ({})".format(path, base_exc) + super(InvalidYAMLError, self).__init__(message) + self.path = path + self.base_exc = base_exc + + +def _load_config(): try: config = yaml_util.load_yaml(CORE.config_path) - except OSError: - raise EsphomeError(u"Invalid YAML at {}. Please see YAML syntax reference or use an online " - u"YAML syntax validator".format(CORE.config_path)) + except EsphomeError as e: + raise InvalidYAMLError(CORE.config_path, e) CORE.raw_config = config - config = substitutions.do_substitution_pass(config) - core_config.preload_core_config(config) try: result = validate_config(config) @@ -573,13 +603,21 @@ def load_config(): return result +def load_config(): + try: + return _load_config() + except vol.Invalid as err: + raise EsphomeError("Error while parsing config: {}".format(err)) + + def line_info(obj, highlight=True): """Display line config source.""" if not highlight: return None - if hasattr(obj, '__config_file__'): - return color('cyan', "[source {}:{}]" - .format(obj.__config_file__, obj.__line__ or '?')) + if isinstance(obj, ESPHomeDataBase) and obj.esp_range is not None: + mark = obj.esp_range.start_mark + source = u"[source {}:{}]".format(mark.document, mark.line + 1) + return color('cyan', source) return None @@ -595,14 +633,14 @@ def _print_on_next_line(obj): def dump_dict(config, path, at_root=True): # type: (Config, ConfigPath, bool) -> Tuple[unicode, bool] - conf = config.nested_item(path) + conf = config.get_nested_item(path) ret = u'' multiline = False if at_root: error = config.get_error_for_path(path) if error is not None: - ret += u'\n' + color('bold_red', error) + u'\n' + ret += u'\n' + color('bold_red', _format_vol_invalid(error, config)) + u'\n' if isinstance(conf, (list, tuple)): multiline = True @@ -614,14 +652,14 @@ def dump_dict(config, path, at_root=True): path_ = path + [i] error = config.get_error_for_path(path_) if error is not None: - ret += u'\n' + color('bold_red', error) + u'\n' + ret += u'\n' + color('bold_red', _format_vol_invalid(error, config)) + u'\n' sep = u'- ' if config.is_in_error_path(path_): sep = color('red', sep) msg, _ = dump_dict(config, path_, at_root=False) msg = indent(msg) - inf = line_info(config.nested_item(path_), highlight=config.is_in_error_path(path_)) + inf = line_info(config.get_nested_item(path_), highlight=config.is_in_error_path(path_)) if inf is not None: msg = inf + u'\n' + msg elif msg: @@ -637,14 +675,14 @@ def dump_dict(config, path, at_root=True): path_ = path + [k] error = config.get_error_for_path(path_) if error is not None: - ret += u'\n' + color('bold_red', error) + u'\n' + ret += u'\n' + color('bold_red', _format_vol_invalid(error, config)) + u'\n' st = u'{}: '.format(k) if config.is_in_error_path(path_): st = color('red', st) msg, m = dump_dict(config, path_, at_root=False) - inf = line_info(config.nested_item(path_), highlight=config.is_in_error_path(path_)) + inf = line_info(config.get_nested_item(path_), highlight=config.is_in_error_path(path_)) if m: msg = u'\n' + indent(msg) @@ -717,12 +755,12 @@ def read_config(verbose): safe_print(color('bold_red', u"Failed config")) safe_print('') - for path, domain in res.domains: + for path, domain in res.output_paths: if not res.is_in_error_path(path): continue safe_print(color('bold_red', u'{}:'.format(domain)) + u' ' + - (line_info(res.nested_item(path)) or u'')) + (line_info(res.get_nested_item(path)) or u'')) safe_print(indent(dump_dict(res, path)[0])) return None return OrderedDict(res) diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py new file mode 100644 index 000000000..c235371db --- /dev/null +++ b/esphome/config_helpers.py @@ -0,0 +1,27 @@ +from __future__ import print_function + +import codecs +import json + +from esphome.core import CORE, EsphomeError +from esphome.py_compat import safe_input + + +def read_config_file(path): + # type: (basestring) -> unicode + if CORE.vscode: + print(json.dumps({ + 'type': 'read_file', + 'path': path, + })) + data = json.loads(safe_input()) + assert data['type'] == 'file_response' + return data['content'] + + try: + with codecs.open(path, encoding='utf-8') as handle: + return handle.read() + except IOError as exc: + raise EsphomeError(u"Error accessing file {}: {}".format(path, exc)) + except UnicodeDecodeError as exc: + raise EsphomeError(u"Unable to read file {}: {}".format(path, exc)) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 0c2321cb5..a2da05af7 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -5,6 +5,7 @@ from __future__ import print_function import logging import os import re +from contextlib import contextmanager import uuid as uuid_ from datetime import datetime @@ -14,9 +15,10 @@ from esphome import core from esphome.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \ CONF_INTERNAL, CONF_NAME, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, \ CONF_RETAIN, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, \ - CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE + CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes +from esphome.helpers import list_starts_with from esphome.py_compat import integer_types, string_types, text_type, IS_PY2 from esphome.voluptuous_schema import _Schema @@ -25,8 +27,6 @@ _LOGGER = logging.getLogger(__name__) # pylint: disable=invalid-name Schema = _Schema -Optional = vol.Optional -Required = vol.Required All = vol.All Coerce = vol.Coerce Range = vol.Range @@ -39,14 +39,8 @@ Length = vol.Length Exclusive = vol.Exclusive Inclusive = vol.Inclusive ALLOW_EXTRA = vol.ALLOW_EXTRA - -port = All(Coerce(int), Range(min=1, max=65535)) -float_ = Coerce(float) -positive_float = All(float_, Range(min=0)) -zero_to_one_float = All(float_, Range(min=0, max=1)) -negative_one_to_one_float = All(float_, Range(min=-1, max=1)) -positive_int = All(Coerce(int), Range(min=0)) -positive_not_null_int = All(Coerce(int), Range(min=0, min_included=False)) +UNDEFINED = vol.UNDEFINED +RequiredFieldInvalid = vol.RequiredFieldInvalid ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_' @@ -71,6 +65,41 @@ RESERVED_IDS = [ ] +class Optional(vol.Optional): + """Mark a field as optional and optionally define a default for the field. + + When no default is defined, the validated config will not contain the key. + You can check if the key is defined with 'CONF_ in config'. Or to access + the key and return None if it does not exist, call config.get(CONF_) + + If a default *is* set, the resulting validated config will always contain the + default value. You can therefore directly access the value using the + 'config[CONF_]' syntax. + + In ESPHome, all configuration defaults should be defined with the Optional class + during config validation - specifically *not* in the C++ code or the code generation + phase. + """ + def __init__(self, key, default=UNDEFINED): + super(Optional, self).__init__(key, default=default) + + +class Required(vol.Required): + """Define a field to be required to be set. The validated configuration is guaranteed + to contain this key. + + All required values should be acceessed with the `config[CONF_]` syntax in code + - *not* the `config.get(CONF_)` syntax. + """ + def __init__(self, key): + super(Required, self).__init__(key) + + +def check_not_templatable(value): + if isinstance(value, Lambda): + raise Invalid("This option is not templatable!") + + def alphanumeric(value): if value is None: raise Invalid("string value is None") @@ -90,46 +119,77 @@ def valid_name(value): def string(value): + """Validate that a configuration value is a string. If not, automatically converts to a string. + + Note that this can be lossy, for example the input value 60.00 (float) will be turned into + "60.0" (string). For values where this could be a problem `string_string` has to be used. + """ + check_not_templatable(value) if isinstance(value, (dict, list)): raise Invalid("string value cannot be dictionary or list.") + if isinstance(value, text_type): + return value if value is not None: return text_type(value) raise Invalid("string value is None") def string_strict(value): - """Strictly only allow strings.""" - if isinstance(value, string_types): + """Like string, but only allows strings, and does not automatically convert other types to + strings.""" + check_not_templatable(value) + if isinstance(value, text_type): return value + if isinstance(value, string_types): + return text_type(value) raise Invalid("Must be string, got {}. did you forget putting quotes " "around the value?".format(type(value))) def icon(value): - """Validate icon.""" + """Validate that a given config value is a valid icon.""" value = string_strict(value) + if not value: + return value if value.startswith('mdi:'): return value raise Invalid('Icons should start with prefix "mdi:"') def boolean(value): - """Validate and coerce a boolean value.""" + """Validate the given config option to be a boolean. + + This option allows a bunch of different ways of expressing boolean values: + - instance of boolean + - 'true'/'false' + - 'yes'/'no' + - 'enable'/disable + """ + check_not_templatable(value) + if isinstance(value, bool): + return value if isinstance(value, str): value = value.lower() - if value in ('1', 'true', 'yes', 'on', 'enable'): + if value in ('true', 'yes', 'on', 'enable'): return True - if value in ('0', 'false', 'no', 'off', 'disable'): + if value in ('false', 'no', 'off', 'disable'): return False - raise Invalid('invalid boolean value {}'.format(value)) - return bool(value) + raise Invalid(u"Expected boolean value, but cannot convert {} to a boolean. " + u"Please use 'true' or 'false'".format(value)) def ensure_list(*validators): - """Wrap value in list if it is not one.""" + """Validate this configuration option to be a list. + + If the config value is not a list, it is automatically converted to a + single-item list. + + None and empty dictionaries are converted to empty lists. + """ user = All(*validators) def validator(value): + check_not_templatable(value) if value is None or (isinstance(value, dict) and not value): return [] if not isinstance(value, list): @@ -138,56 +198,80 @@ def ensure_list(*validators): errs = [] for i, val in enumerate(value): try: - ret.append(user(val)) - except vol.MultipleInvalid as err: - err.prepend([i]) + with prepend_path([i]): + ret.append(user(val)) + except MultipleInvalid as err: errs.extend(err.errors) except Invalid as err: - err.prepend([i]) errs.append(err) if errs: - raise vol.MultipleInvalid(errs) + raise MultipleInvalid(errs) return ret return validator -def ensure_list_not_empty(value): - if isinstance(value, list): - return value - return [value] - - -def ensure_dict(value): - if value is None: - return {} - if not isinstance(value, dict): - raise Invalid("Expected a dictionary") - return value - - -def hex_int_(value): - if isinstance(value, integer_types): - return HexInt(value) - value = string_strict(value).lower() - if value.startswith('0x'): - return HexInt(int(value, 16)) - return HexInt(int(value)) +def hex_int(value): + """Validate the given value to be a hex integer. This is mostly for cosmetic + purposes of the generated code. + """ + return HexInt(int_(value)) def int_(value): + """Validate that the config option is an integer. + + Automatically also converts strings to ints. + """ + check_not_templatable(value) if isinstance(value, integer_types): return value value = string_strict(value).lower() + base = 10 if value.startswith('0x'): - return int(value, 16) - return int(value) + base = 16 + try: + return int(value, base) + except ValueError: + raise Invalid(u"Expected integer, but cannot parse {} as an integer".format(value)) -hex_int = Coerce(hex_int_) +def int_range(min=None, max=None, min_included=True, max_included=True): + """Validate that the config option is an integer in the given range.""" + if min is not None: + assert isinstance(min, integer_types) + if max is not None: + assert isinstance(max, integer_types) + return All(int_, Range(min=min, max=max, min_included=min_included, max_included=max_included)) + + +def hex_int_range(min=None, max=None, min_included=True, max_included=True): + """Validate that the config option is an integer in the given range.""" + return All(hex_int, + Range(min=min, max=max, min_included=min_included, max_included=max_included)) + + +def float_range(min=None, max=None, min_included=True, max_included=True): + """Validate that the config option is a floating point number in the given range.""" + if min is not None: + assert isinstance(min, (int, float)) + if max is not None: + assert isinstance(max, (int, float)) + return All(float_, Range(min=min, max=max, min_included=min_included, + max_included=max_included)) + + +port = int_range(min=1, max=65535) +float_ = Coerce(float) +positive_float = float_range(min=0) +zero_to_one_float = float_range(min=0, max=1) +negative_one_to_one_float = float_range(min=-1, max=1) +positive_int = int_range(min=0) +positive_not_null_int = int_range(min=0, min_included=False) def validate_id_name(value): + """Validate that the given value would be a valid C++ identifier name.""" value = string(value) if not value: raise Invalid("ID must not be empty") @@ -205,18 +289,28 @@ def validate_id_name(value): return value -def use_variable_id(type): +def use_id(type): + """Declare that this configuration option should point to an ID with the given type.""" def validator(value): + check_not_templatable(value) if value is None: return core.ID(None, is_declaration=False, type=type) + if isinstance(value, core.ID) and value.is_declaration is False and value.type is type: + return value return core.ID(validate_id_name(value), is_declaration=False, type=type) return validator -def declare_variable_id(type): +def declare_id(type): + """Declare that this configuration option should be used to declare a variable ID + with the given type. + + If two IDs with the same name exist, a validation error is thrown. + """ def validator(value): + check_not_templatable(value) if value is None: return core.ID(None, is_declaration=True, type=type) @@ -226,17 +320,26 @@ def declare_variable_id(type): def templatable(other_validators): + """Validate that the configuration option can (optionally) be templated. + + The user can declare a value as template by using the '!lambda' tag. In that case, + validation is skipped. Otherwise (if the value is not templated) the validator given + as the first argument to this method is called. + """ + schema = Schema(other_validators) + def validator(value): if isinstance(value, Lambda): return value if isinstance(other_validators, dict): - return Schema(other_validators)(value) - return other_validators(value) + return schema(value) + return schema(value) return validator def only_on(platforms): + """Validate that this option can only be specified on the given ESP platforms.""" if not isinstance(platforms, list): platforms = [platforms] @@ -255,7 +358,7 @@ only_on_esp8266 = only_on('ESP8266') # Adapted from: # https://github.com/alecthomas/voluptuous/issues/115#issuecomment-144464666 def has_at_least_one_key(*keys): - """Validate that at least one key exists.""" + """Validate that at least one of the given keys exist in the config.""" def validate(obj): """Test keys exist in dict.""" @@ -270,6 +373,7 @@ def has_at_least_one_key(*keys): def has_exactly_one_key(*keys): + """Validate that exactly one of the given keys exist in the config.""" def validate(obj): if not isinstance(obj, dict): raise Invalid('expected dictionary') @@ -285,6 +389,7 @@ def has_exactly_one_key(*keys): def has_at_most_one_key(*keys): + """Validate that at most one of the given keys exist in the config.""" def validate(obj): if not isinstance(obj, dict): raise Invalid('expected dictionary') @@ -300,17 +405,17 @@ def has_at_most_one_key(*keys): TIME_PERIOD_ERROR = "Time period {} should be format number + unit, for example 5ms, 5s, 5min, 5h" time_period_dict = All( - dict, Schema({ - 'days': float_, - 'hours': float_, - 'minutes': float_, - 'seconds': float_, - 'milliseconds': float_, - 'microseconds': float_, + Schema({ + Optional('days'): float_, + Optional('hours'): float_, + Optional('minutes'): float_, + Optional('seconds'): float_, + Optional('milliseconds'): float_, + Optional('microseconds'): float_, }), - has_at_least_one_key('days', 'hours', 'minutes', - 'seconds', 'milliseconds', 'microseconds'), - lambda value: TimePeriod(**value)) + has_at_least_one_key('days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds'), + lambda value: TimePeriod(**value) +) def time_period_str_colon(value): @@ -338,6 +443,8 @@ def time_period_str_colon(value): def time_period_str_unit(value): """Validate and transform time period with time unit and integer value.""" + check_not_templatable(value) + if isinstance(value, int): raise Invalid("Don't know what '{0}' means as it has no time *unit*! Did you mean " "'{0}s'?".format(value)) @@ -682,6 +789,7 @@ def mqtt_qos(value): def requires_component(comp): + """Validate that this option can only be specified when the component `comp` is loaded.""" def validator(value): if comp not in CORE.raw_config: raise Invalid("This option requires component {}".format(comp)) @@ -690,16 +798,20 @@ def requires_component(comp): return validator -uint8_t = All(int_, Range(min=0, max=255)) -uint16_t = All(int_, Range(min=0, max=65535)) -uint32_t = All(int_, Range(min=0, max=4294967295)) -hex_uint8_t = All(hex_int, Range(min=0, max=255)) -hex_uint16_t = All(hex_int, Range(min=0, max=65535)) -hex_uint32_t = All(hex_int, Range(min=0, max=4294967295)) +uint8_t = int_range(min=0, max=255) +uint16_t = int_range(min=0, max=65535) +uint32_t = int_range(min=0, max=4294967295) +hex_uint8_t = hex_int_range(min=0, max=255) +hex_uint16_t = hex_int_range(min=0, max=65535) +hex_uint32_t = hex_int_range(min=0, max=4294967295) i2c_address = hex_uint8_t def percentage(value): + """Validate that the value is a percentage. + + The resulting value is an integer in the range 0.0 to 1.0. + """ value = possibly_negative_percentage(value) return zero_to_one_float(value) @@ -728,6 +840,9 @@ def percentage_int(value): def invalid(message): + """Mark this value as invalid. Each time *any* value is passed here it will result in a + validation error with the given message. + """ def validator(value): raise Invalid(message) @@ -738,14 +853,56 @@ def valid(value): return value +@contextmanager +def prepend_path(path): + """A contextmanager helper to prepend a path to all voluptuous errors.""" + if not isinstance(path, (list, tuple)): + path = [path] + try: + yield + except vol.Invalid as e: + e.prepend(path) + raise e + + +@contextmanager +def remove_prepend_path(path): + """A contextmanager helper to remove a path from a voluptuous error.""" + if not isinstance(path, (list, tuple)): + path = [path] + try: + yield + except vol.Invalid as e: + if list_starts_with(e.path, path): + # Can't set e.path (namedtuple + for _ in range(len(path)): + e.path.pop(0) + raise e + + def one_of(*values, **kwargs): + """Validate that the config option is one of the given values. + + :param values: The valid values for this type + + :Keyword Arguments: + - *lower* (``bool``, default=False): Whether to convert the incoming values to lowercase + strings. + - *upper* (``bool``, default=False): Whether to convert the incoming values to uppercase + strings. + - *int* (``bool``, default=False): Whether to convert the incoming values to integers. + - *float* (``bool``, default=False): Whether to convert the incoming values to floats. + - *space* (``str``, default=' '): What to convert spaces in the input string to. + """ options = u', '.join(u"'{}'".format(x) for x in values) - lower = kwargs.get('lower', False) - upper = kwargs.get('upper', False) - string_ = kwargs.get('string', False) or lower or upper - to_int = kwargs.get('int', False) - to_float = kwargs.get('float', False) - space = kwargs.get('space', ' ') + lower = kwargs.pop('lower', False) + upper = kwargs.pop('upper', False) + string_ = kwargs.pop('string', False) or lower or upper + to_int = kwargs.pop('int', False) + to_float = kwargs.pop('float', False) + space = kwargs.pop('space', ' ') + if kwargs: + raise ValueError def validator(value): if string_: @@ -760,13 +917,44 @@ def one_of(*values, **kwargs): if upper: value = Upper(value) if value not in values: - raise Invalid(u"Unknown value '{}', must be one of {}".format(value, options)) + import difflib + options_ = [text_type(x) for x in values] + option = text_type(value) + matches = difflib.get_close_matches(option, options_) + if matches: + raise Invalid(u"Unknown value '{}', did you mean {}?" + u"".format(value, u", ".join(u"'{}'".format(x) for x in matches))) + raise Invalid(u"Unknown value '{}', valid options are {}.".format(value, options)) + return value + + return validator + + +def enum(mapping, **kwargs): + """Validate this config option against an enum mapping. + + The mapping should be a dictionary with the key representing the config value name and + a value representing the expression to set during code generation. + + Accepts all kwargs of one_of. + """ + assert isinstance(mapping, dict) + one_of_validator = one_of(*mapping, **kwargs) + + def validator(value): + from esphome.yaml_util import make_data_base + + value = make_data_base(one_of_validator(value)) + cls = value.__class__ + value.__class__ = cls.__class__(cls.__name__ + "Enum", (cls, core.EnumValue), {}) + value.enum_value = mapping[value] return value return validator def lambda_(value): + """Coerce this configuration option to a lambda.""" if isinstance(value, Lambda): return value return Lambda(string_strict(value)) @@ -792,23 +980,25 @@ def dimensions(value): def directory(value): value = string(value) - path = CORE.relative_path(value) + path = CORE.relative_config_path(value) if not os.path.exists(path): - raise Invalid(u"Could not find directory '{}'. Please make sure it exists.".format( - path)) + raise Invalid(u"Could not find directory '{}'. Please make sure it exists (full path: {})." + u"".format(path, os.path.abspath(path))) if not os.path.isdir(path): - raise Invalid(u"Path '{}' is not a directory.".format(path)) + raise Invalid(u"Path '{}' is not a directory (full path: {})." + u"".format(path, os.path.abspath(path))) return value def file_(value): value = string(value) - path = CORE.relative_path(value) + path = CORE.relative_config_path(value) if not os.path.exists(path): - raise Invalid(u"Could not find file '{}'. Please make sure it exists.".format( - path)) + raise Invalid(u"Could not find file '{}'. Please make sure it exists (full path: {})." + u"".format(path, os.path.abspath(path))) if not os.path.isfile(path): - raise Invalid(u"Path '{}' is not a file.".format(path)) + raise Invalid(u"Path '{}' is not a file (full path: {})." + u"".format(path, os.path.abspath(path))) return value @@ -816,6 +1006,10 @@ ENTITY_ID_CHARACTERS = 'abcdefghijklmnopqrstuvwxyz0123456789_' def entity_id(value): + """Validate that this option represents a valid Home Assistant entity id. + + Should only be used for 'homeassistant' platforms. + """ value = string_strict(value).lower() if value.count('.') != 1: raise Invalid("Entity ID must have exactly one dot in it") @@ -827,20 +1021,30 @@ def entity_id(value): def extract_keys(schema): + """Extract the names of the keys from the given schema.""" if isinstance(schema, Schema): schema = schema.schema assert isinstance(schema, dict) - keys = list(schema.keys()) + keys = [] + for skey in list(schema.keys()): + if isinstance(skey, string_types): + keys.append(skey) + elif isinstance(skey, vol.Marker) and isinstance(skey.schema, string_types): + keys.append(skey.schema) + else: + raise ValueError() keys.sort() return keys class GenerateID(Optional): + """Mark this key as being an auto-generated ID key.""" def __init__(self, key=CONF_ID): super(GenerateID, self).__init__(key, default=lambda: None) class SplitDefault(Optional): + """Mark this key to have a split default for ESP8266/ESP32.""" def __init__(self, key, esp8266=vol.UNDEFINED, esp32=vol.UNDEFINED): super(SplitDefault, self).__init__(key) self._esp8266_default = vol.default_factory(esp8266) @@ -861,6 +1065,7 @@ class SplitDefault(Optional): class OnlyWith(Optional): + """Set the default value only if the given component is loaded.""" def __init__(self, key, component, default=None): super(OnlyWith, self).__init__(key) self._component = component @@ -878,56 +1083,70 @@ class OnlyWith(Optional): pass -def nameable(*schemas): - def validator(config): - config = All(*schemas)(config) - if CONF_NAME not in config and CONF_ID not in config: - print(config) +def _nameable_validator(config): + if CONF_NAME not in config and CONF_ID not in config: + raise Invalid("At least one of 'id:' or 'name:' is required!") + if CONF_NAME not in config: + id = config[CONF_ID] + if not id.is_manual: raise Invalid("At least one of 'id:' or 'name:' is required!") - if CONF_NAME not in config: - id = config[CONF_ID] - if not id.is_manual: - print(config) - raise Invalid("At least one of 'id:' or 'name:' is required!") - config[CONF_NAME] = id.id - config[CONF_INTERNAL] = True - return config + config[CONF_NAME] = id.id + config[CONF_INTERNAL] = True return config - - return validator + return config -def validate_registry_entry(name, registry, ignore_keys): +def ensure_schema(schema): + if not isinstance(schema, vol.Schema): + return Schema(schema) + return schema + + +def validate_registry_entry(name, registry): + base_schema = ensure_schema(registry.base_schema).extend({ + Optional(CONF_TYPE_ID): valid, + }, extra=ALLOW_EXTRA) + ignore_keys = extract_keys(base_schema) + def validator(value): if isinstance(value, string_types): value = {value: {}} if not isinstance(value, dict): raise Invalid(u"{} must consist of key-value mapping! Got {}" u"".format(name.title(), value)) - item = value.copy() - key = next((x for x in item if x not in ignore_keys), None) + value = base_schema(value) + key = next((x for x in value if x not in ignore_keys), None) if key is None: - raise Invalid(u"Key missing from {}! Got {}".format(name, item)) + raise Invalid(u"Key missing from {}! Got {}".format(name, value)) if key not in registry: - raise vol.Invalid(u"Unable to find {} with the name '{}'".format(name, key)) - key2 = next((x for x in item if x != key and x not in ignore_keys), None) + raise Invalid(u"Unable to find {} with the name '{}'".format(name, key), [key]) + key2 = next((x for x in value if x != key and x not in ignore_keys), None) if key2 is not None: - raise vol.Invalid(u"Cannot have two {0}s in one item. Key '{1}' overrides '{2}'! " - u"Did you forget to indent the block inside the {0}?" - u"".format(name, key, key2)) - validator_ = registry[key][0] - try: - item[key] = validator_(item[key] or {}) - except vol.Invalid as err: - err.prepend([key]) - raise err - return item + raise Invalid(u"Cannot have two {0}s in one item. Key '{1}' overrides '{2}'! " + u"Did you forget to indent the block inside the {0}?" + u"".format(name, key, key2)) + + if value[key] is None: + value[key] = {} + + registry_entry = registry[key] + + with prepend_path([key]): + value[key] = registry_entry.schema(value[key]) + + if registry_entry.type_id is not None: + my_base_schema = base_schema.extend({ + GenerateID(CONF_TYPE_ID): declare_id(registry_entry.type_id) + }) + value = my_base_schema(value) + + return value return validator -def validate_registry(name, registry, ignore_keys): - return ensure_list(validate_registry_entry(name, registry, ignore_keys)) +def validate_registry(name, registry): + return ensure_list(validate_registry_entry(name, registry)) def maybe_simple_value(*validators): @@ -956,6 +1175,7 @@ MQTT_COMPONENT_SCHEMA = Schema({ Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA)), Optional(CONF_INTERNAL): boolean, }) +MQTT_COMPONENT_SCHEMA.add_extra(_nameable_validator) MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({ Optional(CONF_COMMAND_TOPIC): All(requires_component('mqtt'), subscribe_topic), @@ -964,3 +1184,19 @@ MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({ COMPONENT_SCHEMA = Schema({ Optional(CONF_SETUP_PRIORITY): float_ }) + + +def polling_component_schema(default_update_interval): + """Validate that this component represents a PollingComponent with a configurable + update_interval. + + :param default_update_interval: The default update interval to set for the integration. + """ + if default_update_interval is None: + return COMPONENT_SCHEMA.extend({ + Required(CONF_UPDATE_INTERVAL): default_update_interval, + }) + assert isinstance(default_update_interval, string_types) + return COMPONENT_SCHEMA.extend({ + Optional(CONF_UPDATE_INTERVAL, default=default_update_interval): update_interval, + }) diff --git a/esphome/const.py b/esphome/const.py index 5860a724b..623e07c89 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -414,6 +414,7 @@ CONF_TURN_ON_ACTION = 'turn_on_action' CONF_TX_BUFFER_SIZE = 'tx_buffer_size' CONF_TX_PIN = 'tx_pin' CONF_TYPE = 'type' +CONF_TYPE_ID = 'type_id' CONF_UART_ID = 'uart_id' CONF_UID = 'uid' CONF_UNIQUE = 'unique' @@ -448,6 +449,7 @@ ICON_BATTERY = 'mdi:battery' ICON_BRIEFCASE_DOWNLOAD = 'mdi:briefcase-download' ICON_BRIGHTNESS_5 = 'mdi:brightness-5' ICON_CHEMICAL_WEAPON = 'mdi:chemical-weapon' +ICON_EMPTY = '' ICON_FLASH = 'mdi:flash' ICON_FLOWER = 'mdi:flower' ICON_GAS_CYLINDER = 'mdi:gas-cylinder' @@ -460,6 +462,7 @@ ICON_PERIODIC_TABLE_CO2 = 'mdi:periodic-table-co2' ICON_POWER = 'mdi:power' ICON_PULSE = 'mdi:pulse' ICON_RESTART = 'mdi:restart' +ICON_ROTATE_RIGHT = 'mdi:rotate-right' ICON_SCALE = 'mdi:scale' ICON_SCREEN_ROTATION = 'mdi:screen-rotation' ICON_SIGNAL = 'mdi:signal' @@ -473,6 +476,7 @@ UNIT_CELSIUS = u'°C' UNIT_DECIBEL = 'dB' UNIT_DEGREES = u'°' UNIT_DEGREE_PER_SECOND = u'°/s' +UNIT_EMPTY = '' UNIT_HECTOPASCAL = 'hPa' UNIT_KELVIN = 'K' UNIT_LUX = 'lx' @@ -486,6 +490,7 @@ UNIT_PARTS_PER_MILLION = 'ppm' UNIT_PERCENT = '%' UNIT_PULSES_PER_MINUTE = 'pulses/min' UNIT_SECOND = 's' +UNIT_STEPS = 'steps' UNIT_VOLT = 'V' UNIT_WATT = 'W' diff --git a/esphome/core.py b/esphome/core.py index 1f2794aff..408b10e80 100644 --- a/esphome/core.py +++ b/esphome/core.py @@ -2,6 +2,7 @@ import functools import heapq import inspect import logging + import math import os import re @@ -136,18 +137,18 @@ class TimePeriod(object): def __str__(self): if self.microseconds is not None: - return '{} us'.format(self.total_microseconds) + return '{}us'.format(self.total_microseconds) if self.milliseconds is not None: - return '{} ms'.format(self.total_milliseconds) + return '{}ms'.format(self.total_milliseconds) if self.seconds is not None: - return '{} s'.format(self.total_seconds) + return '{}s'.format(self.total_seconds) if self.minutes is not None: - return '{} min'.format(self.total_minutes) + return '{}min'.format(self.total_minutes) if self.hours is not None: - return '{} h'.format(self.total_hours) + return '{}h'.format(self.total_hours) if self.days is not None: - return '{} d'.format(self.total_days) - return '0' + return '{}d'.format(self.total_days) + return '0s' @property def total_microseconds(self): @@ -225,7 +226,11 @@ LAMBDA_PROG = re.compile(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)') class Lambda(object): def __init__(self, value): - self._value = value + # pylint: disable=protected-access + if isinstance(value, Lambda): + self._value = value._value + else: + self._value = value self._parts = None self._requires_ids = None @@ -259,11 +264,14 @@ class Lambda(object): class ID(object): - def __init__(self, id, is_declaration=False, type=None): + def __init__(self, id, is_declaration=False, type=None, is_manual=None): self.id = id - self.is_manual = id is not None + if is_manual is None: + self.is_manual = id is not None + else: + self.is_manual = is_manual self.is_declaration = is_declaration - self.type = type + self.type = type # type: Optional[MockObjClass] def resolve(self, registered_ids): from esphome.config_validation import RESERVED_IDS @@ -292,6 +300,46 @@ class ID(object): def __hash__(self): return hash(self.id) + def copy(self): + return ID(self.id, is_declaration=self.is_declaration, type=self.type, + is_manual=self.is_manual) + + +class DocumentLocation(object): + def __init__(self, document, line, column): + # type: (basestring, int, int) -> None + self.document = document # type: basestring + self.line = line # type: int + self.column = column # type: int + + @classmethod + def from_mark(cls, mark): + return cls( + mark.name, + mark.line, + mark.column + ) + + def __str__(self): + return u'{} {}:{}'.format(self.document, self.line, self.column) + + +class DocumentRange(object): + def __init__(self, start_mark, end_mark): + # type: (DocumentLocation, DocumentLocation) -> None + self.start_mark = start_mark # type: DocumentLocation + self.end_mark = end_mark # type: DocumentLocation + + @classmethod + def from_marks(cls, start_mark, end_mark): + return cls( + DocumentLocation.from_mark(start_mark), + DocumentLocation.from_mark(end_mark) + ) + + def __str__(self): + return u'[{} - {}]'.format(self.start_mark, self.end_mark) + class Define(object): def __init__(self, name, value=None): @@ -354,10 +402,13 @@ def coroutine_with_priority(priority): return func @functools.wraps(func) - def wrapper(*args, **kwargs): + def _wrapper_generator(*args, **kwargs): + instance_id = kwargs.pop('__esphome_coroutine_instance__') if not inspect.isgeneratorfunction(func): # If func is not a generator, return result immediately yield func(*args, **kwargs) + # pylint: disable=protected-access + CORE._remove_coroutine(instance_id) return gen = func(*args, **kwargs) var = None @@ -376,6 +427,18 @@ def coroutine_with_priority(priority): except StopIteration: # Stopping iteration yield var + # pylint: disable=protected-access + CORE._remove_coroutine(instance_id) + + @functools.wraps(func) + def wrapper(*args, **kwargs): + import random + instance_id = random.randint(0, 2**32) + kwargs['__esphome_coroutine_instance__'] = instance_id + gen = _wrapper_generator(*args, **kwargs) + # pylint: disable=protected-access + CORE._add_active_coroutine(instance_id, gen) + return gen # pylint: disable=protected-access wrapper._esphome_coroutine = True @@ -402,6 +465,8 @@ class EsphomeCore(object): def __init__(self): # True if command is run from dashboard self.dashboard = False + # True if command is run from vscode api + self.vscode = False # The name of the node self.name = None # type: str # The relative path to the configuration YAML @@ -434,6 +499,9 @@ class EsphomeCore(object): self.build_flags = set() # type: Set[str] # A set of defines to set for the compile process in esphome/core/defines.h self.defines = set() # type: Set[Define] + # A dictionary of started coroutines, used to warn when a coroutine was not + # awaited. + self.active_coroutines = {} # type: Dict[int, Any] def reset(self): self.dashboard = False @@ -452,6 +520,7 @@ class EsphomeCore(object): self.libraries = set() self.build_flags = set() self.defines = set() + self.active_coroutines = {} @property def address(self): # type: () -> str @@ -463,6 +532,12 @@ class EsphomeCore(object): return None + def _add_active_coroutine(self, instance_id, obj): + self.active_coroutines[instance_id] = obj + + def _remove_coroutine(self, instance_id): + self.active_coroutines.pop(instance_id) + @property def arduino_version(self): # type: () -> str return self.config[CONF_ESPHOME][CONF_ARDUINO_VERSION] @@ -475,7 +550,7 @@ class EsphomeCore(object): def config_filename(self): return os.path.basename(self.config_path) - def relative_path(self, *path): + def relative_config_path(self, *path): path_ = os.path.expanduser(os.path.join(*path)) return os.path.join(self.config_dir, path_) @@ -541,6 +616,14 @@ class EsphomeCore(object): except StopIteration: _LOGGER.debug(" -> finished") + # Print not-awaited coroutines + for obj in self.active_coroutines.values(): + _LOGGER.warning(u"Coroutine '%s' %s was never awaited with 'yield'.", obj.__name__, obj) + _LOGGER.warning(u"Please file a bug report with your configuration.") + if self.active_coroutines: + raise EsphomeError() + self.active_coroutines.clear() + def add(self, expression): from esphome.cpp_generator import Expression, Statement, statement @@ -643,10 +726,21 @@ class EsphomeCore(object): return u'\n'.join(global_code) + u'\n' -class AutoLoad(dict): +class AutoLoad(OrderedDict): pass +class EnumValue(object): + """Special type used by ESPHome to mark enum values for cv.enum.""" + @property + def enum_value(self): + return getattr(self, '_enum_value', None) + + @enum_value.setter + def enum_value(self, value): + setattr(self, '_enum_value', value) + + CORE = EsphomeCore() ConfigType = Dict[str, Any] diff --git a/esphome/core/automation.cpp b/esphome/core/automation.cpp deleted file mode 100644 index 4df8fc974..000000000 --- a/esphome/core/automation.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "esphome/core/automation.h" - -namespace esphome { - -static const char *TAG = "automation"; - -void StartupTrigger::setup() { this->trigger(); } -float StartupTrigger::get_setup_priority() const { - // Run after everything is set up - return this->setup_priority_; -} -StartupTrigger::StartupTrigger(float setup_priority) : setup_priority_(setup_priority) {} - -void ShutdownTrigger::on_shutdown() { this->trigger(); } - -void LoopTrigger::loop() { this->trigger(); } -float LoopTrigger::get_setup_priority() const { return setup_priority::DATA; } - -RangeCondition::RangeCondition() = default; - -bool RangeCondition::check(float x) { - float min = this->min_.value(x); - float max = this->max_.value(x); - if (isnan(min)) { - return x >= max; - } else if (isnan(max)) { - return x >= min; - } else { - return min <= x && x <= max; - } -} - -} // namespace esphome diff --git a/esphome/core/automation.h b/esphome/core/automation.h index 5fac5b888..8f8e86a80 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -13,91 +13,48 @@ namespace esphome { TemplatableValue name##_{}; \ \ public: \ - template void set_##name(V value_) { this->name##_ = value_; } + template void set_##name(V name) { this->name##_ = name; } #define TEMPLATABLE_VALUE(type, name) TEMPLATABLE_VALUE_(type, name) +/** Base class for all automation conditions. + * + * @tparam Ts The template parameters to pass when executing. + */ template class Condition { public: + /// Check whether this condition passes. This condition check must be instant, and not cause any delays. virtual bool check(Ts... x) = 0; - bool check_tuple(const std::tuple &tuple); + /// Call check with a tuple of values as parameter. + bool check_tuple(const std::tuple &tuple) { + return this->check_tuple_(tuple, typename gens::type()); + } protected: - template bool check_tuple_(const std::tuple &tuple, seq); -}; - -template class AndCondition : public Condition { - public: - explicit AndCondition(const std::vector *> &conditions); - bool check(Ts... x) override; - - protected: - std::vector *> conditions_; -}; - -template class OrCondition : public Condition { - public: - explicit OrCondition(const std::vector *> &conditions); - bool check(Ts... x) override; - - protected: - std::vector *> conditions_; -}; - -template class LambdaCondition : public Condition { - public: - explicit LambdaCondition(std::function &&f); - bool check(Ts... x) override; - - protected: - std::function f_; -}; - -class RangeCondition : public Condition { - public: - explicit RangeCondition(); - bool check(float x) override; - - template void set_min(V value) { this->min_ = value; } - template void set_max(V value) { this->max_ = value; } - - protected: - TemplatableValue min_{NAN}; - TemplatableValue max_{NAN}; + template bool check_tuple_(const std::tuple &tuple, seq) { + return this->check(std::get(tuple)...); + } }; template class Automation; template class Trigger { public: - void trigger(Ts... x); - void set_parent(Automation *parent); - void stop(); + void trigger(Ts... x) { + if (this->automation_parent_ == nullptr) + return; + this->automation_parent_->trigger(x...); + } + void set_automation_parent(Automation *automation_parent) { this->automation_parent_ = automation_parent; } + void stop() { + if (this->automation_parent_ == nullptr) + return; + this->automation_parent_->stop(); + } protected: - Automation *parent_{nullptr}; -}; - -class StartupTrigger : public Trigger<>, public Component { - public: - explicit StartupTrigger(float setup_priority = setup_priority::LATE); - void setup() override; - float get_setup_priority() const override; - - protected: - float setup_priority_; -}; - -class ShutdownTrigger : public Trigger<>, public Component { - public: - void on_shutdown() override; -}; - -class LoopTrigger : public Trigger<>, public Component { - public: - void loop() override; - float get_setup_priority() const override; + Automation *automation_parent_{nullptr}; }; template class ActionList; @@ -105,137 +62,87 @@ template class ActionList; template class Action { public: virtual void play(Ts... x) = 0; - void play_next(Ts... x); - virtual void stop(); - void stop_next(); + virtual void play_complex(Ts... x) { + this->play(x...); + this->play_next(x...); + } + void play_next(Ts... x) { + if (this->next_ != nullptr) { + this->next_->play_complex(x...); + } + } + virtual void stop() {} + virtual void stop_complex() { + this->stop(); + this->stop_next(); + } + void stop_next() { + if (this->next_ != nullptr) { + this->next_->stop_complex(); + } + } - void play_next_tuple(const std::tuple &tuple); + void play_next_tuple(const std::tuple &tuple) { + this->play_next_tuple_(tuple, typename gens::type()); + } protected: friend ActionList; - template void play_next_tuple_(const std::tuple &tuple, seq); + template void play_next_tuple_(const std::tuple &tuple, seq) { + this->play_next(std::get(tuple)...); + } Action *next_ = nullptr; }; -template class DelayAction : public Action, public Component { - public: - explicit DelayAction(); - - template void set_delay(V value) { this->delay_ = value; } - void stop() override; - - void play(Ts... x) override; - float get_setup_priority() const override; - - protected: - TemplatableValue delay_{0}; -}; - -template class LambdaAction : public Action { - public: - explicit LambdaAction(std::function &&f); - void play(Ts... x) override; - - protected: - std::function f_; -}; - -template class IfAction : public Action { - public: - explicit IfAction(std::vector *> conditions); - - void add_then(const std::vector *> &actions); - - void add_else(const std::vector *> &actions); - - void play(Ts... x) override; - - void stop() override; - - protected: - std::vector *> conditions_; - ActionList then_; - ActionList else_; -}; - -template class WhileAction : public Action { - public: - WhileAction(const std::vector *> &conditions); - - void add_then(const std::vector *> &actions); - - void play(Ts... x) override; - - void stop() override; - - protected: - std::vector *> conditions_; - ActionList then_; - bool is_running_{false}; -}; - -template class WaitUntilAction : public Action, public Component { - public: - WaitUntilAction(const std::vector *> &conditions); - - void play(Ts... x) override; - - void stop() override; - - void loop() override; - - float get_setup_priority() const override; - - protected: - std::vector *> conditions_; - bool triggered_{false}; - std::tuple var_{}; -}; - -template class UpdateComponentAction : public Action { - public: - UpdateComponentAction(PollingComponent *component); - void play(Ts... x) override; - - protected: - PollingComponent *component_; -}; - template class ActionList { public: - Action *add_action(Action *action); - void add_actions(const std::vector *> &actions); - void play(Ts... x); - void stop(); - bool empty() const; + void add_action(Action *action) { + if (this->actions_end_ == nullptr) { + this->actions_begin_ = action; + } else { + this->actions_end_->next_ = action; + } + this->actions_end_ = action; + } + void add_actions(const std::vector *> &actions) { + for (auto *action : actions) { + this->add_action(action); + } + } + void play(Ts... x) { + if (this->actions_begin_ != nullptr) + this->actions_begin_->play_complex(x...); + } + void play_tuple(const std::tuple &tuple) { this->play_tuple_(tuple, typename gens::type()); } + void stop() { + if (this->actions_begin_ != nullptr) + this->actions_begin_->stop_complex(); + } + bool empty() const { return this->actions_begin_ == nullptr; } protected: + template void play_tuple_(const std::tuple &tuple, seq) { this->play(std::get(tuple)...); } + Action *actions_begin_{nullptr}; Action *actions_end_{nullptr}; }; template class Automation { public: - explicit Automation(Trigger *trigger); + explicit Automation(Trigger *trigger) : trigger_(trigger) { this->trigger_->set_automation_parent(this); } - Condition *add_condition(Condition *condition); - void add_conditions(const std::vector *> &conditions); + Action *add_action(Action *action) { this->actions_.add_action(action); } + void add_actions(const std::vector *> &actions) { this->actions_.add_actions(actions); } - Action *add_action(Action *action); - void add_actions(const std::vector *> &actions); + void stop() { this->actions_.stop(); } - void stop(); - - void trigger(Ts... x); + void trigger(Ts... x) { this->actions_.play(x...); } protected: Trigger *trigger_; - std::vector *> conditions_; ActionList actions_; }; } // namespace esphome - -#include "esphome/core/automation.tcc" diff --git a/esphome/core/automation.tcc b/esphome/core/automation.tcc deleted file mode 100644 index a8ecae7a4..000000000 --- a/esphome/core/automation.tcc +++ /dev/null @@ -1,243 +0,0 @@ -#pragma once -#include "esphome/core/automation.h" - -namespace esphome { - -template bool Condition::check_tuple(const std::tuple &tuple) { - return this->check_tuple_(tuple, typename gens::type()); -} -template -template -bool Condition::check_tuple_(const std::tuple &tuple, seq) { - return this->check(std::get(tuple)...); -} - -template bool AndCondition::check(Ts... x) { - for (auto *condition : this->conditions_) { - if (!condition->check(x...)) - return false; - } - - return true; -} - -template -AndCondition::AndCondition(const std::vector *> &conditions) : conditions_(conditions) {} - -template bool OrCondition::check(Ts... x) { - for (auto *condition : this->conditions_) { - if (condition->check(x...)) - return true; - } - - return false; -} - -template -OrCondition::OrCondition(const std::vector *> &conditions) : conditions_(conditions) {} - -template void Trigger::set_parent(Automation *parent) { this->parent_ = parent; } - -template void Trigger::trigger(Ts... x) { - if (this->parent_ == nullptr) - return; - this->parent_->trigger(x...); -} -template void Trigger::stop() { - if (this->parent_ == nullptr) - return; - this->parent_->stop(); -} - -template void Action::play_next(Ts... x) { - if (this->next_ != nullptr) { - this->next_->play(x...); - } -} -template void Action::stop() { this->stop_next(); } -template void Action::stop_next() { - if (this->next_ != nullptr) { - this->next_->stop(); - } -} -template void Action::play_next_tuple(const std::tuple &tuple) { - this->play_next_tuple_(tuple, typename gens::type()); -} -template -template -void Action::play_next_tuple_(const std::tuple &tuple, seq) { - this->play_next(std::get(tuple)...); -} - -template DelayAction::DelayAction() = default; - -template void DelayAction::play(Ts... x) { - auto f = std::bind(&DelayAction::play_next, this, x...); - this->set_timeout(this->delay_.value(x...), f); -} -template float DelayAction::get_setup_priority() const { return setup_priority::HARDWARE; } -template void DelayAction::stop() { - this->cancel_timeout(""); - this->stop_next(); -} - -template Condition *Automation::add_condition(Condition *condition) { - this->conditions_.push_back(condition); - return condition; -} -template void Automation::add_conditions(const std::vector *> &conditions) { - for (auto *condition : conditions) { - this->add_condition(condition); - } -} -template Automation::Automation(Trigger *trigger) : trigger_(trigger) { - this->trigger_->set_parent(this); -} -template Action *Automation::add_action(Action *action) { - this->actions_.add_action(action); -} -template void Automation::add_actions(const std::vector *> &actions) { - this->actions_.add_actions(actions); -} -template void Automation::trigger(Ts... x) { - for (auto *condition : this->conditions_) { - if (!condition->check(x...)) - return; - } - - this->actions_.play(x...); -} -template void Automation::stop() { this->actions_.stop(); } -template LambdaCondition::LambdaCondition(std::function &&f) : f_(std::move(f)) {} -template bool LambdaCondition::check(Ts... x) { return this->f_(x...); } - -template LambdaAction::LambdaAction(std::function &&f) : f_(std::move(f)) {} -template void LambdaAction::play(Ts... x) { - this->f_(x...); - this->play_next(x...); -} - -template Action *ActionList::add_action(Action *action) { - if (this->actions_end_ == nullptr) { - this->actions_begin_ = action; - } else { - this->actions_end_->next_ = action; - } - return this->actions_end_ = action; -} -template void ActionList::add_actions(const std::vector *> &actions) { - for (auto *action : actions) { - this->add_action(action); - } -} -template void ActionList::play(Ts... x) { - if (this->actions_begin_ != nullptr) - this->actions_begin_->play(x...); -} -template void ActionList::stop() { - if (this->actions_begin_ != nullptr) - this->actions_begin_->stop(); -} -template bool ActionList::empty() const { return this->actions_begin_ == nullptr; } -template -IfAction::IfAction(const std::vector *> conditions) : conditions_(conditions) {} -template void IfAction::play(Ts... x) { - bool res = true; - for (auto *condition : this->conditions_) { - if (!condition->check(x...)) { - res = false; - break; - } - } - if (res) { - if (this->then_.empty()) { - this->play_next(x...); - } else { - this->then_.play(x...); - } - } else { - if (this->else_.empty()) { - this->play_next(x...); - } else { - this->else_.play(x...); - } - } -} -template void IfAction::add_then(const std::vector *> &actions) { - this->then_.add_actions(actions); - this->then_.add_action(new LambdaAction([this](Ts... x) { this->play_next(x...); })); -} -template void IfAction::add_else(const std::vector *> &actions) { - this->else_.add_actions(actions); - this->else_.add_action(new LambdaAction([this](Ts... x) { this->play_next(x...); })); -} -template void IfAction::stop() { - this->then_.stop(); - this->else_.stop(); - this->stop_next(); -} - -template void UpdateComponentAction::play(Ts... x) { - this->component_->update(); - this->play_next(x...); -} - -template -UpdateComponentAction::UpdateComponentAction(PollingComponent *component) : component_(component) {} - -template -WhileAction::WhileAction(const std::vector *> &conditions) : conditions_(conditions) {} - -template void WhileAction::add_then(const std::vector *> &actions) { - this->then_.add_actions(actions); - this->then_.add_action(new LambdaAction([this](Ts... x) { - this->is_running_ = false; - this->play(x...); - })); -} -template void WhileAction::play(Ts... x) { - if (this->is_running_) - return; - - for (auto *condition : this->conditions_) { - if (!condition->check(x...)) { - this->play_next(x...); - return; - } - } - this->is_running_ = true; - this->then_.play(x...); -} -template void WhileAction::stop() { - this->then_.stop(); - this->is_running_ = false; - this->stop_next(); -} - -template -WaitUntilAction::WaitUntilAction(const std::vector *> &conditions) : conditions_(conditions) {} -template void WaitUntilAction::play(Ts... x) { - this->var_ = std::make_tuple(x...); - this->triggered_ = true; - this->loop(); -} -template void WaitUntilAction::stop() { - this->triggered_ = false; - this->stop_next(); -} -template void WaitUntilAction::loop() { - if (!this->triggered_) - return; - - for (auto *condition : this->conditions_) { - if (!condition->check_tuple(this->var_)) { - return; - } - } - - this->triggered_ = false; - this->play_next_tuple(this->var_); -} -template float WaitUntilAction::get_setup_priority() const { return setup_priority::DATA; } - -} // namespace esphome diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h new file mode 100644 index 000000000..53d24cbd8 --- /dev/null +++ b/esphome/core/base_automation.h @@ -0,0 +1,243 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" + +namespace esphome { + +template class AndCondition : public Condition { + public: + explicit AndCondition(const std::vector *> &conditions) : conditions_(conditions) {} + bool check(Ts... x) override { + for (auto *condition : this->conditions_) { + if (!condition->check(x...)) + return false; + } + + return true; + } + + protected: + std::vector *> conditions_; +}; + +template class OrCondition : public Condition { + public: + explicit OrCondition(const std::vector *> &conditions) : conditions_(conditions) {} + bool check(Ts... x) override { + for (auto *condition : this->conditions_) { + if (condition->check(x...)) + return true; + } + + return false; + } + + protected: + std::vector *> conditions_; +}; + +template class NotCondition : public Condition { + public: + explicit NotCondition(Condition *condition) : condition_(condition) {} + bool check(Ts... x) override { return !this->condition_->check(x...); } + + protected: + Condition *condition_; +}; + +template class LambdaCondition : public Condition { + public: + explicit LambdaCondition(std::function &&f) : f_(std::move(f)) {} + bool check(Ts... x) override { return this->f_(x...); } + + protected: + std::function f_; +}; + +class StartupTrigger : public Trigger<>, public Component { + public: + explicit StartupTrigger(float setup_priority) : setup_priority_(setup_priority) {} + void setup() override { this->trigger(); } + float get_setup_priority() const override { return this->setup_priority_; } + + protected: + float setup_priority_; +}; + +class ShutdownTrigger : public Trigger<>, public Component { + public: + void on_shutdown() override { this->trigger(); } +}; + +class LoopTrigger : public Trigger<>, public Component { + public: + void loop() override { this->trigger(); } + float get_setup_priority() const override { return setup_priority::DATA; } +}; + +template class DelayAction : public Action, public Component { + public: + explicit DelayAction() = default; + + TEMPLATABLE_VALUE(uint32_t, delay) + + void stop() override { this->cancel_timeout(""); } + + void play(Ts... x) override { /* ignore - see play_complex */ + } + + void play_complex(Ts... x) override { + auto f = std::bind(&DelayAction::play_next, this, x...); + this->set_timeout(this->delay_.value(x...), f); + } + float get_setup_priority() const override { return setup_priority::HARDWARE; } +}; + +template class LambdaAction : public Action { + public: + explicit LambdaAction(std::function &&f) : f_(std::move(f)) {} + void play(Ts... x) override { this->f_(x...); } + + protected: + std::function f_; +}; + +template class IfAction : public Action { + public: + explicit IfAction(Condition *condition) : condition_(condition) {} + + void add_then(const std::vector *> &actions) { + this->then_.add_actions(actions); + this->then_.add_action(new LambdaAction([this](Ts... x) { this->play_next(x...); })); + } + + void add_else(const std::vector *> &actions) { + this->else_.add_actions(actions); + this->else_.add_action(new LambdaAction([this](Ts... x) { this->play_next(x...); })); + } + + void play(Ts... x) override { /* ignore - see play_complex */ + } + + void play_complex(Ts... x) override { + bool res = this->condition_->check(x...); + if (res) { + if (this->then_.empty()) { + this->play_next(x...); + } else { + this->then_.play(x...); + } + } else { + if (this->else_.empty()) { + this->play_next(x...); + } else { + this->else_.play(x...); + } + } + } + + void stop() override { + this->then_.stop(); + this->else_.stop(); + } + + protected: + Condition *condition_; + ActionList then_; + ActionList else_; +}; + +template class WhileAction : public Action { + public: + WhileAction(Condition *condition) : condition_(condition) {} + + void add_then(const std::vector *> &actions) { + this->then_.add_actions(actions); + this->then_.add_action(new LambdaAction([this](Ts... x) { + if (this->condition_->check_tuple(this->var_)) { + // play again + this->then_.play_tuple(this->var_); + } else { + // condition false, play next + this->play_next_tuple(this->var_); + } + })); + } + + void play(Ts... x) override { /* ignore - see play_complex */ + } + + void play_complex(Ts... x) override { + // Store loop parameters + this->var_ = std::make_tuple(x...); + // Initial condition check + if (!this->condition_->check_tuple(this->var_)) { + // If new condition check failed, stop loop if running + this->then_.stop(); + this->play_next_tuple(this->var_); + return; + } + + this->then_.play_tuple(this->var_); + } + + void stop() override { this->then_.stop(); } + + protected: + Condition *condition_; + ActionList then_; + std::tuple var_{}; +}; + +template class WaitUntilAction : public Action, public Component { + public: + WaitUntilAction(Condition *condition) : condition_(condition) {} + + void play(Ts... x) { /* ignore - see play_complex */ + } + + void play_complex(Ts... x) override { + // Check if we can continue immediately. + if (this->condition_->check(x...)) { + this->triggered_ = false; + this->play_next(x...); + return; + } + this->var_ = std::make_tuple(x...); + this->triggered_ = true; + this->loop(); + } + + void stop() override { this->triggered_ = false; } + + void loop() override { + if (!this->triggered_) + return; + + if (!this->condition_->check_tuple(this->var_)) { + return; + } + + this->triggered_ = false; + this->play_next_tuple(this->var_); + } + + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + Condition *condition_; + bool triggered_{false}; + std::tuple var_{}; +}; + +template class UpdateComponentAction : public Action { + public: + UpdateComponentAction(PollingComponent *component) : component_(component) {} + void play(Ts... x) override { this->component_->update(); } + + protected: + PollingComponent *component_; +}; + +} // namespace esphome diff --git a/esphome/core/component.h b/esphome/core/component.h index 57dd1c08c..60f306ede 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -242,6 +242,8 @@ class Component { */ class PollingComponent : public Component { public: + PollingComponent() : PollingComponent(0) {} + /** Initialize this polling component with the given update interval in ms. * * @param update_interval The update interval in ms. @@ -274,6 +276,7 @@ class PollingComponent : public Component { /// Helper class that enables naming of objects so that it doesn't have to be re-implement every time. class Nameable { public: + Nameable() : Nameable("") {} explicit Nameable(const std::string &name); const std::string &get_name() const; void set_name(const std::string &name); diff --git a/esphome/core/defines.h b/esphome/core/defines.h index a587896fa..af9de1b42 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -3,7 +3,6 @@ #define ESPHOME_VERSION "dev" -#define ESPHOME_LOG_LEVEL 6 #define USE_API #define USE_LOGGER #define USE_BINARY_SENSOR diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 184e8eef2..64a056e9c 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -88,7 +88,7 @@ std::string to_lowercase_underscore(std::string s) { std::string sanitize_string_whitelist(const std::string &s, const std::string &whitelist) { std::string out(s); out.erase(std::remove_if(out.begin(), out.end(), - [&out, &whitelist](const char &c) { return whitelist.find(c) == std::string::npos; }), + [&whitelist](const char &c) { return whitelist.find(c) == std::string::npos; }), out.end()); return out; } diff --git a/esphome/core/preferences.cpp b/esphome/core/preferences.cpp index 1847b1a03..65140bbdc 100644 --- a/esphome/core/preferences.cpp +++ b/esphome/core/preferences.cpp @@ -29,7 +29,7 @@ bool ESPPreferenceObject::load_() { bool valid = this->data_[this->length_words_] == this->calculate_crc_(); - ESP_LOGVV(TAG, "LOAD %zu: valid=%s, 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, // NOLINT + ESP_LOGVV(TAG, "LOAD %u: valid=%s, 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, // NOLINT YESNO(valid), this->data_[0], this->data_[1], this->type_, this->calculate_crc_()); return valid; } @@ -42,7 +42,7 @@ bool ESPPreferenceObject::save_() { this->data_[this->length_words_] = this->calculate_crc_(); if (!this->save_internal_()) return false; - ESP_LOGVV(TAG, "SAVE %zu: 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, // NOLINT + ESP_LOGVV(TAG, "SAVE %u: 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, // NOLINT this->data_[0], this->data_[1], this->type_, this->calculate_crc_()); return true; } diff --git a/esphome/core_config.py b/esphome/core_config.py index a9b394277..ae55ac8d2 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -12,9 +12,8 @@ from esphome.const import ARDUINO_VERSION_ESP32_DEV, ARDUINO_VERSION_ESP8266_DEV CONF_PLATFORMIO_OPTIONS, CONF_PRIORITY, CONF_TRIGGER_ID, \ CONF_ESP8266_RESTORE_FROM_FLASH, __version__, ARDUINO_VERSION_ESP8266_2_3_0, \ ARDUINO_VERSION_ESP8266_2_5_0 -from esphome.core import CORE, EsphomeError, coroutine_with_priority +from esphome.core import CORE, coroutine_with_priority from esphome.pins import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS -from esphome.py_compat import text_type _LOGGER = logging.getLogger(__name__) @@ -42,7 +41,6 @@ def validate_board(value): validate_platform = cv.one_of('ESP32', 'ESP8266', upper=True) - PLATFORMIO_ESP8266_LUT = { '2.5.0': 'espressif8266@2.0.1', '2.4.2': 'espressif8266@1.8.0', @@ -105,50 +103,54 @@ CONFIG_SCHEMA = cv.Schema({ cv.SplitDefault(CONF_BOARD_FLASH_MODE, esp8266='dout'): cv.one_of(*BUILD_FLASH_MODES, lower=True), cv.Optional(CONF_ON_BOOT): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(StartupTrigger), - cv.Optional(CONF_PRIORITY): cv.float_, + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger), + cv.Optional(CONF_PRIORITY, default=600.0): cv.float_, }), cv.Optional(CONF_ON_SHUTDOWN): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(ShutdownTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ShutdownTrigger), }), cv.Optional(CONF_ON_LOOP): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(LoopTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoopTrigger), }), cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(cv.file_), cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict), }) +PRELOAD_CONFIG_SCHEMA = cv.Schema({ + cv.Required(CONF_NAME): cv.valid_name, + cv.Required(CONF_PLATFORM): validate_platform, +}, extra=cv.ALLOW_EXTRA) + +PRELOAD_CONFIG_SCHEMA2 = PRELOAD_CONFIG_SCHEMA.extend({ + cv.Required(CONF_BOARD): validate_board, + cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, +}) + def preload_core_config(config): + core_key = 'esphome' if 'esphomeyaml' in config: _LOGGER.warning("The esphomeyaml section has been renamed to esphome in 1.11.0. " "Please replace 'esphomeyaml:' in your configuration with 'esphome:'.") config[CONF_ESPHOME] = config.pop('esphomeyaml') + core_key = 'esphomeyaml' if CONF_ESPHOME not in config: - raise EsphomeError(u"No esphome section in config") - core_conf = config[CONF_ESPHOME] - if CONF_PLATFORM not in core_conf: - raise EsphomeError("esphome.platform not specified.") - if CONF_BOARD not in core_conf: - raise EsphomeError("esphome.board not specified.") - if CONF_NAME not in core_conf: - raise EsphomeError("esphome.name not specified.") - - try: - CORE.esp_platform = validate_platform(core_conf[CONF_PLATFORM]) - CORE.board = validate_board(core_conf[CONF_BOARD]) - CORE.name = cv.valid_name(core_conf[CONF_NAME]) - CORE.build_path = CORE.relative_path( - cv.string(core_conf.get(CONF_BUILD_PATH, default_build_path()))) - except cv.Invalid as e: - raise EsphomeError(text_type(e)) + raise cv.RequiredFieldInvalid("required key not provided", CONF_ESPHOME) + with cv.prepend_path(core_key): + out = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) + CORE.name = out[CONF_NAME] + CORE.esp_platform = out[CONF_PLATFORM] + with cv.prepend_path(core_key): + out2 = PRELOAD_CONFIG_SCHEMA2(config[CONF_ESPHOME]) + CORE.board = out2[CONF_BOARD] + CORE.build_path = CORE.relative_config_path(out2[CONF_BUILD_PATH]) @coroutine_with_priority(-1000.0) def add_includes(includes): # Add includes at the very end, so that the included files can access global variables for include in includes: - path = CORE.relative_path(include) + path = CORE.relative_config_path(include) res = os.path.relpath(path, CORE.relative_build_path('src')) cg.add_global(cg.RawExpression(u'#include "{}"'.format(res))) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 9334545a5..f6d1b63a0 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -1,3 +1,5 @@ +import inspect + import math # pylint: disable=unused-import, wrong-import-order @@ -5,7 +7,8 @@ from typing import Any, Generator, List, Optional, Tuple, Type, Union, Dict, Cal from esphome.core import ( # noqa CORE, HexInt, ID, Lambda, TimePeriod, TimePeriodMicroseconds, - TimePeriodMilliseconds, TimePeriodMinutes, TimePeriodSeconds, coroutine, Library, Define) + TimePeriodMilliseconds, TimePeriodMinutes, TimePeriodSeconds, coroutine, Library, Define, + EnumValue) from esphome.helpers import cpp_string_escape, indent_all_but_first_and_last from esphome.py_compat import integer_types, string_types, text_type from esphome.util import OrderedDict @@ -249,10 +252,15 @@ def safe_exp( obj # type: Union[Expression, bool, str, unicode, int, long, float, TimePeriod, list] ): # type: (...) -> Expression + """Try to convert obj to an expression by automatically converting native python types to + expressions/literals. + """ from esphome.cpp_types import bool_, float_, int32 if isinstance(obj, Expression): return obj + if isinstance(obj, EnumValue): + return safe_exp(obj.enum_value) if isinstance(obj, bool): return BoolLiteral(obj) if isinstance(obj, string_types): @@ -279,6 +287,12 @@ def safe_exp( return int32 if obj is float: return float_ + if isinstance(obj, ID): + raise ValueError(u"Object {} is an ID. Did you forget to register the variable?" + u"".format(obj)) + if inspect.isgenerator(obj): + raise ValueError(u"Object {} is a coroutine. Did you forget to await the expression with " + u"'yield'?".format(obj)) raise ValueError(u"Object is not an expression", obj) @@ -335,10 +349,20 @@ def statement(expression): # type: (Union[Expression, Statement]) -> Statement def variable(id, # type: ID - rhs, # type: Expression + rhs, # type: SafeExpType type=None # type: MockObj ): # type: (...) -> MockObj + """Declare a new variable (not pointer type) in the code generation. + + :param id: The ID used to declare the variable. + :param rhs: The expression to place on the right hand side of the assignment. + :param type: Manually define a type for the variable, only use this when it's not possible + to do so during config validation phase (for example because of template arguments). + + :returns The new variable as a MockObj. + """ + assert isinstance(id, ID) rhs = safe_exp(rhs) obj = MockObj(id, u'.') if type is not None: @@ -350,10 +374,19 @@ def variable(id, # type: ID def Pvariable(id, # type: ID - rhs, # type: Expression + rhs, # type: SafeExpType type=None # type: MockObj ): # type: (...) -> MockObj + """Declare a new pointer variable in the code generation. + + :param id: The ID used to declare the variable. + :param rhs: The expression to place on the right hand side of the assignment. + :param type: Manually define a type for the variable, only use this when it's not possible + to do so during config validation phase (for example because of template arguments). + + :returns The new variable as a MockObj. + """ rhs = safe_exp(rhs) obj = MockObj(id, u'->') if type is not None: @@ -367,21 +400,35 @@ def Pvariable(id, # type: ID def new_Pvariable(id, # type: ID - *args # type: Tuple[SafeExpType] + *args # type: SafeExpType ): + """Declare a new pointer variable in the code generation by calling it's constructor + with the given arguments. + + :param id: The ID used to declare the variable (also specifies the type). + :param args: The values to pass to the constructor. + + :returns The new variable as a MockObj. + """ + if args and isinstance(args[0], TemplateArguments): + id = id.copy() + id.type = id.type.template(args[0]) + args = args[1:] rhs = id.type.new(*args) return Pvariable(id, rhs) -def add(expression, # type: Union[Expression, Statement] +def add(expression, # type: Union[SafeExpType, Statement] ): # type: (...) -> None + """Add an expression to the codegen setup() storage.""" CORE.add(expression) -def add_global(expression, # type: Union[Expression, Statement] +def add_global(expression, # type: Union[SafeExpType, Statement] ): # type: (...) -> None + """Add an expression to the codegen global storage (above setup()).""" CORE.add_global(expression) @@ -389,12 +436,18 @@ def add_library(name, # type: str version # type: Optional[str] ): # type: (...) -> None + """Add a library to the codegen library storage. + + :param name: The name of the library (for example 'AsyncTCP') + :param version: The version of the library, may be None. + """ CORE.add_library(Library(name, version)) def add_build_flag(build_flag, # type: str ): # type: (...) -> None + """Add a global build flag to the compiler flags.""" CORE.add_build_flag(build_flag) @@ -402,6 +455,10 @@ def add_define(name, # type: str value=None, # type: Optional[SafeExpType] ): # type: (...) -> None + """Add a global define to the auto-generated defines.h file. + + Optionally define a value to set this define to. + """ if value is None: CORE.add_define(Define(name)) else: @@ -410,6 +467,15 @@ def add_define(name, # type: str @coroutine def get_variable(id): # type: (ID) -> Generator[MockObj] + """ + Wait for the given ID to be defined in the code generation and + return it as a MockObj. + + This is a coroutine, you need to await it with a 'yield' expression! + + :param id: The ID to retrieve + :return: The variable as a MockObj. + """ var = yield CORE.get_variable(id) yield var @@ -421,6 +487,17 @@ def process_lambda(value, # type: Lambda return_type=None # type: Optional[SafeExpType] ): # type: (...) -> Generator[LambdaExpression] + """Process the given lambda value into a LambdaExpression. + + This is a coroutine because lambdas can depend on other IDs, + you need to await it with 'yield'! + + :param value: The lambda to process. + :param parameters: The parameters to pass to the Lambda, list of tuples + :param capture: The capture expression for the lambda, usually ''. + :param return_type: The return type of the lambda. + :return: The generated lambda expression. + """ from esphome.components.globals import GlobalsComponent if value is None: @@ -443,6 +520,7 @@ def process_lambda(value, # type: Lambda def is_template(value): + """Return if value is a lambda expression.""" return isinstance(value, Lambda) @@ -452,6 +530,17 @@ def templatable(value, # type: Any output_type, # type: Optional[SafeExpType], to_exp=None # type: Optional[Any] ): + """Generate code for a templatable config option. + + If `value` is a templated value, the lambda expression is returned. + Otherwise the value is returned as-is (optionally process with to_exp). + + :param value: The value to process. + :param args: The arguments for the lambda expression. + :param output_type: The output type of the lambda expression. + :param to_exp: An optional callable to use for converting non-templated values. + :return: The potentially templated value. + """ if is_template(value): lambda_ = yield process_lambda(value, args, return_type=output_type) yield lambda_ @@ -465,6 +554,10 @@ def templatable(value, # type: Any class MockObj(Expression): + """A general expression that can be used to represent any value. + + Mostly consists of magic methods that allow ESPHome's codegen syntax. + """ def __init__(self, base, op=u'.'): self.base = base self.op = op diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index e457d4529..ec0c8f88f 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -1,4 +1,5 @@ -from esphome.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_SETUP_PRIORITY +from esphome.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_SETUP_PRIORITY, \ + CONF_UPDATE_INTERVAL, CONF_TYPE_ID from esphome.core import coroutine from esphome.cpp_generator import RawExpression, add from esphome.cpp_types import App, GPIOPin @@ -6,10 +7,14 @@ from esphome.cpp_types import App, GPIOPin @coroutine def gpio_pin_expression(conf): + """Generate an expression for the given pin option. + + This is a coroutine, you must await it with a 'yield' expression! + """ if conf is None: return from esphome import pins - for key, (_, func) in pins.PIN_SCHEMA_REGISTRY.items(): + for key, (func, _) in pins.PIN_SCHEMA_REGISTRY.items(): if key in conf: yield coroutine(func)(conf) return @@ -21,17 +26,34 @@ def gpio_pin_expression(conf): @coroutine -def register_component(obj, config): +def register_component(var, config): + """Register the given obj as a component. + + This is a coroutine, you must await it with a 'yield' expression! + + :param var: The variable representing the component. + :param config: The configuration for the component. + """ if CONF_SETUP_PRIORITY in config: - add(obj.set_setup_priority(config[CONF_SETUP_PRIORITY])) - add(App.register_component(obj)) + add(var.set_setup_priority(config[CONF_SETUP_PRIORITY])) + if CONF_UPDATE_INTERVAL in config: + add(var.set_update_interval(config[CONF_UPDATE_INTERVAL])) + add(App.register_component(var)) + yield var + + +def extract_registry_entry_config(registry, full_config): + # type: (Registry, ConfigType) -> RegistryEntry + key, config = next((k, v) for k, v in full_config.items() if k in registry) + return registry[key], config @coroutine def build_registry_entry(registry, full_config): - key, config = next((k, v) for k, v in full_config.items() if k in registry) - builder = coroutine(registry[key][1]) - yield builder(config) + registry_entry, config = extract_registry_entry_config(registry, full_config) + type_id = full_config[CONF_TYPE_ID] + builder = registry_entry.coroutine_fun + yield builder(config, type_id) @coroutine diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index 4781faa77..a59137834 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -17,8 +17,6 @@ NAN = global_ns.namespace('NAN') esphome_ns = global_ns # using namespace esphome; App = esphome_ns.App Nameable = esphome_ns.class_('Nameable') -Trigger = esphome_ns.class_('Trigger') -Action = esphome_ns.class_('Action') Component = esphome_ns.class_('Component') ComponentPtr = Component.operator('ptr') PollingComponent = esphome_ns.class_('PollingComponent', Component) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 6c1d9f261..5180b110a 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -68,13 +68,19 @@ def template_args(): def authenticated(func): def decorator(self, *args, **kwargs): - if not self.is_authenticated(): + if not is_authenticated(self): self.redirect(RELATIVE_URL + 'login') return None return func(self, *args, **kwargs) return decorator +def is_authenticated(request_handler): + if USING_HASSIO_AUTH or USING_PASSWORD: + return request_handler.get_secure_cookie('authenticated') == cookie_authenticated_yes + return True + + def bind_config(func): def decorator(self, *args, **kwargs): configuration = self.get_argument('configuration') @@ -89,115 +95,155 @@ def bind_config(func): # pylint: disable=abstract-method class BaseHandler(tornado.web.RequestHandler): - def is_authenticated(self): - if USING_HASSIO_AUTH or USING_PASSWORD: - return self.get_secure_cookie('authenticated') == cookie_authenticated_yes + pass - return True + +def websocket_class(cls): + # pylint: disable=protected-access + if not hasattr(cls, '_message_handlers'): + cls._message_handlers = {} + + for _, method in cls.__dict__.iteritems(): + if hasattr(method, "_message_handler"): + cls._message_handlers[method._message_handler] = method + + return cls + + +def websocket_method(name): + def wrap(fn): + # pylint: disable=protected-access + fn._message_handler = name + return fn + return wrap # pylint: disable=abstract-method, arguments-differ +@websocket_class class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler): def __init__(self, application, request, **kwargs): super(EsphomeCommandWebSocket, self).__init__(application, request, **kwargs) - self.proc = None - self.closed = False + self._proc = None + self._is_closed = False def on_message(self, message): if USING_HASSIO_AUTH or USING_PASSWORD: if self.get_secure_cookie('authenticated') != cookie_authenticated_yes: return - if self.proc is not None: + # Messages are always JSON, 500 when not + json_message = json.loads(message) + type_ = json_message['type'] + # pylint: disable=no-member + handlers = type(self)._message_handlers + if type_ not in handlers: + _LOGGER.warning("Requested unknown message type %s", type_) return - command = self.build_command(message) + + handlers[type_](self, json_message) + + @websocket_method('spawn') + def handle_spawn(self, json_message): + if self._proc is not None: + # spawn can only be called once + return + command = self.build_command(json_message) _LOGGER.info(u"Running command '%s'", ' '.join(shlex_quote(x) for x in command)) - self.proc = tornado.process.Subprocess(command, - stdout=tornado.process.Subprocess.STREAM, - stderr=subprocess.STDOUT) - self.proc.set_exit_callback(self.proc_on_exit) - tornado.ioloop.IOLoop.current().spawn_callback(self.redirect_stream) + self._proc = tornado.process.Subprocess(command, + stdout=tornado.process.Subprocess.STREAM, + stderr=subprocess.STDOUT, + stdin=tornado.process.Subprocess.STREAM) + self._proc.set_exit_callback(self._proc_on_exit) + tornado.ioloop.IOLoop.current().spawn_callback(self._redirect_stdout) + + @property + def is_process_active(self): + return self._proc is not None and self._proc.returncode is None + + @websocket_method('stdin') + def handle_stdin(self, json_message): + if not self.is_process_active: + return + data = json_message['data'] + data = codecs.encode(data, 'utf8', 'replace') + _LOGGER.debug("< stdin: %s", data) + self._proc.stdin.write(data) @tornado.gen.coroutine - def redirect_stream(self): + def _redirect_stdout(self): + if IS_PY2: + reg = '[\n\r]' + else: + reg = b'[\n\r]' + while True: try: - if IS_PY2: - reg = '[\n\r]' - else: - reg = b'[\n\r]' - data = yield self.proc.stdout.read_until_regex(reg) - if not IS_PY2: - data = data.decode('utf-8', 'backslashreplace') + data = yield self._proc.stdout.read_until_regex(reg) except tornado.iostream.StreamClosedError: break - try: - self.write_message({'event': 'line', 'data': data}) - except UnicodeDecodeError: - data = codecs.decode(data, 'utf8', 'replace') - self.write_message({'event': 'line', 'data': data}) + data = codecs.decode(data, 'utf8', 'replace') - def proc_on_exit(self, returncode): - if not self.closed: + _LOGGER.debug("> stdout: %s", data) + self.write_message({'event': 'line', 'data': data}) + + def _proc_on_exit(self, returncode): + if not self._is_closed: + # Check if the proc was not forcibly closed _LOGGER.debug("Process exited with return code %s", returncode) self.write_message({'event': 'exit', 'code': returncode}) def on_close(self): - self.closed = True - if self.proc is not None and self.proc.returncode is None: + # Shutdown proc on WS close + self._is_closed = True + # Check if proc exists (if 'start' has been run) + if self.is_process_active: _LOGGER.debug("Terminating process") - self.proc.proc.terminate() + self._proc.proc.terminate() - def build_command(self, message): + def build_command(self, json_message): raise NotImplementedError class EsphomeLogsHandler(EsphomeCommandWebSocket): - def build_command(self, message): - js = json.loads(message) - config_file = CONFIG_DIR + '/' + js['configuration'] - return ["esphome", "--dashboard", config_file, "logs", '--serial-port', js["port"]] + def build_command(self, json_message): + config_file = os.path.join(CONFIG_DIR, json_message['configuration']) + return ["esphome", "--dashboard", config_file, "logs", '--serial-port', + json_message["port"]] -class EsphomeRunHandler(EsphomeCommandWebSocket): - def build_command(self, message): - js = json.loads(message) - config_file = os.path.join(CONFIG_DIR, js['configuration']) - return ["esphome", "--dashboard", config_file, "run", '--upload-port', js["port"]] +class EsphomeUploadHandler(EsphomeCommandWebSocket): + def build_command(self, json_message): + config_file = os.path.join(CONFIG_DIR, json_message['configuration']) + return ["esphome", "--dashboard", config_file, "run", '--upload-port', + json_message["port"]] class EsphomeCompileHandler(EsphomeCommandWebSocket): - def build_command(self, message): - js = json.loads(message) - config_file = os.path.join(CONFIG_DIR, js['configuration']) + def build_command(self, json_message): + config_file = os.path.join(CONFIG_DIR, json_message['configuration']) return ["esphome", "--dashboard", config_file, "compile"] class EsphomeValidateHandler(EsphomeCommandWebSocket): - def build_command(self, message): - js = json.loads(message) - config_file = os.path.join(CONFIG_DIR, js['configuration']) + def build_command(self, json_message): + config_file = os.path.join(CONFIG_DIR, json_message['configuration']) return ["esphome", "--dashboard", config_file, "config"] class EsphomeCleanMqttHandler(EsphomeCommandWebSocket): - def build_command(self, message): - js = json.loads(message) - config_file = os.path.join(CONFIG_DIR, js['configuration']) + def build_command(self, json_message): + config_file = os.path.join(CONFIG_DIR, json_message['configuration']) return ["esphome", "--dashboard", config_file, "clean-mqtt"] class EsphomeCleanHandler(EsphomeCommandWebSocket): - def build_command(self, message): - js = json.loads(message) - config_file = os.path.join(CONFIG_DIR, js['configuration']) + def build_command(self, json_message): + config_file = os.path.join(CONFIG_DIR, json_message['configuration']) return ["esphome", "--dashboard", config_file, "clean"] -class EsphomeHassConfigHandler(EsphomeCommandWebSocket): - def build_command(self, message): - js = json.loads(message) - config_file = os.path.join(CONFIG_DIR, js['configuration']) - return ["esphome", "--dashboard", config_file, "hass-config"] +class EsphomeVscodeHandler(EsphomeCommandWebSocket): + def build_command(self, json_message): + return ["esphome", "--dashboard", "-q", 'dummy', "vscode"] class SerialPortRequestHandler(BaseHandler): @@ -579,12 +625,12 @@ def make_app(debug=False): (RELATIVE_URL + "", MainRequestHandler), (RELATIVE_URL + "login", LoginHandler), (RELATIVE_URL + "logs", EsphomeLogsHandler), - (RELATIVE_URL + "run", EsphomeRunHandler), + (RELATIVE_URL + "upload", EsphomeUploadHandler), (RELATIVE_URL + "compile", EsphomeCompileHandler), (RELATIVE_URL + "validate", EsphomeValidateHandler), (RELATIVE_URL + "clean-mqtt", EsphomeCleanMqttHandler), (RELATIVE_URL + "clean", EsphomeCleanHandler), - (RELATIVE_URL + "hass-config", EsphomeHassConfigHandler), + (RELATIVE_URL + "vscode", EsphomeVscodeHandler), (RELATIVE_URL + "edit", EditRequestHandler), (RELATIVE_URL + "download.bin", DownloadBinaryRequestHandler), (RELATIVE_URL + "serial-ports", SerialPortRequestHandler), diff --git a/esphome/dashboard/static/esphome.js b/esphome/dashboard/static/esphome.js index bd3d00608..abcfc9004 100644 --- a/esphome/dashboard/static/esphome.js +++ b/esphome/dashboard/static/esphome.js @@ -1,9 +1,18 @@ // Disclaimer: This file was written in a hurry and by someone // who does not know JS at all. This file desperately needs cleanup. + +// ============================= Global Vars ============================= document.addEventListener('DOMContentLoaded', () => { M.AutoInit(document.body); }); +let wsProtocol = "ws:"; +if (window.location.protocol === "https:") { + wsProtocol = 'wss:'; +} +const wsUrl = `${wsProtocol}//${window.location.hostname}:${window.location.port}${relative_url}`; + +// ============================= Color Log Parsing ============================= const initializeColorState = () => { return { bold: false, @@ -170,23 +179,13 @@ const colorReplace = (pre, state, text) => { } } addSpan(text.substring(i)); - scrollToBottomOfElement(pre); + if (pre.scrollTop + 56 >= (pre.scrollHeight - pre.offsetHeight)) { + // at bottom + pre.scrollTop = pre.scrollHeight; + } }; -const removeUpdateAvailable = (filename) => { - const p = document.querySelector(`.update-available[data-node="${filename}"]`); - if (p === undefined) - return; - p.remove(); -}; - -let configuration = ""; -let wsProtocol = "ws:"; -if (window.location.protocol === "https:") { - wsProtocol = 'wss:'; -} -const wsUrl = `${wsProtocol}//${window.location.hostname}:${window.location.port}${relative_url}`; - +// ============================= Online/Offline Status Indicators ============================= let isFetchingPing = false; const fetchPing = () => { if (isFetchingPing) @@ -231,6 +230,7 @@ const fetchPing = () => { setInterval(fetchPing, 2000); fetchPing(); +// ============================= Serial Port Selector ============================= const portSelect = document.querySelector('.nav-wrapper select'); let ports = []; @@ -286,313 +286,239 @@ const getUploadPort = () => { setInterval(fetchSerialPorts, 5000); fetchSerialPorts(true); -const logsModalElem = document.getElementById("modal-logs"); -document.querySelectorAll(".action-show-logs").forEach((showLogs) => { - showLogs.addEventListener('click', (e) => { - configuration = e.target.getAttribute('data-node'); - const modalInstance = M.Modal.getInstance(logsModalElem); - const log = logsModalElem.querySelector(".log"); - log.innerHTML = ""; - const colorState = initializeColorState(); - const stopLogsButton = logsModalElem.querySelector(".stop-logs"); +// ============================= Logs Button ============================= + +class LogModalElem { + constructor({ + name, + onPrepare = (modalElem, config) => {}, + onProcessExit = (modalElem, code) => {}, + onSocketClose = (modalElem) => {}, + dismissible = true, + }) { + this.modalId = `modal-${name}`; + this.actionClass = `action-${name}`; + this.wsUrl = `${wsUrl}${name}`; + this.dismissible = dismissible; + this.activeConfig = null; + + this.modalElem = document.getElementById(this.modalId); + this.logElem = this.modalElem.querySelector('.log'); + this.onPrepare = onPrepare; + this.onProcessExit = onProcessExit; + this.onSocketClose = onSocketClose; + } + + setup() { + const boundOnPress = this._onPress.bind(this); + document.querySelectorAll(`.${this.actionClass}`).forEach((btn) => { + btn.addEventListener('click', boundOnPress); + }); + } + + _setupModalInstance() { + this.modalInstance = M.Modal.getInstance(this.modalElem); + this.modalInstance.options.dismissible = this.dismissible; + this._boundKeydown = this._onKeydown.bind(this); + this.modalInstance.options.onOpenStart = () => { + document.addEventListener('keydown', this._boundKeydown); + }; + this.modalInstance.options.onCloseStart = this._onCloseStart.bind(this); + } + + _onCloseStart() { + document.removeEventListener('keydown', this._boundKeydown); + this.activeSocket.close(); + } + + _onPress(event) { + console.log("_onPress"); + this.activeConfig = event.target.getAttribute('data-node'); + this._setupModalInstance(); + // clear log + this.logElem.innerHTML = ""; + const colorlogState = initializeColorState(); + // prepare modal + this.modalElem.querySelectorAll('.filename').forEach((field) => { + field.innerHTML = this.activeConfig; + }); + this.onPrepare(this.modalElem, this.activeConfig); + document.addEventListener('keydown', this._onKeydown); + let stopped = false; - stopLogsButton.innerHTML = "Stop"; - modalInstance.open(); - const filenameField = logsModalElem.querySelector('.filename'); - filenameField.innerHTML = configuration; + // open modal + this.modalInstance.open(); - const logSocket = new WebSocket(wsUrl + "logs"); - logSocket.addEventListener('message', (event) => { + const socket = new WebSocket(this.wsUrl); + this.activeSocket = socket; + socket.addEventListener('message', (event) => { const data = JSON.parse(event.data); if (data.event === "line") { - colorReplace(log, colorState, data.data); + colorReplace(this.logElem, colorlogState, data.data); } else if (data.event === "exit") { - if (data.code === 0) { - M.toast({html: "Program exited successfully."}); - } else { - M.toast({html: `Program failed with code ${data.code}`}); - } - - stopLogsButton.innerHTML = "Close"; + this.onProcessExit(this.modalElem, data.code); stopped = true; } }); - logSocket.addEventListener('open', () => { - const msg = JSON.stringify({configuration: configuration, port: getUploadPort()}); - logSocket.send(msg); + socket.addEventListener('open', () => { + const msg = JSON.stringify(this.encodeSpawnMessage(this.activeConfig)); + socket.send(msg); }); - logSocket.addEventListener('close', () => { + socket.addEventListener('close', () => { if (!stopped) { - M.toast({html: 'Terminated process.'}); + this.onSocketClose(this.modalElem); } }); - modalInstance.options.onCloseStart = () => { - logSocket.close(); + } + + _onKeydown(event) { + if (event.keyCode === 27) { + this.modalInstance.close(); + } + } + + encodeSpawnMessage(config) { + return { + type: 'spawn', + configuration: config, + port: getUploadPort(), }; - }); + } +} + +const logsModal = new LogModalElem({ + name: "logs", + onPrepare: (modalElem, config) => { + modalElem.querySelector(".stop-logs").innerHTML = "Stop"; + }, + onProcessExit: (modalElem, code) => { + if (code === 0) { + M.toast({html: "Program exited successfully."}); + } else { + M.toast({html: `Program failed with code ${code}`}); + } + modalElem.querySelector(".stop-logs").innerHTML = "Close"; + }, + onSocketClose: (modalElem) => { + M.toast({html: 'Terminated process.'}); + }, }); +logsModal.setup(); -const uploadModalElem = document.getElementById("modal-upload"); - -document.querySelectorAll(".action-upload").forEach((upload) => { - upload.addEventListener('click', (e) => { - configuration = e.target.getAttribute('data-node'); - const modalInstance = M.Modal.getInstance(uploadModalElem); - modalInstance.options['dismissible'] = false; - const log = uploadModalElem.querySelector(".log"); - log.innerHTML = ""; - const colorState = initializeColorState(); - const stopLogsButton = uploadModalElem.querySelector(".stop-logs"); - let stopped = false; - stopLogsButton.innerHTML = "Stop"; - modalInstance.open(); - - const filenameField = uploadModalElem.querySelector('.filename'); - filenameField.innerHTML = configuration; - - const logSocket = new WebSocket(wsUrl + "run"); - logSocket.addEventListener('message', (event) => { - const data = JSON.parse(event.data); - if (data.event === "line") { - colorReplace(log, colorState, data.data); - } else if (data.event === "exit") { - if (data.code === 0) { - M.toast({html: "Program exited successfully."}); - removeUpdateAvailable(configuration); - } else { - M.toast({html: `Program failed with code ${data.code}`}); - } - - stopLogsButton.innerHTML = "Close"; - stopped = true; - } - }); - logSocket.addEventListener('open', () => { - const msg = JSON.stringify({configuration: configuration, port: getUploadPort()}); - logSocket.send(msg); - }); - logSocket.addEventListener('close', () => { - if (!stopped) { - M.toast({html: 'Terminated process.'}); - } - }); - modalInstance.options.onCloseStart = () => { - logSocket.close(); - }; - }); +const uploadModal = new LogModalElem({ + name: 'upload', + onPrepare: (modalElem, config) => { + modalElem.querySelector(".stop-logs").innerHTML = "Stop"; + }, + onProcessExit: (modalElem, code) => { + if (code === 0) { + M.toast({html: "Program exited successfully."}); + } else { + M.toast({html: `Program failed with code ${code}`}); + } + modalElem.querySelector(".stop-logs").innerHTML = "Close"; + }, + onSocketClose: (modalElem) => { + M.toast({html: 'Terminated process.'}); + }, + dismissible: false, }); +uploadModal.setup(); -const validateModalElem = document.getElementById("modal-validate"); - -document.querySelectorAll(".action-validate").forEach((upload) => { - upload.addEventListener('click', (e) => { - configuration = e.target.getAttribute('data-node'); - const modalInstance = M.Modal.getInstance(validateModalElem); - const log = validateModalElem.querySelector(".log"); - log.innerHTML = ""; - const colorState = initializeColorState(); - const stopLogsButton = validateModalElem.querySelector(".stop-logs"); - let stopped = false; - stopLogsButton.innerHTML = "Stop"; - modalInstance.open(); - - const filenameField = validateModalElem.querySelector('.filename'); - filenameField.innerHTML = configuration; - - const logSocket = new WebSocket(wsUrl + "validate"); - logSocket.addEventListener('message', (event) => { - const data = JSON.parse(event.data); - if (data.event === "line") { - colorReplace(log, colorState, data.data); - } else if (data.event === "exit") { - if (data.code === 0) { - M.toast({ - html: `${configuration} is valid 👍`, - displayLength: 5000, - }); - } else { - M.toast({ - html: `${configuration} is invalid 😕`, - displayLength: 5000, - }); - } - - stopLogsButton.innerHTML = "Close"; - stopped = true; - } - }); - logSocket.addEventListener('open', () => { - const msg = JSON.stringify({configuration: configuration}); - logSocket.send(msg); - }); - logSocket.addEventListener('close', () => { - if (!stopped) { - M.toast({html: 'Terminated process.'}); - } - }); - modalInstance.options.onCloseStart = () => { - logSocket.close(); - }; - }); +const validateModal = new LogModalElem({ + name: 'validate', + onPrepare: (modalElem, config) => { + modalElem.querySelector(".stop-logs").innerHTML = "Stop"; + }, + onProcessExit: (modalElem, code) => { + if (code === 0) { + M.toast({ + html: `${configuration} is valid 👍`, + displayLength: 5000, + }); + } else { + M.toast({ + html: `${configuration} is invalid 😕`, + displayLength: 5000, + }); + } + modalElem.querySelector(".stop-logs").innerHTML = "Close"; + }, + onSocketClose: (modalElem) => { + M.toast({html: 'Terminated process.'}); + }, }); +validateModal.setup(); -const compileModalElem = document.getElementById("modal-compile"); -const downloadButton = compileModalElem.querySelector('.download-binary'); - -document.querySelectorAll(".action-compile").forEach((upload) => { - upload.addEventListener('click', (e) => { - configuration = e.target.getAttribute('data-node'); - const modalInstance = M.Modal.getInstance(compileModalElem); - modalInstance.options['dismissible'] = false; - const log = compileModalElem.querySelector(".log"); - log.innerHTML = ""; - const colorState = initializeColorState(); - const stopLogsButton = compileModalElem.querySelector(".stop-logs"); - let stopped = false; - stopLogsButton.innerHTML = "Stop"; +const downloadButton = document.querySelector('.download-binary'); +const compileModal = new LogModalElem({ + name: 'compile', + onPrepare: (modalElem, config) => { + modalElem.querySelector('.stop-logs').innerHTML = "Stop"; downloadButton.classList.add('disabled'); - - modalInstance.open(); - - const filenameField = compileModalElem.querySelector('.filename'); - filenameField.innerHTML = configuration; - - const logSocket = new WebSocket(wsUrl + "compile"); - logSocket.addEventListener('message', (event) => { - const data = JSON.parse(event.data); - if (data.event === "line") { - colorReplace(log, colorState, data.data); - } else if (data.event === "exit") { - if (data.code === 0) { - M.toast({html: "Program exited successfully."}); - downloadButton.classList.remove('disabled'); - } else { - M.toast({html: `Program failed with code ${data.code}`}); - } - - stopLogsButton.innerHTML = "Close"; - stopped = true; - } - }); - logSocket.addEventListener('open', () => { - const msg = JSON.stringify({configuration: configuration}); - logSocket.send(msg); - }); - logSocket.addEventListener('close', () => { - if (!stopped) { - M.toast({html: 'Terminated process.'}); - } - }); - modalInstance.options.onCloseStart = () => { - logSocket.close(); - }; - }); + }, + onProcessExit: (modalElem, code) => { + if (code === 0) { + M.toast({html: "Program exited successfully."}); + downloadButton.classList.remove('disabled'); + } else { + M.toast({html: `Program failed with code ${data.code}`}); + } + modalElem.querySelector(".stop-logs").innerHTML = "Close"; + }, + onSocketClose: (modalElem) => { + M.toast({html: 'Terminated process.'}); + }, + dismissible: false, }); - +compileModal.setup(); downloadButton.addEventListener('click', () => { const link = document.createElement("a"); link.download = name; - link.href = `${relative_url}download.bin?configuration=${encodeURIComponent(configuration)}`; + link.href = `${relative_url}download.bin?configuration=${encodeURIComponent(compileModal.activeConfig)}`; document.body.appendChild(link); link.click(); link.remove(); }); -const cleanMqttModalElem = document.getElementById("modal-clean-mqtt"); - -document.querySelectorAll(".action-clean-mqtt").forEach((btn) => { - btn.addEventListener('click', (e) => { - configuration = e.target.getAttribute('data-node'); - const modalInstance = M.Modal.getInstance(cleanMqttModalElem); - const log = cleanMqttModalElem.querySelector(".log"); - log.innerHTML = ""; - const colorState = initializeColorState(); - const stopLogsButton = cleanMqttModalElem.querySelector(".stop-logs"); - let stopped = false; - stopLogsButton.innerHTML = "Stop"; - modalInstance.open(); - - const filenameField = cleanMqttModalElem.querySelector('.filename'); - filenameField.innerHTML = configuration; - - const logSocket = new WebSocket(wsUrl + "clean-mqtt"); - logSocket.addEventListener('message', (event) => { - const data = JSON.parse(event.data); - if (data.event === "line") { - colorReplace(log, colorState, data.data); - } else if (data.event === "exit") { - stopLogsButton.innerHTML = "Close"; - stopped = true; - } - }); - logSocket.addEventListener('open', () => { - const msg = JSON.stringify({configuration: configuration}); - logSocket.send(msg); - }); - logSocket.addEventListener('close', () => { - if (!stopped) { - M.toast({html: 'Terminated process.'}); - } - }); - modalInstance.options.onCloseStart = () => { - logSocket.close(); - }; - }); +const cleanMqttModal = new LogModalElem({ + name: 'clean-mqtt', + onPrepare: (modalElem, config) => { + modalElem.querySelector('.stop-logs').innerHTML = "Stop"; + }, + onProcessExit: (modalElem, code) => { + modalElem.querySelector(".stop-logs").innerHTML = "Close"; + }, + onSocketClose: (modalElem) => { + M.toast({html: 'Terminated process.'}); + }, }); +cleanMqttModal.setup(); -const cleanModalElem = document.getElementById("modal-clean"); - -document.querySelectorAll(".action-clean").forEach((btn) => { - btn.addEventListener('click', (e) => { - configuration = e.target.getAttribute('data-node'); - const modalInstance = M.Modal.getInstance(cleanModalElem); - const log = cleanModalElem.querySelector(".log"); - log.innerHTML = ""; - const colorState = initializeColorState(); - const stopLogsButton = cleanModalElem.querySelector(".stop-logs"); - let stopped = false; - stopLogsButton.innerHTML = "Stop"; - modalInstance.open(); - - const filenameField = cleanModalElem.querySelector('.filename'); - filenameField.innerHTML = configuration; - - const logSocket = new WebSocket(wsUrl + "clean"); - logSocket.addEventListener('message', (event) => { - const data = JSON.parse(event.data); - if (data.event === "line") { - colorReplace(log, colorState, data.data); - } else if (data.event === "exit") { - if (data.code === 0) { - M.toast({html: "Program exited successfully."}); - downloadButton.classList.remove('disabled'); - } else { - M.toast({html: `Program failed with code ${data.code}`}); - } - stopLogsButton.innerHTML = "Close"; - stopped = true; - } - }); - logSocket.addEventListener('open', () => { - const msg = JSON.stringify({configuration: configuration}); - logSocket.send(msg); - }); - logSocket.addEventListener('close', () => { - if (!stopped) { - M.toast({html: 'Terminated process.'}); - } - }); - modalInstance.options.onCloseStart = () => { - logSocket.close(); - }; - }); +const cleanModal = new LogModalElem({ + name: 'clean', + onPrepare: (modalElem, config) => { + modalElem.querySelector(".stop-logs").innerHTML = "Stop"; + }, + onProcessExit: (modalElem, code) => { + if (code === 0) { + M.toast({html: "Program exited successfully."}); + } else { + M.toast({html: `Program failed with code ${code}`}); + } + modalElem.querySelector(".stop-logs").innerHTML = "Close"; + }, + onSocketClose: (modalElem) => { + M.toast({html: 'Terminated process.'}); + }, }); +cleanModal.setup(); document.querySelectorAll(".action-delete").forEach((btn) => { btn.addEventListener('click', (e) => { - configuration = e.target.getAttribute('data-node'); + let configuration = e.target.getAttribute('data-node'); fetch(`${relative_url}delete?configuration=${configuration}`, { credentials: "same-origin", @@ -620,6 +546,7 @@ document.querySelectorAll(".action-delete").forEach((btn) => { const editModalElem = document.getElementById("modal-editor"); const editorElem = editModalElem.querySelector("#editor"); const editor = ace.edit(editorElem); +let activeEditorConfig = null; editor.setTheme("ace/theme/dreamweaver"); editor.session.setMode("ace/mode/yaml"); editor.session.setOption('useSoftTabs', true); @@ -627,13 +554,13 @@ editor.session.setOption('tabSize', 2); const saveButton = editModalElem.querySelector(".save-button"); const saveEditor = () => { - fetch(`${relative_url}edit?configuration=${configuration}`, { + fetch(`${relative_url}edit?configuration=${activeEditorConfig}`, { credentials: "same-origin", method: "POST", body: editor.getValue() }).then(res => res.text()).then(() => { M.toast({ - html: `Saved ${configuration}` + html: `Saved ${activeEditorConfig}` }); }); }; @@ -649,12 +576,12 @@ saveButton.addEventListener('click', saveEditor); document.querySelectorAll(".action-edit").forEach((btn) => { btn.addEventListener('click', (e) => { - configuration = e.target.getAttribute('data-node'); + activeEditorConfig = e.target.getAttribute('data-node'); const modalInstance = M.Modal.getInstance(editModalElem); const filenameField = editModalElem.querySelector('.filename'); - filenameField.innerHTML = configuration; + filenameField.innerHTML = activeEditorConfig; - fetch(`${relative_url}edit?configuration=${configuration}`, {credentials: "same-origin"}) + fetch(`${relative_url}edit?configuration=${activeEditorConfig}`, {credentials: "same-origin"}) .then(res => res.text()).then(response => { editor.setValue(response, -1); }); @@ -669,10 +596,6 @@ const startWizard = () => { const modalInstance = M.Modal.getInstance(modalSetupElem); modalInstance.open(); - modalInstance.options.onCloseStart = () => { - - }; - $('.stepper').activateStepper({ linearStepsNavigation: false, autoFocusInput: true, @@ -682,15 +605,4 @@ const startWizard = () => { }); }; -const scrollToBottomOfElement = (element) => { - var atBottom = false; - if (element.scrollTop + 30 >= (element.scrollHeight - element.offsetHeight)) { - atBottom = true; - } - - if (atBottom) { - element.scrollTop = element.scrollHeight; - } -} - -setupWizardStart.addEventListener('click', startWizard); \ No newline at end of file +setupWizardStart.addEventListener('click', startWizard); diff --git a/esphome/dashboard/templates/index.html b/esphome/dashboard/templates/index.html index af3dd7a71..74a8d6f72 100644 --- a/esphome/dashboard/templates/index.html +++ b/esphome/dashboard/templates/index.html @@ -86,7 +86,7 @@