mirror of
https://github.com/esphome/esphome.git
synced 2025-01-09 14:21:46 +01:00
add options vars to remote packages
This commit is contained in:
parent
2cca26ada4
commit
8e213328b5
3 changed files with 72 additions and 43 deletions
|
@ -1,21 +1,23 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome import git, yaml_util
|
from esphome import git, yaml_util
|
||||||
from esphome.config_helpers import merge_config
|
from esphome.config_helpers import merge_config
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ESPHOME,
|
CONF_ESPHOME,
|
||||||
CONF_FILE,
|
CONF_FILE,
|
||||||
CONF_FILES,
|
CONF_FILES,
|
||||||
CONF_MIN_VERSION,
|
CONF_MIN_VERSION,
|
||||||
|
CONF_NAME,
|
||||||
CONF_PACKAGES,
|
CONF_PACKAGES,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_REF,
|
CONF_REF,
|
||||||
CONF_REFRESH,
|
CONF_REFRESH,
|
||||||
CONF_URL,
|
CONF_URL,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
|
CONF_VARS,
|
||||||
|
__version__ as ESPHOME_VERSION,
|
||||||
)
|
)
|
||||||
from esphome.const import __version__ as ESPHOME_VERSION
|
|
||||||
from esphome.core import EsphomeError
|
from esphome.core import EsphomeError
|
||||||
|
|
||||||
DOMAIN = CONF_PACKAGES
|
DOMAIN = CONF_PACKAGES
|
||||||
|
@ -74,7 +76,19 @@ BASE_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_PASSWORD): cv.string,
|
cv.Optional(CONF_PASSWORD): cv.string,
|
||||||
cv.Exclusive(CONF_FILE, "files"): validate_yaml_filename,
|
cv.Exclusive(CONF_FILE, "files"): validate_yaml_filename,
|
||||||
cv.Exclusive(CONF_FILES, "files"): cv.All(
|
cv.Exclusive(CONF_FILES, "files"): cv.All(
|
||||||
cv.ensure_list(validate_yaml_filename),
|
cv.ensure_list(
|
||||||
|
cv.Any(
|
||||||
|
validate_yaml_filename,
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_NAME): validate_yaml_filename,
|
||||||
|
cv.Optional(CONF_VARS, default={}): cv.Schema(
|
||||||
|
{cv.string: cv.string}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
cv.Length(min=1),
|
cv.Length(min=1),
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_REF): cv.git_ref,
|
cv.Optional(CONF_REF): cv.git_ref,
|
||||||
|
@ -106,16 +120,25 @@ def _process_base_package(config: dict) -> dict:
|
||||||
username=config.get(CONF_USERNAME),
|
username=config.get(CONF_USERNAME),
|
||||||
password=config.get(CONF_PASSWORD),
|
password=config.get(CONF_PASSWORD),
|
||||||
)
|
)
|
||||||
files: list[str] = config[CONF_FILES]
|
files = []
|
||||||
|
for file in config[CONF_FILES]:
|
||||||
|
if isinstance(file, str):
|
||||||
|
files.append({CONF_NAME: file, CONF_VARS: {}})
|
||||||
|
else:
|
||||||
|
files.append(file)
|
||||||
|
print(files)
|
||||||
|
|
||||||
def get_packages(files) -> dict:
|
def get_packages(files) -> dict:
|
||||||
packages = {}
|
packages = {}
|
||||||
for file in files:
|
for idx, file in enumerate(files):
|
||||||
yaml_file: Path = repo_dir / file
|
filename = file[CONF_NAME]
|
||||||
|
yaml_file: Path = repo_dir / filename
|
||||||
|
vars = file.get(CONF_VARS)
|
||||||
|
|
||||||
if not yaml_file.is_file():
|
if not yaml_file.is_file():
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
f"{file} does not exist in repository", path=[CONF_FILES]
|
f"{filename} does not exist in repository",
|
||||||
|
path=[CONF_FILES],
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -131,11 +154,13 @@ def _process_base_package(config: dict) -> dict:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
f"Current ESPHome Version is too old to use this package: {ESPHOME_VERSION} < {min_version}"
|
f"Current ESPHome Version is too old to use this package: {ESPHOME_VERSION} < {min_version}"
|
||||||
)
|
)
|
||||||
|
if vars:
|
||||||
packages[file] = new_yaml
|
vars = {k: str(v) for k, v in vars.items()}
|
||||||
|
new_yaml = yaml_util.substitute_vars(new_yaml, vars)
|
||||||
|
packages[f"{filename}{idx}"] = new_yaml
|
||||||
except EsphomeError as e:
|
except EsphomeError as e:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
f"{file} is not a valid YAML file. Please check the file contents.\n{e}"
|
f"{filename} is not a valid YAML file. Please check the file contents.\n{e}"
|
||||||
) from e
|
) from e
|
||||||
return packages
|
return packages
|
||||||
|
|
||||||
|
|
|
@ -916,6 +916,7 @@ CONF_VALUE = "value"
|
||||||
CONF_VALUE_FONT = "value_font"
|
CONF_VALUE_FONT = "value_font"
|
||||||
CONF_VARIABLES = "variables"
|
CONF_VARIABLES = "variables"
|
||||||
CONF_VARIANT = "variant"
|
CONF_VARIANT = "variant"
|
||||||
|
CONF_VARS = "vars"
|
||||||
CONF_VERSION = "version"
|
CONF_VERSION = "version"
|
||||||
CONF_VIBRATIONS = "vibrations"
|
CONF_VIBRATIONS = "vibrations"
|
||||||
CONF_VISIBLE = "visible"
|
CONF_VISIBLE = "visible"
|
||||||
|
|
|
@ -273,48 +273,18 @@ class ESPHomeLoaderMixin:
|
||||||
|
|
||||||
@_add_data_ref
|
@_add_data_ref
|
||||||
def construct_include(self, node):
|
def construct_include(self, node):
|
||||||
|
from esphome.const import CONF_VARS
|
||||||
|
|
||||||
def extract_file_vars(node):
|
def extract_file_vars(node):
|
||||||
fields = self.construct_yaml_map(node)
|
fields = self.construct_yaml_map(node)
|
||||||
file = fields.get("file")
|
file = fields.get("file")
|
||||||
if file is None:
|
if file is None:
|
||||||
raise yaml.MarkedYAMLError("Must include 'file'", node.start_mark)
|
raise yaml.MarkedYAMLError("Must include 'file'", node.start_mark)
|
||||||
vars = fields.get("vars")
|
vars = fields.get(CONF_VARS)
|
||||||
if vars:
|
if vars:
|
||||||
vars = {k: str(v) for k, v in vars.items()}
|
vars = {k: str(v) for k, v in vars.items()}
|
||||||
return file, vars
|
return file, vars
|
||||||
|
|
||||||
def substitute_vars(config, vars):
|
|
||||||
from esphome.components import substitutions
|
|
||||||
from esphome.const import CONF_DEFAULTS, CONF_SUBSTITUTIONS
|
|
||||||
|
|
||||||
org_subs = None
|
|
||||||
result = config
|
|
||||||
if not isinstance(config, dict):
|
|
||||||
# when the included yaml contains a list or a scalar
|
|
||||||
# wrap it into an OrderedDict because do_substitution_pass expects it
|
|
||||||
result = OrderedDict([("yaml", config)])
|
|
||||||
elif CONF_SUBSTITUTIONS in result:
|
|
||||||
org_subs = result.pop(CONF_SUBSTITUTIONS)
|
|
||||||
|
|
||||||
defaults = {}
|
|
||||||
if CONF_DEFAULTS in result:
|
|
||||||
defaults = result.pop(CONF_DEFAULTS)
|
|
||||||
|
|
||||||
result[CONF_SUBSTITUTIONS] = vars
|
|
||||||
for k, v in defaults.items():
|
|
||||||
if k not in result[CONF_SUBSTITUTIONS]:
|
|
||||||
result[CONF_SUBSTITUTIONS][k] = v
|
|
||||||
|
|
||||||
# Ignore missing vars that refer to the top level substitutions
|
|
||||||
substitutions.do_substitution_pass(result, None, ignore_missing=True)
|
|
||||||
result.pop(CONF_SUBSTITUTIONS)
|
|
||||||
|
|
||||||
if not isinstance(config, dict):
|
|
||||||
result = result["yaml"] # unwrap the result
|
|
||||||
elif org_subs:
|
|
||||||
result[CONF_SUBSTITUTIONS] = org_subs
|
|
||||||
return result
|
|
||||||
|
|
||||||
if isinstance(node, yaml.nodes.MappingNode):
|
if isinstance(node, yaml.nodes.MappingNode):
|
||||||
file, vars = extract_file_vars(node)
|
file, vars = extract_file_vars(node)
|
||||||
else:
|
else:
|
||||||
|
@ -432,6 +402,39 @@ def parse_yaml(file_name: str, file_handle: TextIOWrapper) -> Any:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def substitute_vars(config, vars):
|
||||||
|
from esphome.components import substitutions
|
||||||
|
from esphome.const import CONF_DEFAULTS, CONF_SUBSTITUTIONS
|
||||||
|
|
||||||
|
org_subs = None
|
||||||
|
result = config
|
||||||
|
if not isinstance(config, dict):
|
||||||
|
# when the included yaml contains a list or a scalar
|
||||||
|
# wrap it into an OrderedDict because do_substitution_pass expects it
|
||||||
|
result = OrderedDict([("yaml", config)])
|
||||||
|
elif CONF_SUBSTITUTIONS in result:
|
||||||
|
org_subs = result.pop(CONF_SUBSTITUTIONS)
|
||||||
|
|
||||||
|
defaults = {}
|
||||||
|
if CONF_DEFAULTS in result:
|
||||||
|
defaults = result.pop(CONF_DEFAULTS)
|
||||||
|
|
||||||
|
result[CONF_SUBSTITUTIONS] = vars
|
||||||
|
for k, v in defaults.items():
|
||||||
|
if k not in result[CONF_SUBSTITUTIONS]:
|
||||||
|
result[CONF_SUBSTITUTIONS][k] = v
|
||||||
|
|
||||||
|
# Ignore missing vars that refer to the top level substitutions
|
||||||
|
substitutions.do_substitution_pass(result, None, ignore_missing=True)
|
||||||
|
result.pop(CONF_SUBSTITUTIONS)
|
||||||
|
|
||||||
|
if not isinstance(config, dict):
|
||||||
|
result = result["yaml"] # unwrap the result
|
||||||
|
elif org_subs:
|
||||||
|
result[CONF_SUBSTITUTIONS] = org_subs
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _load_yaml_internal(fname: str) -> Any:
|
def _load_yaml_internal(fname: str) -> Any:
|
||||||
"""Load a YAML file."""
|
"""Load a YAML file."""
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in a new issue