diff --git a/esphome/__main__.py b/esphome/__main__.py index 4059024531..27262355d6 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -428,6 +428,8 @@ def parse_args(argv): 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('-s', '--substitution', nargs=2, action='append', + help='Add a substitution', metavar=('key', 'value')) parser.add_argument('configuration', help='Your YAML configuration file.', nargs='*') subparsers = parser.add_subparsers(help='Commands', dest='command') @@ -532,7 +534,7 @@ def run_esphome(argv): CORE.config_path = conf_path CORE.dashboard = args.dashboard - config = read_config() + config = read_config(dict(args.substitution) if args.substitution else {}) if config is None: return 1 CORE.config = config diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index 292a7bf299..ab99e040d4 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -101,11 +101,15 @@ def _substitute_item(substitutions, item, path): return None -def do_substitution_pass(config): - if CONF_SUBSTITUTIONS not in config: +def do_substitution_pass(config, command_line_substitutions): + if CONF_SUBSTITUTIONS not in config and not command_line_substitutions: return substitutions = config[CONF_SUBSTITUTIONS] + if substitutions is None: + substitutions = command_line_substitutions + elif command_line_substitutions: + substitutions = {**substitutions, **command_line_substitutions} with cv.prepend_path('substitutions'): if not isinstance(substitutions, dict): raise cv.Invalid("Substitutions must be a key to value mapping, got {}" diff --git a/esphome/config.py b/esphome/config.py index 8d7c622a27..741b4fb04a 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -387,15 +387,15 @@ def recursive_check_replaceme(value): return value -def validate_config(config): +def validate_config(config, command_line_substitutions): result = Config() # 1. Load substitutions if CONF_SUBSTITUTIONS in config: - result[CONF_SUBSTITUTIONS] = config[CONF_SUBSTITUTIONS] + result[CONF_SUBSTITUTIONS] = {**config[CONF_SUBSTITUTIONS], **command_line_substitutions} result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS) try: - substitutions.do_substitution_pass(config) + substitutions.do_substitution_pass(config, command_line_substitutions) except vol.Invalid as err: result.add_error(err) return result @@ -656,7 +656,7 @@ class InvalidYAMLError(EsphomeError): self.base_exc = base_exc -def _load_config(): +def _load_config(command_line_substitutions): try: config = yaml_util.load_yaml(CORE.config_path) except EsphomeError as e: @@ -664,7 +664,7 @@ def _load_config(): CORE.raw_config = config try: - result = validate_config(config) + result = validate_config(config, command_line_substitutions) except EsphomeError: raise except Exception: @@ -674,9 +674,9 @@ def _load_config(): return result -def load_config(): +def load_config(command_line_substitutions): try: - return _load_config() + return _load_config(command_line_substitutions) except vol.Invalid as err: raise EsphomeError(f"Error while parsing config: {err}") @@ -813,10 +813,10 @@ def strip_default_ids(config): return config -def read_config(): +def read_config(command_line_substitutions): _LOGGER.info("Reading configuration %s...", CORE.config_path) try: - res = load_config() + res = load_config(command_line_substitutions) except EsphomeError as err: _LOGGER.error("Error while reading config: %s", err) return None diff --git a/esphome/vscode.py b/esphome/vscode.py index e8c0b106f7..8782ed6e5c 100644 --- a/esphome/vscode.py +++ b/esphome/vscode.py @@ -66,7 +66,7 @@ def read_config(args): CORE.config_path = data['file'] vs = VSCodeResult() try: - res = load_config() + res = load_config(dict(args.substitution) if args.substitution else {}) except Exception as err: # pylint: disable=broad-except vs.add_yaml_error(str(err)) else: