1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-18 19:22:22 +01:00

Merge branch 'fix_clean_build_files_not_removing_platformio_cache' into integration

This commit is contained in:
J. Nick Koston
2025-09-16 10:50:06 -05:00
11 changed files with 196 additions and 32 deletions

View File

@@ -105,6 +105,7 @@ jobs:
script/ci-custom.py script/ci-custom.py
script/build_codeowners.py --check script/build_codeowners.py --check
script/build_language_schema.py --check script/build_language_schema.py --check
script/generate-esp32-boards.py --check
pytest: pytest:
name: Run pytest name: Run pytest

View File

@@ -113,7 +113,7 @@ void ADE7880::update() {
if (this->channel_a_ != nullptr) { if (this->channel_a_ != nullptr) {
auto *chan = this->channel_a_; auto *chan = this->channel_a_;
this->update_sensor_from_s24zp_register16_(chan->current, AIRMS, [](float val) { return val / 100000.0f; }); this->update_sensor_from_s24zp_register16_(chan->current, AIRMS, [](float val) { return val / 100000.0f; });
this->update_sensor_from_s24zp_register16_(chan->voltage, BVRMS, [](float val) { return val / 10000.0f; }); this->update_sensor_from_s24zp_register16_(chan->voltage, AVRMS, [](float val) { return val / 10000.0f; });
this->update_sensor_from_s24zp_register16_(chan->active_power, AWATT, [](float val) { return val / 100.0f; }); this->update_sensor_from_s24zp_register16_(chan->active_power, AWATT, [](float val) { return val / 100.0f; });
this->update_sensor_from_s24zp_register16_(chan->apparent_power, AVA, [](float val) { return val / 100.0f; }); this->update_sensor_from_s24zp_register16_(chan->apparent_power, AVA, [](float val) { return val / 100.0f; });
this->update_sensor_from_s16_register16_(chan->power_factor, APF, this->update_sensor_from_s16_register16_(chan->power_factor, APF,

View File

@@ -2,6 +2,7 @@ import esphome.codegen as cg
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_CLEAR,
CONF_GAIN, CONF_GAIN,
CONF_ID, CONF_ID,
DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_ILLUMINANCE,
@@ -29,7 +30,6 @@ CONF_F5 = "f5"
CONF_F6 = "f6" CONF_F6 = "f6"
CONF_F7 = "f7" CONF_F7 = "f7"
CONF_F8 = "f8" CONF_F8 = "f8"
CONF_CLEAR = "clear"
CONF_NIR = "nir" CONF_NIR = "nir"
UNIT_COUNTS = "#" UNIT_COUNTS = "#"

View File

@@ -1504,6 +1504,10 @@ BOARDS = {
"name": "BPI-Bit", "name": "BPI-Bit",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"bpi-centi-s3": {
"name": "BPI-Centi-S3",
"variant": VARIANT_ESP32S3,
},
"bpi_leaf_s3": { "bpi_leaf_s3": {
"name": "BPI-Leaf-S3", "name": "BPI-Leaf-S3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
@@ -1664,10 +1668,46 @@ BOARDS = {
"name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)", "name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"esp32-s3-devkitc-1-n32r8v": {
"name": "Espressif ESP32-S3-DevKitC-1-N32R8V (32 MB Flash Octal, 8 MB PSRAM Octal)",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitc1-n16r16": {
"name": "Espressif ESP32-S3-DevKitC-1-N16R16V (16 MB Flash Quad, 16 MB PSRAM Octal)",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitc1-n16r2": {
"name": "Espressif ESP32-S3-DevKitC-1-N16R2 (16 MB Flash Quad, 2 MB PSRAM Quad)",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitc1-n16r8": {
"name": "Espressif ESP32-S3-DevKitC-1-N16R8V (16 MB Flash Quad, 8 MB PSRAM Octal)",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitc1-n4r2": {
"name": "Espressif ESP32-S3-DevKitC-1-N4R2 (4 MB Flash Quad, 2 MB PSRAM Quad)",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitc1-n4r8": {
"name": "Espressif ESP32-S3-DevKitC-1-N4R8 (4 MB Flash Quad, 8 MB PSRAM Octal)",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitc1-n8r2": {
"name": "Espressif ESP32-S3-DevKitC-1-N8R2 (8 MB Flash Quad, 2 MB PSRAM quad)",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitc1-n8r8": {
"name": "Espressif ESP32-S3-DevKitC-1-N8R8 (8 MB Flash Quad, 8 MB PSRAM Octal)",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitm-1": { "esp32-s3-devkitm-1": {
"name": "Espressif ESP32-S3-DevKitM-1", "name": "Espressif ESP32-S3-DevKitM-1",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"esp32-s3-fh4r2": {
"name": "Espressif ESP32-S3-FH4R2 (4 MB QD, 2MB PSRAM)",
"variant": VARIANT_ESP32S3,
},
"esp32-solo1": { "esp32-solo1": {
"name": "Espressif Generic ESP32-solo1 4M Flash", "name": "Espressif Generic ESP32-solo1 4M Flash",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@@ -1764,6 +1804,10 @@ BOARDS = {
"name": "Franzininho WiFi MSC", "name": "Franzininho WiFi MSC",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"freenove-esp32-s3-n8r8": {
"name": "Freenove ESP32-S3 WROOM N8R8 (8MB Flash / 8MB PSRAM)",
"variant": VARIANT_ESP32S3,
},
"freenove_esp32_s3_wroom": { "freenove_esp32_s3_wroom": {
"name": "Freenove ESP32-S3 WROOM N8R8 (8MB Flash / 8MB PSRAM)", "name": "Freenove ESP32-S3 WROOM N8R8 (8MB Flash / 8MB PSRAM)",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
@@ -1964,6 +2008,10 @@ BOARDS = {
"name": "M5Stack AtomS3", "name": "M5Stack AtomS3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"m5stack-atoms3u": {
"name": "M5Stack AtomS3U",
"variant": VARIANT_ESP32S3,
},
"m5stack-core-esp32": { "m5stack-core-esp32": {
"name": "M5Stack Core ESP32", "name": "M5Stack Core ESP32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@@ -2084,6 +2132,10 @@ BOARDS = {
"name": "Ai-Thinker NodeMCU-32S2 (ESP-12K)", "name": "Ai-Thinker NodeMCU-32S2 (ESP-12K)",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"nologo_esp32c3_super_mini": {
"name": "Nologo ESP32C3 SuperMini",
"variant": VARIANT_ESP32C3,
},
"nscreen-32": { "nscreen-32": {
"name": "YeaCreate NSCREEN-32", "name": "YeaCreate NSCREEN-32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@@ -2192,6 +2244,10 @@ BOARDS = {
"name": "SparkFun LoRa Gateway 1-Channel", "name": "SparkFun LoRa Gateway 1-Channel",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"sparkfun_pro_micro_esp32c3": {
"name": "SparkFun Pro Micro ESP32-C3",
"variant": VARIANT_ESP32C3,
},
"sparkfun_qwiic_pocket_esp32c6": { "sparkfun_qwiic_pocket_esp32c6": {
"name": "SparkFun ESP32-C6 Qwiic Pocket", "name": "SparkFun ESP32-C6 Qwiic Pocket",
"variant": VARIANT_ESP32C6, "variant": VARIANT_ESP32C6,
@@ -2256,6 +2312,14 @@ BOARDS = {
"name": "Turta IoT Node", "name": "Turta IoT Node",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"um_bling": {
"name": "Unexpected Maker BLING!",
"variant": VARIANT_ESP32S3,
},
"um_edges3_d": {
"name": "Unexpected Maker EDGES3[D]",
"variant": VARIANT_ESP32S3,
},
"um_feathers2": { "um_feathers2": {
"name": "Unexpected Maker FeatherS2", "name": "Unexpected Maker FeatherS2",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
@@ -2268,10 +2332,18 @@ BOARDS = {
"name": "Unexpected Maker FeatherS3", "name": "Unexpected Maker FeatherS3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"um_feathers3_neo": {
"name": "Unexpected Maker FeatherS3 Neo",
"variant": VARIANT_ESP32S3,
},
"um_nanos3": { "um_nanos3": {
"name": "Unexpected Maker NanoS3", "name": "Unexpected Maker NanoS3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"um_omgs3": {
"name": "Unexpected Maker OMGS3",
"variant": VARIANT_ESP32S3,
},
"um_pros3": { "um_pros3": {
"name": "Unexpected Maker PROS3", "name": "Unexpected Maker PROS3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
@@ -2280,6 +2352,14 @@ BOARDS = {
"name": "Unexpected Maker RMP", "name": "Unexpected Maker RMP",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"um_squixl": {
"name": "Unexpected Maker SQUiXL",
"variant": VARIANT_ESP32S3,
},
"um_tinyc6": {
"name": "Unexpected Maker TinyC6",
"variant": VARIANT_ESP32C6,
},
"um_tinys2": { "um_tinys2": {
"name": "Unexpected Maker TinyS2", "name": "Unexpected Maker TinyS2",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
@@ -2401,3 +2481,4 @@ BOARDS = {
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
} }
# DO NOT ADD ANYTHING BELOW THIS LINE

View File

@@ -491,7 +491,7 @@ bool MQTTClientComponent::publish(const std::string &topic, const std::string &p
bool MQTTClientComponent::publish(const std::string &topic, const char *payload, size_t payload_length, uint8_t qos, bool MQTTClientComponent::publish(const std::string &topic, const char *payload, size_t payload_length, uint8_t qos,
bool retain) { bool retain) {
return publish({.topic = topic, .payload = payload, .qos = qos, .retain = retain}); return publish({.topic = topic, .payload = std::string(payload, payload_length), .qos = qos, .retain = retain});
} }
bool MQTTClientComponent::publish(const MQTTMessage &message) { bool MQTTClientComponent::publish(const MQTTMessage &message) {

View File

@@ -186,6 +186,7 @@ CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_CHECK = "check" CONF_CHECK = "check"
CONF_CHIPSET = "chipset" CONF_CHIPSET = "chipset"
CONF_CLEAN_SESSION = "clean_session" CONF_CLEAN_SESSION = "clean_session"
CONF_CLEAR = "clear"
CONF_CLEAR_IMPEDANCE = "clear_impedance" CONF_CLEAR_IMPEDANCE = "clear_impedance"
CONF_CLIENT_CERTIFICATE = "client_certificate" CONF_CLIENT_CERTIFICATE = "client_certificate"
CONF_CLIENT_CERTIFICATE_KEY = "client_certificate_key" CONF_CLIENT_CERTIFICATE_KEY = "client_certificate_key"

View File

@@ -699,6 +699,15 @@ class EsphomeCore:
def relative_piolibdeps_path(self, *path): def relative_piolibdeps_path(self, *path):
return self.relative_build_path(".piolibdeps", *path) return self.relative_build_path(".piolibdeps", *path)
@property
def platformio_cache_dir(self) -> str:
"""Get the PlatformIO cache directory path."""
# Check if running in Docker/HA addon with custom cache dir
if cache_dir := os.environ.get("PLATFORMIO_CACHE_DIR"):
return cache_dir
# Default PlatformIO cache location
return os.path.expanduser("~/.platformio/.cache")
@property @property
def firmware_bin(self): def firmware_bin(self):
if self.is_libretiny: if self.is_libretiny:

View File

@@ -315,6 +315,13 @@ def clean_build():
_LOGGER.info("Deleting %s", dependencies_lock) _LOGGER.info("Deleting %s", dependencies_lock)
os.remove(dependencies_lock) os.remove(dependencies_lock)
# Clean PlatformIO cache to resolve CMake compiler detection issues
# This helps when toolchain paths change or get corrupted
cache_dir = CORE.platformio_cache_dir
if os.path.isdir(cache_dir):
_LOGGER.info("Deleting PlatformIO cache %s", cache_dir)
shutil.rmtree(cache_dir)
GITIGNORE_CONTENT = """# Gitignore settings for ESPHome GITIGNORE_CONTENT = """# Gitignore settings for ESPHome
# This is an example and may include too much for your use-case. # This is an example and may include too much for your use-case.

View File

@@ -1,14 +1,18 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse
import json import json
import os from pathlib import Path
import subprocess import subprocess
import sys
import tempfile import tempfile
from esphome.components.esp32 import ESP_IDF_PLATFORM_VERSION as ver from esphome.components.esp32 import ESP_IDF_PLATFORM_VERSION as ver
from esphome.helpers import write_file_if_changed
version_str = f"{ver.major}.{ver.minor:02d}.{ver.patch:02d}" version_str = f"{ver.major}.{ver.minor:02d}.{ver.patch:02d}"
print(f"ESP32 Platform Version: {version_str}") root = Path(__file__).parent.parent
boards_file_path = root / "esphome" / "components" / "esp32" / "boards.py"
def get_boards(): def get_boards():
@@ -17,6 +21,9 @@ def get_boards():
[ [
"git", "git",
"clone", "clone",
"-q",
"-c",
"advice.detachedHead=false",
"--depth", "--depth",
"1", "1",
"--branch", "--branch",
@@ -26,16 +33,14 @@ def get_boards():
], ],
check=True, check=True,
) )
boards_file = os.path.join(tempdir, "boards") boards_directory = Path(tempdir) / "boards"
boards = {} boards = {}
for fname in os.listdir(boards_file): for fname in boards_directory.glob("*.json"):
if not fname.endswith(".json"): with fname.open(encoding="utf-8") as f:
continue
with open(os.path.join(boards_file, fname), encoding="utf-8") as f:
board_info = json.load(f) board_info = json.load(f)
mcu = board_info["build"]["mcu"] mcu = board_info["build"]["mcu"]
name = board_info["name"] name = board_info["name"]
board = fname[:-5] board = fname.stem
variant = mcu.upper() variant = mcu.upper()
boards[board] = { boards[board] = {
"name": name, "name": name,
@@ -47,33 +52,47 @@ def get_boards():
TEMPLATE = """ "%s": { TEMPLATE = """ "%s": {
"name": "%s", "name": "%s",
"variant": %s, "variant": %s,
}, },"""
"""
def main(): def main(check: bool):
boards = get_boards() boards = get_boards()
# open boards.py, delete existing BOARDS variable and write the new boards dict # open boards.py, delete existing BOARDS variable and write the new boards dict
boards_file_path = os.path.join( existing_content = boards_file_path.read_text(encoding="UTF-8")
os.path.dirname(__file__), "..", "esphome", "components", "esp32", "boards.py"
)
with open(boards_file_path, encoding="UTF-8") as f:
lines = f.readlines()
with open(boards_file_path, "w", encoding="UTF-8") as f: parts: list[str] = []
for line in lines: for line in existing_content.splitlines():
if line.startswith("BOARDS = {"): if line == "BOARDS = {":
f.write("BOARDS = {\n") parts.append(line)
f.writelines( parts.extend(
TEMPLATE % (board, info["name"], info["variant"]) TEMPLATE % (board, info["name"], info["variant"])
for board, info in sorted(boards.items()) for board, info in sorted(boards.items())
) )
f.write("}\n") parts.append("}")
parts.append("# DO NOT ADD ANYTHING BELOW THIS LINE")
break break
f.write(line) parts.append(line)
parts.append("")
content = "\n".join(parts)
if check:
if existing_content != content:
print("boards.py file is not up to date.")
print("Please run `script/generate-esp32-boards.py`")
sys.exit(1)
print("boards.py file is up to date")
elif write_file_if_changed(boards_file_path, content):
print("ESP32 boards updated successfully.")
if __name__ == "__main__": if __name__ == "__main__":
main() parser = argparse.ArgumentParser()
print("ESP32 boards updated successfully.") parser.add_argument(
"--check",
help="Check if the boards.py file is up to date.",
action="store_true",
)
args = parser.parse_args()
main(args.check)

View File

@@ -660,3 +660,37 @@ class TestEsphomeCore:
os.environ.pop("ESPHOME_IS_HA_ADDON", None) os.environ.pop("ESPHOME_IS_HA_ADDON", None)
os.environ.pop("ESPHOME_DATA_DIR", None) os.environ.pop("ESPHOME_DATA_DIR", None)
assert target.data_dir == expected_default assert target.data_dir == expected_default
def test_platformio_cache_dir_with_env_var(self):
"""Test platformio_cache_dir when PLATFORMIO_CACHE_DIR env var is set."""
target = core.EsphomeCore()
test_cache_dir = "/custom/cache/dir"
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": test_cache_dir}):
assert target.platformio_cache_dir == test_cache_dir
def test_platformio_cache_dir_without_env_var(self):
"""Test platformio_cache_dir defaults to ~/.platformio/.cache."""
target = core.EsphomeCore()
with patch.dict(os.environ, {}, clear=True):
# Ensure env var is not set
os.environ.pop("PLATFORMIO_CACHE_DIR", None)
expected = os.path.expanduser("~/.platformio/.cache")
assert target.platformio_cache_dir == expected
def test_platformio_cache_dir_empty_env_var(self):
"""Test platformio_cache_dir with empty env var falls back to default."""
target = core.EsphomeCore()
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": ""}):
expected = os.path.expanduser("~/.platformio/.cache")
assert target.platformio_cache_dir == expected
def test_platformio_cache_dir_docker_addon_path(self):
"""Test platformio_cache_dir in Docker/HA addon environment."""
target = core.EsphomeCore()
addon_cache = "/data/cache/platformio"
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": addon_cache}):
assert target.platformio_cache_dir == addon_cache

View File

@@ -349,15 +349,25 @@ def test_clean_build(
dependencies_lock = tmp_path / "dependencies.lock" dependencies_lock = tmp_path / "dependencies.lock"
dependencies_lock.write_text("lock file") dependencies_lock.write_text("lock file")
# Create PlatformIO cache directory
platformio_cache_dir = tmp_path / ".platformio" / ".cache"
platformio_cache_dir.mkdir(parents=True)
(platformio_cache_dir / "downloads").mkdir()
(platformio_cache_dir / "http").mkdir()
(platformio_cache_dir / "tmp").mkdir()
(platformio_cache_dir / "downloads" / "package.tar.gz").write_text("package")
# Setup mocks # Setup mocks
mock_core.relative_pioenvs_path.return_value = str(pioenvs_dir) mock_core.relative_pioenvs_path.return_value = str(pioenvs_dir)
mock_core.relative_piolibdeps_path.return_value = str(piolibdeps_dir) mock_core.relative_piolibdeps_path.return_value = str(piolibdeps_dir)
mock_core.relative_build_path.return_value = str(dependencies_lock) mock_core.relative_build_path.return_value = str(dependencies_lock)
mock_core.platformio_cache_dir = str(platformio_cache_dir)
# Verify all exist before # Verify all exist before
assert pioenvs_dir.exists() assert pioenvs_dir.exists()
assert piolibdeps_dir.exists() assert piolibdeps_dir.exists()
assert dependencies_lock.exists() assert dependencies_lock.exists()
assert platformio_cache_dir.exists()
# Call the function # Call the function
with caplog.at_level("INFO"): with caplog.at_level("INFO"):
@@ -367,12 +377,14 @@ def test_clean_build(
assert not pioenvs_dir.exists() assert not pioenvs_dir.exists()
assert not piolibdeps_dir.exists() assert not piolibdeps_dir.exists()
assert not dependencies_lock.exists() assert not dependencies_lock.exists()
assert not platformio_cache_dir.exists()
# Verify logging # Verify logging
assert "Deleting" in caplog.text assert "Deleting" in caplog.text
assert ".pioenvs" in caplog.text assert ".pioenvs" in caplog.text
assert ".piolibdeps" in caplog.text assert ".piolibdeps" in caplog.text
assert "dependencies.lock" in caplog.text assert "dependencies.lock" in caplog.text
assert "PlatformIO cache" in caplog.text
@patch("esphome.writer.CORE") @patch("esphome.writer.CORE")