[core] Hash downloaded file paths at the default data dir location (#18824)

This commit is contained in:
J. Nick Koston
2026-08-28 21:08:15 -05:00
committed by GitHub
parent 06bc3d70c2
commit 3fea080ed8
4 changed files with 102 additions and 4 deletions
+7 -2
View File
@@ -783,7 +783,8 @@ class EsphomeCore:
can compare a locally computed hash against the one a device
advertises. Machine-local data is kept out of the input: build_path
(which embeds ESPHOME_BUILD_PATH and OS path separators) is excluded,
and Path values are dumped relative to the config directory.
and Path values are dumped relative to the config directory, with
the data directory always at its default ``.esphome`` location.
"""
if self._config_hash is None:
from esphome import yaml_util
@@ -794,11 +795,15 @@ class EsphomeCore:
esphome_conf = dict(esphome_conf)
esphome_conf.pop(CONF_BUILD_PATH, None)
config[CONF_ESPHOME] = esphome_conf
relative_to = data_dir = None
if self.config_path is not None:
relative_to, data_dir = self.config_dir, self.data_dir
config_str = yaml_util.dump(
config,
show_secrets=True,
sort_keys=True,
relative_to=self.config_dir if self.config_path is not None else None,
relative_to=relative_to,
data_dir=data_dir,
)
self._config_hash = fnv1a_32bit_hash(config_str)
return self._config_hash
+20 -2
View File
@@ -1057,11 +1057,19 @@ def _load_yaml_internal_with_type(
loader.dispose()
def dump(dict_, show_secrets=False, sort_keys=False, relative_to: Path | None = None):
def dump(
dict_,
show_secrets=False,
sort_keys=False,
relative_to: Path | None = None,
data_dir: Path | None = None,
):
"""Dump YAML to a string and remove null.
When ``relative_to`` is given, Path values are dumped relative to that
directory (POSIX form) so the output is machine independent.
directory (POSIX form) so the output is machine independent; Path values
under ``data_dir`` are then dumped as ``.esphome/<rest>``. ``data_dir``
has no effect unless ``relative_to`` is also given.
"""
if show_secrets:
_SECRET_VALUES.clear()
@@ -1073,6 +1081,7 @@ def dump(dict_, show_secrets=False, sort_keys=False, relative_to: Path | None =
class _Dumper(ESPHomeDumper):
_redact_sensitive = not show_secrets
_relative_to = relative_to
_data_dir = data_dir
return yaml.dump(
dict_,
@@ -1231,6 +1240,9 @@ class ESPHomeDumper(yaml.SafeDumper):
# directory (in POSIX form) so the output does not depend on where the
# config lives on the machine that produced it.
_relative_to: Path | None = None
# Paths under this directory are dumped as ``.esphome/<rest>`` so the
# add-on's ``/data`` mount matches the CLI layout.
_data_dir: Path | None = None
def represent_mapping(self, tag, mapping, flow_style=None):
value = []
@@ -1274,6 +1286,12 @@ class ESPHomeDumper(yaml.SafeDumper):
# path that still cannot be relativized (e.g. a different drive)
# keeps its POSIX form so separators stay stable across OSes.
path = Path(os.path.normpath(value))
# Checked first: the default data dir sits inside the config dir.
if self._data_dir is not None and path.is_relative_to(
data_dir := os.path.normpath(self._data_dir)
):
rel = Path(".esphome") / path.relative_to(data_dir)
return self.represent_stringify(rel.as_posix())
with suppress(ValueError):
path = path.relative_to(
os.path.normpath(self._relative_to), walk_up=True