diff --git a/.github/instructions/esphome-release.instructions.md b/.github/instructions/esphome-release.instructions.md new file mode 100644 index 0000000..a2b644b --- /dev/null +++ b/.github/instructions/esphome-release.instructions.md @@ -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: config ` +- Treat successful validation of all top-level configs as the release gate for ESPHome bumps. +- Preserve the existing release tag format: `esphome--`, 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. \ No newline at end of file diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml index d5586f3..2f4ca70 100644 --- a/.github/workflows/build-firmware.yml +++ b/.github/workflows/build-firmware.yml @@ -1,5 +1,8 @@ name: Build ESPHome Firmware +env: + DEFAULT_ESPHOME_VERSION: "2026.4.0" + on: push: tags: @@ -7,16 +10,47 @@ on: workflow_dispatch: inputs: esphome_version: - description: "ESPHome version (e.g., 2026.1.5)" + description: "ESPHome version (e.g., 2026.4.0)" required: true - default: "2026.1.5" + default: "2026.4.0" build: description: "Build number (e.g., b1)" required: true default: "b1" 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: + needs: resolve-version runs-on: ubuntu-latest strategy: fail-fast: false @@ -196,7 +230,7 @@ jobs: id: esphome-build with: yaml-file: ${{ matrix.yaml_file }} - version: latest + version: ${{ needs.resolve-version.outputs.esphome_version }} complete-manifest: true - name: Save build metadata @@ -211,7 +245,8 @@ jobs: "version": "${{ matrix.version }}", "version_key": "${{ matrix.version_key }}", "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 @@ -228,7 +263,9 @@ jobs: path: metadata/ deploy: - needs: build + needs: + - resolve-version + - build runs-on: ubuntu-latest permissions: contents: write @@ -250,27 +287,10 @@ jobs: 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 VERSION="${{ needs.resolve-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" @@ -436,6 +456,44 @@ jobs: 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 uses: peaceiris/actions-gh-pages@v4 with: @@ -447,6 +505,6 @@ jobs: if: github.event_name == 'push' uses: softprops/action-gh-release@v2 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/* - generate_release_notes: true diff --git a/.github/workflows/validate-firmware.yml b/.github/workflows/validate-firmware.yml index f3def06..9897fce 100644 --- a/.github/workflows/validate-firmware.yml +++ b/.github/workflows/validate-firmware.yml @@ -1,15 +1,20 @@ name: Validate ESPHome Configs +env: + ESPHOME_VERSION: "2026.4.0" + on: push: branches: [main] paths: - "boneio-*.yaml" - "packages/**" + - ".github/workflows/validate-firmware.yml" pull_request: paths: - "boneio-*.yaml" - "packages/**" + - ".github/workflows/validate-firmware.yml" jobs: validate-urls: @@ -24,14 +29,12 @@ jobs: for FILE in boneio-*.yaml; do BASENAME=$(basename "$FILE") - # Check package_import_url - URL=$(grep 'package_import_url:' "$FILE" 2>/dev/null | awk '{print $2}' | tr -d "'\"") + URL=$(grep 'package_import_url:' "$FILE" 2>/dev/null | awk '{print $2}' | sed "s/['\"]//g") if [ -z "$URL" ]; then echo "::warning::No package_import_url found in $FILE" continue fi - # Extract filename from URL (format: github://org/repo/path/file.yaml@ref) URL_FILE=$(echo "$URL" | sed 's|.*esphome/||' | sed 's|@.*||') if [ "$URL_FILE" != "$BASENAME" ]; then @@ -39,7 +42,6 @@ jobs: ERRORS=$((ERRORS + 1)) fi - # Check for double extensions if echo "$URL" | grep -q '\.yaml\.yaml'; then echo "::error file=$FILE::Double .yaml.yaml extension in package_import_url" ERRORS=$((ERRORS + 1)) @@ -52,3 +54,19 @@ jobs: exit 1 fi 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 diff --git a/README.md b/README.md index 03af5c8..fb5d825 100644 --- a/README.md +++ b/README.md @@ -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. +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 through network, copy file you'd like to use from root directory and paste it in your esphome dashboard. Set boneio name as visible + +Firmware releases are created from tags in the format `esphome--`, for example `esphome-2026.4.0-b1`. diff --git a/boneio-8x10A_gen2_lights-v0_1-sd.yaml b/boneio-8x10A_gen2_lights-v0_1-sd.yaml new file mode 100644 index 0000000..a36249d --- /dev/null +++ b/boneio-8x10A_gen2_lights-v0_1-sd.yaml @@ -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 diff --git a/create_firmware.py b/create_firmware.py index 3cdacc1..dbb1839 100644 --- a/create_firmware.py +++ b/create_firmware.py @@ -40,7 +40,9 @@ include_files = [ 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"): @@ -93,7 +95,7 @@ for file in glob.glob("*.yaml"): print("No file found.") break 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) result = subprocess.run( cmd,