mirror of
https://github.com/boneIO-eu/esphome.git
synced 2026-04-10 13:00:27 +02:00
453 lines
16 KiB
YAML
453 lines
16 KiB
YAML
name: Build ESPHome Firmware
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "esphome-*"
|
|
workflow_dispatch:
|
|
inputs:
|
|
esphome_version:
|
|
description: "ESPHome version (e.g., 2026.1.5)"
|
|
required: true
|
|
default: "2026.1.5"
|
|
build:
|
|
description: "Build number (e.g., b1)"
|
|
required: true
|
|
default: "b1"
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
# Dimmer LED v0.3
|
|
- yaml_file: boneio-dimmer_8ch-v0_3.yaml
|
|
board_name: "Dimmer LED"
|
|
board_value: "dimmer"
|
|
version: "v0.3"
|
|
version_key: "v0.3"
|
|
output_option: "8 Channels"
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-dimmer_2rgbw-v0_3.yaml
|
|
board_name: "Dimmer LED"
|
|
board_value: "dimmer"
|
|
version: "v0.3"
|
|
version_key: "v0.3"
|
|
output_option: "2xRGBW"
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-dimmer_4cct-v0_3.yaml
|
|
board_name: "Dimmer LED"
|
|
board_value: "dimmer"
|
|
version: "v0.3"
|
|
version_key: "v0.3"
|
|
output_option: "4 CCT"
|
|
chip_family: "ESP32"
|
|
|
|
# Dimmer LED gen2 v0.1
|
|
- yaml_file: boneio-dimmer_gen2_8ch-v0_1.yaml
|
|
board_name: "Dimmer LED gen2"
|
|
board_value: "dimmer_g2"
|
|
version: "v0.1"
|
|
version_key: "v0.1"
|
|
output_option: "8 Channels"
|
|
chip_family: "ESP32-S3"
|
|
- yaml_file: boneio-dimmer_gen2_2rgbw-v0_1.yaml
|
|
board_name: "Dimmer LED gen2"
|
|
board_value: "dimmer_g2"
|
|
version: "v0.1"
|
|
version_key: "v0.1"
|
|
output_option: "2xRGBW"
|
|
chip_family: "ESP32-S3"
|
|
- yaml_file: boneio-dimmer_gen2_4cct-v0_1.yaml
|
|
board_name: "Dimmer LED gen2"
|
|
board_value: "dimmer_g2"
|
|
version: "v0.1"
|
|
version_key: "v0.1"
|
|
output_option: "4 CCT"
|
|
chip_family: "ESP32-S3"
|
|
|
|
# Input24 gen2
|
|
- yaml_file: boneio-input24_gen2-v0_1.yaml
|
|
board_name: "boneIO ESP Input24 gen2"
|
|
board_value: "input24"
|
|
version: "v0.1"
|
|
version_key: "v0.1"
|
|
output_option: "None"
|
|
chip_family: "ESP32-S3"
|
|
|
|
# 8x10A gen2
|
|
- yaml_file: boneio-8x10A_gen2_lights-v0_1.yaml
|
|
board_name: "boneIO ESP 8x10A gen2"
|
|
board_value: "8x10A"
|
|
version: "v0.1"
|
|
version_key: "v0.1"
|
|
output_option: "Lights"
|
|
chip_family: "ESP32-S3"
|
|
- yaml_file: boneio-8x10A_gen2_switches-v0_1.yaml
|
|
board_name: "boneIO ESP 8x10A gen2"
|
|
board_value: "8x10A"
|
|
version: "v0.1"
|
|
version_key: "v0.1"
|
|
output_option: "Switches"
|
|
chip_family: "ESP32-S3"
|
|
|
|
# 32x10 v0.7
|
|
- yaml_file: boneio-32x10_lights_v0_7.yaml
|
|
board_name: "boneIO ESP 32x10"
|
|
board_value: "32x10"
|
|
version: "v0.7 PP"
|
|
version_key: "v0.7"
|
|
output_option: "Lights"
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-32x10_switches_v0_7.yaml
|
|
board_name: "boneIO ESP 32x10"
|
|
board_value: "32x10"
|
|
version: "v0.7 PP"
|
|
version_key: "v0.7"
|
|
output_option: "Switches"
|
|
chip_family: "ESP32"
|
|
|
|
# 32x10 v0.6
|
|
- yaml_file: boneio-32x10_lights_v0_5-v0_6.yaml
|
|
board_name: "boneIO ESP 32x10"
|
|
board_value: "32x10"
|
|
version: "v0.5/v0.6 PP"
|
|
version_key: "v0.6"
|
|
output_option: "Lights"
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-32x10_switches_v0_5-v0_6.yaml
|
|
board_name: "boneIO ESP 32x10"
|
|
board_value: "32x10"
|
|
version: "v0.5/v0.6 PP"
|
|
version_key: "v0.6"
|
|
output_option: "Switches"
|
|
chip_family: "ESP32"
|
|
|
|
# 24x16
|
|
- yaml_file: boneio-24x16_switches_v0_7.yaml
|
|
board_name: "boneIO ESP 24x16"
|
|
board_value: "24x16"
|
|
version: "v0.7 MP"
|
|
version_key: "v0.7"
|
|
output_option: "Switches"
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-24x16_switches_v0_5-v0_6.yaml
|
|
board_name: "boneIO ESP 24x16"
|
|
board_value: "24x16"
|
|
version: "v0.5/v0.6 MP"
|
|
version_key: "v0.6"
|
|
output_option: "Switches"
|
|
chip_family: "ESP32"
|
|
|
|
# Cover
|
|
- yaml_file: boneio-cover_v0_7.yaml
|
|
board_name: "boneIO ESP Cover"
|
|
board_value: "cover"
|
|
version: "v0.7 PP"
|
|
version_key: "v0.7"
|
|
output_option: ""
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-cover_v0_5-v0_6.yaml
|
|
board_name: "boneIO ESP Cover"
|
|
board_value: "cover"
|
|
version: "v0.5/v0.6 PP"
|
|
version_key: "v0.6"
|
|
output_option: ""
|
|
chip_family: "ESP32"
|
|
|
|
# Cover Mix
|
|
- yaml_file: boneio-cover_mix_lights_v0_7.yaml
|
|
board_name: "boneIO ESP Cover Mix"
|
|
board_value: "mix"
|
|
version: "v0.7 PP"
|
|
version_key: "v0.7"
|
|
output_option: "Lights"
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-cover_mix_switches_v0_7.yaml
|
|
board_name: "boneIO ESP Cover Mix"
|
|
board_value: "mix"
|
|
version: "v0.7 PP"
|
|
version_key: "v0.7"
|
|
output_option: "Switches"
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-cover_mix_lights_v0_5-v0_6.yaml
|
|
board_name: "boneIO ESP Cover Mix"
|
|
board_value: "mix"
|
|
version: "v0.5/v0.6 PP"
|
|
version_key: "v0.6"
|
|
output_option: "Lights"
|
|
chip_family: "ESP32"
|
|
- yaml_file: boneio-cover_mix_switches_v0_5-v0_6.yaml
|
|
board_name: "boneIO ESP Cover Mix"
|
|
board_value: "mix"
|
|
version: "v0.5/v0.6 PP"
|
|
version_key: "v0.6"
|
|
output_option: "Switches"
|
|
chip_family: "ESP32"
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Build firmware
|
|
uses: esphome/build-action@v7
|
|
id: esphome-build
|
|
with:
|
|
yaml-file: ${{ matrix.yaml_file }}
|
|
version: latest
|
|
complete-manifest: true
|
|
|
|
- name: Save build metadata
|
|
run: |
|
|
mkdir -p metadata
|
|
cat > metadata/${{ steps.esphome-build.outputs.original-name }}.json << 'METADATA_EOF'
|
|
{
|
|
"firmware_name": "${{ steps.esphome-build.outputs.original-name }}",
|
|
"yaml_file": "${{ matrix.yaml_file }}",
|
|
"board_name": "${{ matrix.board_name }}",
|
|
"board_value": "${{ matrix.board_value }}",
|
|
"version": "${{ matrix.version }}",
|
|
"version_key": "${{ matrix.version_key }}",
|
|
"output_option": "${{ matrix.output_option }}",
|
|
"chip_family": "${{ matrix.chip_family }}"
|
|
}
|
|
METADATA_EOF
|
|
|
|
- name: Upload firmware artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: firmware-${{ steps.esphome-build.outputs.original-name }}
|
|
path: ${{ steps.esphome-build.outputs.name }}
|
|
|
|
- name: Upload metadata artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: metadata-${{ steps.esphome-build.outputs.original-name }}
|
|
path: metadata/
|
|
|
|
deploy:
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
pages: write
|
|
id-token: write
|
|
|
|
steps:
|
|
- name: Download firmware artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: artifacts
|
|
pattern: firmware-*
|
|
merge-multiple: true
|
|
|
|
- name: Download metadata artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: metadata
|
|
pattern: metadata-*
|
|
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
|
|
run: |
|
|
mkdir -p gh-pages/firmware gh-pages/manifests release
|
|
export VERSION="${{ steps.version.outputs.full_version }}"
|
|
export BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
export GITHUB_PAGES_URL="https://boneio-eu.github.io/esphome"
|
|
|
|
# Debug: show artifact structure
|
|
echo "=== Artifacts structure ==="
|
|
find artifacts/ -type f | head -80
|
|
echo "=== End artifacts ==="
|
|
|
|
# Copy and rename firmware binaries
|
|
# esphome/build-action outputs: name-chip/name-chip.factory.bin and name-chip.ota.bin
|
|
for factory_bin in $(find artifacts/ -name '*.factory.bin'); do
|
|
BASENAME=$(basename "$factory_bin")
|
|
# Remove .factory from name: boneio-dr-8ch-03-esp32.factory.bin -> boneio-dr-8ch-03-esp32.bin
|
|
SIMPLE_NAME="${BASENAME/.factory.bin/.bin}"
|
|
cp "$factory_bin" "gh-pages/firmware/${SIMPLE_NAME}"
|
|
echo " Copied to gh-pages/firmware/${SIMPLE_NAME}"
|
|
done
|
|
|
|
# Copy all binaries (factory + ota) to release folder for GitHub Release
|
|
find artifacts/ -name '*.factory.bin' -exec cp {} release/ \;
|
|
find artifacts/ -name '*.ota.bin' -exec cp {} release/ \;
|
|
echo "=== Release files ==="
|
|
ls -la release/
|
|
echo "=== End release ==="
|
|
|
|
# Generate manifest JSONs + firmware-catalog.json from metadata
|
|
python3 << 'PYTHON_EOF'
|
|
import json
|
|
import glob
|
|
import os
|
|
|
|
version = os.environ.get("VERSION", "unknown")
|
|
build_date = os.environ.get("BUILD_DATE", "unknown")
|
|
github_pages_url = os.environ.get("GITHUB_PAGES_URL", "")
|
|
|
|
# Read all metadata files
|
|
builds = []
|
|
for meta_file in glob.glob("metadata/*.json"):
|
|
with open(meta_file) as f:
|
|
builds.append(json.load(f))
|
|
|
|
# Discover actual firmware files on gh-pages to map metadata -> binary
|
|
fw_files = {}
|
|
for f in glob.glob("gh-pages/firmware/*.bin"):
|
|
fw_files[os.path.basename(f)] = f
|
|
|
|
print(f"Found {len(fw_files)} firmware files: {list(fw_files.keys())}")
|
|
|
|
# Generate individual ESP Web Tools manifests
|
|
for build in builds:
|
|
fw_name = build["firmware_name"]
|
|
chip = build["chip_family"]
|
|
chip_suffix = chip.lower().replace("-", "") # ESP32-S3 -> esp32s3
|
|
|
|
# Find matching binary file
|
|
bin_filename = f"{fw_name}-{chip_suffix}.bin"
|
|
if bin_filename not in fw_files:
|
|
# Fallback: try without chip suffix
|
|
bin_filename = f"{fw_name}.bin"
|
|
if bin_filename not in fw_files:
|
|
print(f" WARNING: No binary found for {fw_name} (tried {fw_name}-{chip_suffix}.bin)")
|
|
# List candidates
|
|
candidates = [k for k in fw_files if fw_name in k]
|
|
if candidates:
|
|
bin_filename = candidates[0]
|
|
print(f" Using candidate: {bin_filename}")
|
|
else:
|
|
continue
|
|
|
|
manifest = {
|
|
"name": f'{build["board_name"]} {build["output_option"]} {build["version"]}'.strip(),
|
|
"version": version,
|
|
"builds": [
|
|
{
|
|
"chipFamily": chip,
|
|
"parts": [
|
|
{
|
|
"path": f'{github_pages_url}/firmware/{bin_filename}',
|
|
"offset": 0
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
manifest_path = f'gh-pages/manifests/{fw_name}.json'
|
|
with open(manifest_path, 'w') as f:
|
|
json.dump(manifest, f, indent=2)
|
|
print(f' Generated manifest: {fw_name}.json -> {bin_filename}')
|
|
|
|
# Group by board for catalog
|
|
boards_map = {}
|
|
for build in builds:
|
|
board_value = build["board_value"]
|
|
if board_value not in boards_map:
|
|
boards_map[board_value] = {
|
|
"name": build["board_name"],
|
|
"value": board_value,
|
|
"versions_map": {}
|
|
}
|
|
|
|
version_key = build["version_key"]
|
|
if version_key not in boards_map[board_value]["versions_map"]:
|
|
boards_map[board_value]["versions_map"][version_key] = {
|
|
"version": build["version"],
|
|
"versionKey": version_key,
|
|
"options": []
|
|
}
|
|
|
|
manifest_url = f'{github_pages_url}/manifests/{build["firmware_name"]}.json'
|
|
yaml_url = f'https://github.com/boneIO-eu/esphome/blob/main/{build["yaml_file"]}'
|
|
|
|
option = {
|
|
"name": build["output_option"] if build["output_option"] else "Standard",
|
|
"manifest_url": manifest_url,
|
|
"yaml_url": yaml_url
|
|
}
|
|
boards_map[board_value]["versions_map"][version_key]["options"].append(option)
|
|
|
|
# Convert to final structure
|
|
boards = []
|
|
for board_value, board_data in boards_map.items():
|
|
versions = list(board_data["versions_map"].values())
|
|
boards.append({
|
|
"name": board_data["name"],
|
|
"value": board_data["value"],
|
|
"versions": versions
|
|
})
|
|
|
|
catalog = {
|
|
"version": version,
|
|
"build_date": build_date,
|
|
"boards": boards
|
|
}
|
|
|
|
with open("gh-pages/firmware-catalog.json", "w") as f:
|
|
json.dump(catalog, f, indent=2)
|
|
|
|
print(f"Generated firmware-catalog.json with {len(boards)} boards")
|
|
print(f"Generated {len(builds)} manifest files")
|
|
PYTHON_EOF
|
|
|
|
# Create version file
|
|
echo "$VERSION" > gh-pages/version.txt
|
|
|
|
# Create index page
|
|
cat > gh-pages/index.html << 'EOF'
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head><title>boneIO Firmware</title></head>
|
|
<body>
|
|
<h1>boneIO ESPHome Firmware</h1>
|
|
<p><a href="firmware-catalog.json">Firmware Catalog (JSON API)</a></p>
|
|
<h2>Manifests</h2>
|
|
<ul>
|
|
EOF
|
|
for json_file in gh-pages/manifests/*.json; do
|
|
NAME=$(basename "$json_file" .json)
|
|
echo "<li><a href=\"manifests/${NAME}.json\">${NAME}</a></li>" >> gh-pages/index.html
|
|
done
|
|
cat >> gh-pages/index.html << 'EOF'
|
|
</ul>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
|
|
- name: Deploy to GitHub Pages
|
|
uses: peaceiris/actions-gh-pages@v4
|
|
with:
|
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
publish_dir: ./gh-pages
|
|
force_orphan: true
|
|
|
|
- name: Create GitHub Release
|
|
if: github.event_name == 'push'
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
name: "Firmware ${{ steps.version.outputs.esphome_version }} (${{ steps.version.outputs.build }})"
|
|
files: release/*
|
|
generate_release_notes: true
|