fixes for esphome 2026.4 release

This commit is contained in:
pszafer
2026-04-16 14:16:03 +02:00
parent ad67951090
commit 20bece5e5e
6 changed files with 477 additions and 31 deletions

View File

@@ -0,0 +1,14 @@
---
description: "Use when updating ESPHome versions, firmware GitHub Actions, release tags, validation flows, or package refs in the esphome repository. Covers how to bump to a new ESPHome release, validate configs, and prepare a release tag."
---
# ESPHome Release Update Workflow
- Keep one target ESPHome version across all release-related files. At minimum update `.github/workflows/build-firmware.yml`, `.github/workflows/validate-firmware.yml`, and `create_firmware.py`.
- Never leave the build workflow on `version: latest`. Resolve the version once and pass the exact version into every build step.
- Before editing YAML packages, validate all top-level `boneio-*.yaml` configs against the target image with Docker:
`docker run --rm -v "$PWD":/config ghcr.io/esphome/esphome:<version> config <file>`
- Treat successful validation of all top-level configs as the release gate for ESPHome bumps.
- Preserve the existing release tag format: `esphome-<esphome_version>-<build>`, for example `esphome-2026.4.0-b1`.
- Keep GitHub Release notes explicit: mention the pinned ESPHome version, firmware bundle version, and the GitHub Pages catalog URL.
- Do not modify generated `.esphome/` outputs as part of the version bump unless the task explicitly asks for committed build artifacts.
- If a config fails on the new ESPHome version, fix the root YAML or shared package in `packages/` instead of weakening validation.

View File

@@ -1,5 +1,8 @@
name: Build ESPHome Firmware name: Build ESPHome Firmware
env:
DEFAULT_ESPHOME_VERSION: "2026.4.0"
on: on:
push: push:
tags: tags:
@@ -7,16 +10,47 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
esphome_version: esphome_version:
description: "ESPHome version (e.g., 2026.1.5)" description: "ESPHome version (e.g., 2026.4.0)"
required: true required: true
default: "2026.1.5" default: "2026.4.0"
build: build:
description: "Build number (e.g., b1)" description: "Build number (e.g., b1)"
required: true required: true
default: "b1" default: "b1"
jobs: jobs:
resolve-version:
runs-on: ubuntu-latest
outputs:
esphome_version: ${{ steps.version.outputs.esphome_version }}
build: ${{ steps.version.outputs.build }}
full_version: ${{ steps.version.outputs.full_version }}
release_tag: ${{ steps.version.outputs.release_tag }}
steps:
- name: Resolve release version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
ESPHOME_VER="${{ github.event.inputs.esphome_version }}"
BUILD="${{ github.event.inputs.build }}"
else
TAG="${GITHUB_REF#refs/tags/esphome-}"
ESPHOME_VER=$(echo "$TAG" | sed 's/-b[0-9]*$//')
BUILD=$(echo "$TAG" | grep -oP 'b\d+$' || echo 'b1')
fi
if [ -z "$ESPHOME_VER" ]; then
ESPHOME_VER="${DEFAULT_ESPHOME_VERSION}"
fi
echo "esphome_version=$ESPHOME_VER" >> "$GITHUB_OUTPUT"
echo "build=$BUILD" >> "$GITHUB_OUTPUT"
echo "full_version=${ESPHOME_VER}-${BUILD}" >> "$GITHUB_OUTPUT"
echo "release_tag=esphome-${ESPHOME_VER}-${BUILD}" >> "$GITHUB_OUTPUT"
echo "Version: $ESPHOME_VER, Build: $BUILD"
build: build:
needs: resolve-version
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
@@ -196,7 +230,7 @@ jobs:
id: esphome-build id: esphome-build
with: with:
yaml-file: ${{ matrix.yaml_file }} yaml-file: ${{ matrix.yaml_file }}
version: latest version: ${{ needs.resolve-version.outputs.esphome_version }}
complete-manifest: true complete-manifest: true
- name: Save build metadata - name: Save build metadata
@@ -211,7 +245,8 @@ jobs:
"version": "${{ matrix.version }}", "version": "${{ matrix.version }}",
"version_key": "${{ matrix.version_key }}", "version_key": "${{ matrix.version_key }}",
"output_option": "${{ matrix.output_option }}", "output_option": "${{ matrix.output_option }}",
"chip_family": "${{ matrix.chip_family }}" "chip_family": "${{ matrix.chip_family }}",
"esphome_version": "${{ needs.resolve-version.outputs.esphome_version }}"
} }
METADATA_EOF METADATA_EOF
@@ -228,7 +263,9 @@ jobs:
path: metadata/ path: metadata/
deploy: deploy:
needs: build needs:
- resolve-version
- build
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: write contents: write
@@ -250,27 +287,10 @@ jobs:
pattern: metadata-* pattern: metadata-*
merge-multiple: true merge-multiple: true
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
ESPHOME_VER="${{ github.event.inputs.esphome_version }}"
BUILD="${{ github.event.inputs.build }}"
else
# Tag format: esphome-2026.1.5-b1
TAG="${GITHUB_REF#refs/tags/esphome-}"
ESPHOME_VER=$(echo "$TAG" | sed 's/-b[0-9]*$//')
BUILD=$(echo "$TAG" | grep -oP 'b\d+$' || echo 'b1')
fi
echo "esphome_version=$ESPHOME_VER" >> $GITHUB_OUTPUT
echo "build=$BUILD" >> $GITHUB_OUTPUT
echo "full_version=${ESPHOME_VER}-${BUILD}" >> $GITHUB_OUTPUT
echo "Version: $ESPHOME_VER, Build: $BUILD"
- name: Prepare GitHub Pages content - name: Prepare GitHub Pages content
run: | run: |
mkdir -p gh-pages/firmware gh-pages/manifests release mkdir -p gh-pages/firmware gh-pages/manifests release
export VERSION="${{ steps.version.outputs.full_version }}" export VERSION="${{ needs.resolve-version.outputs.full_version }}"
export BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") export BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
export GITHUB_PAGES_URL="https://boneio-eu.github.io/esphome" export GITHUB_PAGES_URL="https://boneio-eu.github.io/esphome"
@@ -436,6 +456,44 @@ jobs:
</html> </html>
EOF EOF
- name: Generate release notes
run: |
python3 << 'PYTHON_EOF' > release-notes.md
import json
import glob
metadata_files = sorted(glob.glob("metadata/*.json"))
builds = []
for path in metadata_files:
with open(path) as handle:
builds.append(json.load(handle))
esphome_version = "${{ needs.resolve-version.outputs.esphome_version }}"
build_number = "${{ needs.resolve-version.outputs.build }}"
full_version = "${{ needs.resolve-version.outputs.full_version }}"
board_lines = []
seen = set()
for build in builds:
label = f'- {build["board_name"]} {build["version"]} / {build["output_option"] or "Standard"} ({build["chip_family"]})'
if label not in seen:
seen.add(label)
board_lines.append(label)
print(f"# ESPHome {esphome_version} ({build_number})")
print()
print("## Build summary")
print()
print(f"- ESPHome version: `{esphome_version}`")
print(f"- Firmware bundle version: `{full_version}`")
print(f"- Firmware variants built: `{len(builds)}`")
print("- GitHub Pages catalog: `https://boneio-eu.github.io/esphome/firmware-catalog.json`")
print()
print("## Included firmware variants")
print()
print("\n".join(board_lines))
PYTHON_EOF
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4 uses: peaceiris/actions-gh-pages@v4
with: with:
@@ -447,6 +505,6 @@ jobs:
if: github.event_name == 'push' if: github.event_name == 'push'
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
name: "Firmware ${{ steps.version.outputs.esphome_version }} (${{ steps.version.outputs.build }})" name: "Firmware ${{ needs.resolve-version.outputs.esphome_version }} (${{ needs.resolve-version.outputs.build }})"
body_path: release-notes.md
files: release/* files: release/*
generate_release_notes: true

View File

@@ -1,15 +1,20 @@
name: Validate ESPHome Configs name: Validate ESPHome Configs
env:
ESPHOME_VERSION: "2026.4.0"
on: on:
push: push:
branches: [main] branches: [main]
paths: paths:
- "boneio-*.yaml" - "boneio-*.yaml"
- "packages/**" - "packages/**"
- ".github/workflows/validate-firmware.yml"
pull_request: pull_request:
paths: paths:
- "boneio-*.yaml" - "boneio-*.yaml"
- "packages/**" - "packages/**"
- ".github/workflows/validate-firmware.yml"
jobs: jobs:
validate-urls: validate-urls:
@@ -24,14 +29,12 @@ jobs:
for FILE in boneio-*.yaml; do for FILE in boneio-*.yaml; do
BASENAME=$(basename "$FILE") BASENAME=$(basename "$FILE")
# Check package_import_url URL=$(grep 'package_import_url:' "$FILE" 2>/dev/null | awk '{print $2}' | sed "s/['\"]//g")
URL=$(grep 'package_import_url:' "$FILE" 2>/dev/null | awk '{print $2}' | tr -d "'\"")
if [ -z "$URL" ]; then if [ -z "$URL" ]; then
echo "::warning::No package_import_url found in $FILE" echo "::warning::No package_import_url found in $FILE"
continue continue
fi fi
# Extract filename from URL (format: github://org/repo/path/file.yaml@ref)
URL_FILE=$(echo "$URL" | sed 's|.*esphome/||' | sed 's|@.*||') URL_FILE=$(echo "$URL" | sed 's|.*esphome/||' | sed 's|@.*||')
if [ "$URL_FILE" != "$BASENAME" ]; then if [ "$URL_FILE" != "$BASENAME" ]; then
@@ -39,7 +42,6 @@ jobs:
ERRORS=$((ERRORS + 1)) ERRORS=$((ERRORS + 1))
fi fi
# Check for double extensions
if echo "$URL" | grep -q '\.yaml\.yaml'; then if echo "$URL" | grep -q '\.yaml\.yaml'; then
echo "::error file=$FILE::Double .yaml.yaml extension in package_import_url" echo "::error file=$FILE::Double .yaml.yaml extension in package_import_url"
ERRORS=$((ERRORS + 1)) ERRORS=$((ERRORS + 1))
@@ -52,3 +54,19 @@ jobs:
exit 1 exit 1
fi fi
echo "All package_import_url checks passed!" echo "All package_import_url checks passed!"
validate-configs:
name: Validate configs on ESPHome 2026.4.0
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate top-level firmware configs
run: |
set -e
docker pull "ghcr.io/esphome/esphome:${ESPHOME_VERSION}"
for FILE in boneio-*.yaml; do
echo "Validating $FILE with ESPHome ${ESPHOME_VERSION}"
docker run --rm -v "$PWD":/config "ghcr.io/esphome/esphome:${ESPHOME_VERSION}" config "$FILE"
done

View File

@@ -6,7 +6,11 @@ Old instructions: Look for branch with yout input board version to download curr
These are source codes for Esphome software installed on boneIO devices. These are source codes for Esphome software installed on boneIO devices.
Current CI and release workflow is pinned to ESPHome 2026.4.0.
To install it via USB-C cable go to https://boneio.eu/esp and choose board you'd like to install. To install it via USB-C cable go to https://boneio.eu/esp and choose board you'd like to install.
To install it through network, copy file you'd like to use from root directory and paste it in your esphome dashboard. To install it through network, copy file you'd like to use from root directory and paste it in your esphome dashboard.
Set boneio name as visible Set boneio name as visible
Firmware releases are created from tags in the format `esphome-<version>-<build>`, for example `esphome-2026.4.0-b1`.

View File

@@ -0,0 +1,350 @@
substitutions:
name: boneio-8x10a-gen2-01
friendly_name: "boneIO ESP 8x10A Gen2"
serial_prefix: "esp8" #Don't change it.
firmware_manifest: "https://boneio.eu/fwesp/boneio-8x10a-gen2-01.json"
esphome:
name: "${name}"
friendly_name: "${friendly_name}"
name_add_mac_suffix: true
project:
name: boneio.8x10a-gen2
version: "0.1"
on_boot:
priority: -100
then:
- delay: 5s
- if:
condition:
lambda: "return id(serial_no) != nullptr;"
then:
- component.update: serial_no
- logger.log: "Serial No updated on boot"
else:
- logger.log: "Serial No component not found"
devices:
- id: main_device
name: "Main device"
- id: output_01
name: "OUT 01"
- id: output_02
name: "OUT 02"
- id: output_03
name: "OUT 03"
- id: output_04
name: "OUT 04"
- id: output_05
name: "OUT 05"
- id: output_06
name: "OUT 06"
- id: output_07
name: "OUT 07"
- id: output_08
name: "OUT 08"
- id: input_01
name: "IN 01"
- id: input_02
name: "IN 02"
- id: input_03
name: "IN 03"
- id: input_04
name: "IN 04"
- id: input_05
name: "IN 05"
- id: input_06
name: "IN 06"
- id: input_07
name: "IN 07"
- id: input_08
name: "IN 08"
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
ethernet:
id: eth
type: W5500
clk_pin: GPIO13
mosi_pin: GPIO39
miso_pin: GPIO38
cs_pin: GPIO12
interrupt_pin: GPIO2
reset_pin: GPIO1
clock_speed: 25MHz
i2c:
sda: GPIO10
scl: GPIO11
scan: True
frequency: 400kHz
output:
- platform: gpio
pin: GPIO18
inverted: false
id: out_01
- platform: gpio
pin: GPIO17
inverted: false
id: out_02
- platform: gpio
pin: GPIO16
inverted: false
id: out_03
- platform: gpio
pin: GPIO15
inverted: false
id: out_04
- platform: gpio
pin: GPIO7
inverted: false
id: out_05
- platform: gpio
pin: GPIO6
inverted: false
id: out_06
- platform: gpio
pin: GPIO5
inverted: false
id: out_07
- platform: gpio
pin: GPIO4
inverted: false
id: out_08
# CAN gpio48 - tx, gpio47 rx, gpio35 stb
uart:
id: boneio_uart
rx_pin: GPIO21
tx_pin: GPIO14
baud_rate: 9600
stop_bits: 1
modbus:
send_wait_time: 80ms
uart_id: boneio_uart
id: boneio_modbus
packages:
internals_packages:
url: https://github.com/boneIO-eu/esphome
ref: packages-v2.0.0
files: ["packages/devices/serial_no.yaml"]
# boneiopackages:
# url: https://github.com/boneIO-eu/esphome_packages
# ref: main
# refresh: 1min
# files:
# - path: sdm630.yaml
dashboard_import:
package_import_url: github://boneIO-eu/esphome/boneio-8x10A_gen2_lights-v0_1-sd.yaml@latest
import_full_config: true
pcf8574:
- id: "pcf_inputs"
address: 0x38
logger:
hardware_uart: UART0
api:
reboot_timeout: 0s
ota:
- platform: esphome
- platform: web_server
web_server:
port: 80
version: 3
local: true
light:
- platform: binary
output: out_01
name: None
device_id: output_01
id: light_01
- platform: binary
output: out_02
name: "OUT 02A"
device_id: output_02
id: light_02a
- platform: binary
output: out_03
name: None
device_id: output_03
id: light_03
- platform: binary
output: out_04
name: None
device_id: output_04
id: light_04
- platform: binary
output: out_05
name: None
device_id: output_05
id: light_05
- platform: binary
output: out_06
name: None
device_id: output_06
id: light_06
- platform: binary
output: out_07
name: None
device_id: output_07
id: light_07
- platform: binary
output: out_08
name: None
device_id: output_08
id: light_08
debug:
update_interval: 15s
sensor:
- platform: lm75b
id: boneIO_temp
name: "Temperature"
update_interval: 30s
entity_category: diagnostic
on_value_range:
- above: 70.0
then:
- switch.turn_on: buzzer
- below: 70.0
then:
- switch.turn_off: buzzer
switch:
- platform: gpio
id: buzzer
name: "Buzzer"
pin:
number: GPIO9
mode:
output: true
inverted: false
- platform: gpio
id: can_bus_switch
name: "CAN Bus Switch"
pin:
number: GPIO35
mode:
output: true
inverted: false
binary_sensor:
- platform: gpio
name: None
device_id: input_01
id: in_01
pin:
pcf8574: pcf_inputs
number: 0
mode:
input: true
inverted: true
on_press:
then:
- light.toggle: light_01
- platform: gpio
name: None
device_id: input_02
id: in_02
pin:
pcf8574: pcf_inputs
number: 1
mode:
input: true
inverted: true
on_press:
then:
- light.toggle: light_02a
- platform: gpio
name: None
device_id: input_03
id: in_03
pin:
pcf8574: pcf_inputs
number: 2
mode:
input: true
inverted: true
on_press:
then:
- light.toggle: light_03
- platform: gpio
name: None
device_id: input_04
id: in_04
pin:
pcf8574: pcf_inputs
number: 3
mode:
input: true
inverted: true
on_press:
then:
- light.toggle: light_04
- platform: gpio
name: None
device_id: input_05
id: in_05
pin:
pcf8574: pcf_inputs
number: 4
mode:
input: true
inverted: true
on_press:
then:
- light.toggle: light_05
- platform: gpio
name: None
device_id: input_06
id: in_06
pin:
pcf8574: pcf_inputs
number: 5
mode:
input: true
inverted: true
on_press:
then:
- light.toggle: light_06
- platform: gpio
name: None
device_id: input_07
id: in_07
pin:
pcf8574: pcf_inputs
number: 6
mode:
input: true
inverted: true
on_press:
then:
- light.toggle: light_07
- platform: gpio
name: None
device_id: input_08
id: in_08
pin:
pcf8574: pcf_inputs
number: 7
mode:
input: true
inverted: true
on_press:
then:
- light.toggle: light_08

View File

@@ -40,7 +40,9 @@ include_files = [
GITHUB_PAGES_URL = "https://boneio-eu.github.io/esphome" GITHUB_PAGES_URL = "https://boneio-eu.github.io/esphome"
FIRMWARE_VERSION = "2026.1.2" ESPHOME_VERSION = "2026.4.0"
ESPHOME_DOCKER_IMAGE = f"ghcr.io/esphome/esphome:{ESPHOME_VERSION}"
FIRMWARE_VERSION = ESPHOME_VERSION
def json_pattern(firmware_name, chip_family="ESP32"): def json_pattern(firmware_name, chip_family="ESP32"):
@@ -93,7 +95,7 @@ for file in glob.glob("*.yaml"):
print("No file found.") print("No file found.")
break break
firmware_path = f"{cwd}/.esphome/build/{filename}/.pioenvs/{filename}/firmware.factory.bin" firmware_path = f"{cwd}/.esphome/build/{filename}/.pioenvs/{filename}/firmware.factory.bin"
cmd = f'docker run --rm -p 6053:6052 -v "{cwd}":/config -it ghcr.io/esphome/esphome compile {file}' cmd = f'docker run --rm -p 6053:6052 -v "{cwd}":/config -it {ESPHOME_DOCKER_IMAGE} compile {file}'
print(cmd) print(cmd)
result = subprocess.run( result = subprocess.run(
cmd, cmd,