From a5ee01ea0cb429df13d49f3ceada4bddb40a75b1 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:39:43 -0500 Subject: [PATCH 1/9] Rename Firmware Channel option "Main" to "Stable" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Matches the naming used by the AIR-1 firmware channel feature; "Stable" is clearer than "Main" for users who don't know the branch layout. Devices that stored "Main" fall back to the initial option, which is the same channel. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Integrations/ESPHome/Core.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 9e34017..bd32e08 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,5 +1,5 @@ substitutions: - version: "26.7.7.1" + version: "26.7.7.2" packages: @@ -201,9 +201,9 @@ select: optimistic: true restore_value: true options: - - "Main" + - "Stable" - "Beta" - initial_option: "Main" + initial_option: "Stable" on_value: then: - script.execute: apply_ota_source @@ -356,7 +356,8 @@ media_player: script: - id: apply_ota_source # Sets the OTA source URL from the two selectors: Firmware Type (WiFi/Ethernet) - # x Firmware Channel (Main/Beta). Main = GitHub Pages, Beta = GitHub release assets. + # x Firmware Channel (Stable/Beta). Stable = GitHub Pages (main branch builds), + # Beta = GitHub release assets. then: - lambda: |- const bool eth = id(firmware_selector).current_option() == "Ethernet"; From acc01f94e523d59de70012393ab9f52ccedd8fe1 Mon Sep 17 00:00:00 2001 From: Trevor Schirmer Date: Wed, 8 Jul 2026 10:34:48 -0400 Subject: [PATCH 2/9] Improvement! --- Integrations/ESPHome/Core.yaml | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index bd32e08..e6d6832 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,5 +1,5 @@ substitutions: - version: "26.7.7.2" + version: "26.7.8.1" packages: @@ -55,6 +55,13 @@ globals: psram: mode: octal speed: 80MHz + # All CAST-1 PCBs have PSRAM, so guarantee it (fail boot if absent). This flips + # ESPHome's psram_guaranteed flag, which unlocks the high-performance network + # tier: 512 WiFi RX buffers / 32 TX buffers and a much larger TCP receive + # window instead of the conservative defaults. Music Assistant bulk-downloads + # each track faster than realtime, and the small TCP window is what its flow + # control slams into at track changes/seeks. (VPE sets this too.) + ignore_not_found: false web_server: port: 80 @@ -243,30 +250,43 @@ speaker: - id: media_mixer_input timeout: never + # Resampler task stacks stay in internal RAM (default): they are hot audio + # tasks, and a PSRAM stack contends with the decode/ring buffers already in + # PSRAM during Music Assistant's bulk track transfers. (Matches VPE.) + - platform: resampler + id: announcement_resampling_speaker + output_speaker: announcement_mixer_input + sample_rate: 48000 + bits_per_sample: 16 + - platform: resampler id: media_resampling_speaker output_speaker: media_mixer_input - task_stack_in_psram: true sample_rate: 48000 bits_per_sample: 16 sendspin: id: sendspin_hub - task_stack_in_psram: true + # Keep the sendspin server task's stack in internal RAM (VPE does the same). + # That single task services the websocket: audio ingest, JSON, and the + # time-sync replies the clock filter depends on — slowing it down with a + # PSRAM stack skews time sync, which surfaces as hard-sync silence gaps at + # track changes. + task_stack_in_psram: false media_source: - platform: sendspin id: sendspin_source - decode_memory: internal + # decode_memory deliberately unset (component default, matches VPE) - platform: audio_http id: http_media_source - buffer_size: 200000 + buffer_size: 500000 - platform: audio_http id: http_announcement_source - buffer_size: 200000 + buffer_size: 250000 media_player: - platform: sendspin @@ -282,7 +302,7 @@ media_player: format: FLAC # FLAC is the least processor intensive codec num_channels: 1 # Stereo audio is unnecessary for announcements sample_rate: 48000 - speaker: announcement_mixer_input + speaker: announcement_resampling_speaker sources: - http_announcement_source From 57e2f40a6bdfc037306c280d8003c910c8648463 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Wed, 8 Jul 2026 10:52:35 -0500 Subject: [PATCH 3/9] Rename rolling beta release tag to beta-fw (branch/tag collision) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rolling pre-release created a tag named "beta", colliding with the beta branch. git resolves a bare fetch refspec to refs/tags/ before refs/heads/, so ESPHome remote packages pinned to ref: beta silently fetch the tag (frozen at creation time) instead of the branch tip - users following beta as a package get stale YAML no matter how often they clean caches. Reproduced and confirmed on MSR-1, which inherited this pattern from CAST-1. Rename the release tag to beta-fw and update the manifest URLs to match. The old beta release/tag should be deleted after this merges and the renamed release is populated. Version: 26.7.8.1 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .github/workflows/build-beta.yml | 12 ++++++------ Integrations/ESPHome/Core.yaml | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-beta.yml b/.github/workflows/build-beta.yml index 780ac15..61768bf 100644 --- a/.github/workflows/build-beta.yml +++ b/.github/workflows/build-beta.yml @@ -59,12 +59,12 @@ jobs: path: fw pattern: firmware* - - name: Ensure rolling 'beta' pre-release exists + - name: Ensure rolling 'beta-fw' pre-release exists env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh release view beta -R "${{ github.repository }}" >/dev/null 2>&1 \ - || gh release create beta -R "${{ github.repository }}" \ + gh release view beta-fw -R "${{ github.repository }}" >/dev/null 2>&1 \ + || gh release create beta-fw -R "${{ github.repository }}" \ --prerelease --title "Beta (rolling)" \ --notes "Latest CAST-1 beta firmware. Auto-updated on every push to the beta branch." @@ -72,7 +72,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - BASE="https://github.com/${{ github.repository }}/releases/download/beta" + BASE="https://github.com/${{ github.repository }}/releases/download/beta-fw" for v in w e; do man=$(find "fw/firmware-$v" -name manifest.json | head -1) if [ -z "$man" ]; then @@ -87,8 +87,8 @@ jobs: | .builds[0].parts |= map(.path = ($base + "/" + (.path | sub(".*/"; "")))) ' "$man" > "manifest-$v.json" cat "manifest-$v.json" - gh release upload beta "manifest-$v.json" -R "${{ github.repository }}" --clobber + gh release upload beta-fw "manifest-$v.json" -R "${{ github.repository }}" --clobber find "fw/firmware-$v" -name '*.bin' -print -exec \ - gh release upload beta {} -R "${{ github.repository }}" --clobber \; + gh release upload beta-fw {} -R "${{ github.repository }}" --clobber \; done echo "Beta assets published." diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index bd32e08..7e9982c 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,5 +1,5 @@ substitutions: - version: "26.7.7.2" + version: "26.7.8.1" packages: @@ -365,8 +365,8 @@ script: std::string url; if (beta) { url = eth - ? "https://github.com/ApolloAutomation/CAST-1/releases/download/beta/manifest-e.json" - : "https://github.com/ApolloAutomation/CAST-1/releases/download/beta/manifest-w.json"; + ? "https://github.com/ApolloAutomation/CAST-1/releases/download/beta-fw/manifest-e.json" + : "https://github.com/ApolloAutomation/CAST-1/releases/download/beta-fw/manifest-w.json"; } else { url = eth ? "https://apolloautomation.github.io/CAST-1/firmware-e/manifest.json" From 6e14e4b1dd6fa28b71cf3a1cce1e94eb8b65d5f1 Mon Sep 17 00:00:00 2001 From: Trevor Schirmer Date: Wed, 8 Jul 2026 19:32:10 -0400 Subject: [PATCH 4/9] Remove Empty Index --- index.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 index.html diff --git a/index.html b/index.html deleted file mode 100644 index e69de29..0000000 From 8a5ed17d445a3607be45e611594dad4aad3da54e Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Sun, 12 Jul 2026 17:47:43 -0500 Subject: [PATCH 5/9] Fix ESPHome beta build: replace deprecated Select .state in wizmote.yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit esphome::select::Select::state is deprecated (removal slated for 2026.7.0) and the removal has now reached ESPHome beta, so the CI beta legs fail with "TemplateSelect has no member named 'state'". Replace the nine id(wizmote_action_*).state reads with .current_option() - same string, identical compiled behavior on stable. Same fix Core.yaml already got in fix/select-state-dev-compat; these uses arrived later with the WizMote feature. No version bump - rides with the unified PR's 26.7.12.1. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Integrations/ESPHome/wizmote.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Integrations/ESPHome/wizmote.yaml b/Integrations/ESPHome/wizmote.yaml index 063d3b1..c643c8a 100644 --- a/Integrations/ESPHome/wizmote.yaml +++ b/Integrations/ESPHome/wizmote.yaml @@ -319,7 +319,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_on).state;' + action: !lambda 'return id(wizmote_action_on).current_option();' button: "on" - if: condition: @@ -327,7 +327,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_off).state;' + action: !lambda 'return id(wizmote_action_off).current_option();' button: "off" - if: condition: @@ -335,7 +335,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_night).state;' + action: !lambda 'return id(wizmote_action_night).current_option();' button: "night" - if: condition: @@ -343,7 +343,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_bright_up).state;' + action: !lambda 'return id(wizmote_action_bright_up).current_option();' button: "brightness_up" - if: condition: @@ -351,7 +351,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_bright_down).state;' + action: !lambda 'return id(wizmote_action_bright_down).current_option();' button: "brightness_down" - if: condition: @@ -359,7 +359,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_btn1).state;' + action: !lambda 'return id(wizmote_action_btn1).current_option();' button: "1" - if: condition: @@ -367,7 +367,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_btn2).state;' + action: !lambda 'return id(wizmote_action_btn2).current_option();' button: "2" - if: condition: @@ -375,7 +375,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_btn3).state;' + action: !lambda 'return id(wizmote_action_btn3).current_option();' button: "3" - if: condition: @@ -383,7 +383,7 @@ script: then: - script.execute: id: run_wizmote_action - action: !lambda 'return id(wizmote_action_btn4).state;' + action: !lambda 'return id(wizmote_action_btn4).current_option();' button: "4" - if: condition: From c881f6eb4b0a70991921bd332f576dd12532d7f6 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Wed, 8 Jul 2026 12:00:55 -0500 Subject: [PATCH 6/9] Unified firmware: runtime Bluetooth Proxy switch + channel fix set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aligns the live CAST-1 channel implementation with the pattern MSR-1 shipped as 26.7.9.1 (ApolloAutomation/MSR-1 #100/#103/#104), folding in the beta-channel-default wrapper work: - NEW: bluetooth_proxy + esp32_ble_tracker compile into both images; a "Bluetooth Proxy" switch (default off, persisted, re-applied at boot) starts/stops scanning at runtime. The existing CONFIG_BT_* sdkconfig options put BT allocations in PSRAM, keeping internal RAM free for the audio pipelines. - apply_ota_source is now re-applied at boot (on_boot -100) - previously a reboot left the update entity on its compile-time default source. URLs come from stable/beta manifest-base substitutions; the Type (WiFi/Ethernet) x Channel (Stable/Beta) cross is unchanged. - Firmware Update button upgraded from bare perform(true) to the standard flow (apply_ota_source + fetch window before perform) - http_request consolidated into Core.yaml and buffers raised to the proven sizes (rx 4096->5120 for GitHub's ~3.6KB CSP header line, tx 1024->2048 for the ~850-char signed-redirect query) - beta-channel/ wrappers default the select to Beta on beta builds (from the beta-channel-default branch); build-beta.yml builds them - version 26.7.12.1 Supersedes #46. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .github/workflows/build-beta.yml | 4 +- Integrations/ESPHome/CAST-1_ETH.yaml | 8 -- Integrations/ESPHome/CAST-1_W.yaml | 8 -- Integrations/ESPHome/Core.yaml | 90 ++++++++++++++++--- .../ESPHome/beta-channel/CAST-1_ETH.yaml | 9 ++ .../ESPHome/beta-channel/CAST-1_W.yaml | 9 ++ 6 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 Integrations/ESPHome/beta-channel/CAST-1_ETH.yaml create mode 100644 Integrations/ESPHome/beta-channel/CAST-1_W.yaml diff --git a/.github/workflows/build-beta.yml b/.github/workflows/build-beta.yml index 61768bf..c2535cb 100644 --- a/.github/workflows/build-beta.yml +++ b/.github/workflows/build-beta.yml @@ -37,8 +37,8 @@ jobs: strategy: matrix: include: - - { yaml: Integrations/ESPHome/CAST-1_W.yaml, name: firmware-w } - - { yaml: Integrations/ESPHome/CAST-1_ETH.yaml, name: firmware-e } + - { yaml: Integrations/ESPHome/beta-channel/CAST-1_W.yaml, name: firmware-w } + - { yaml: Integrations/ESPHome/beta-channel/CAST-1_ETH.yaml, name: firmware-e } uses: esphome/workflows/.github/workflows/build.yml@025a1e6255610c498ed590403b7e510b69e474df # 2026.4.1 with: files: ${{ matrix.yaml }} diff --git a/Integrations/ESPHome/CAST-1_ETH.yaml b/Integrations/ESPHome/CAST-1_ETH.yaml index 3463c7a..86d911a 100644 --- a/Integrations/ESPHome/CAST-1_ETH.yaml +++ b/Integrations/ESPHome/CAST-1_ETH.yaml @@ -37,14 +37,6 @@ ota: - platform: http_request id: ota_managed -http_request: - verify_ssl: true - # Beta OTA manifests/binaries are GitHub release assets, which 302-redirect to - # long signed CDN URLs (~900+ bytes). The default 512-byte RX buffer overflows - # ("Out of buffer"), so enlarge it. See esphome/esphome#13786. - buffer_size_rx: 4096 - buffer_size_tx: 1024 - safe_mode: update: diff --git a/Integrations/ESPHome/CAST-1_W.yaml b/Integrations/ESPHome/CAST-1_W.yaml index 2c1e08b..3dcce8e 100644 --- a/Integrations/ESPHome/CAST-1_W.yaml +++ b/Integrations/ESPHome/CAST-1_W.yaml @@ -37,14 +37,6 @@ ota: - platform: http_request id: ota_managed -http_request: - verify_ssl: true - # Beta OTA manifests/binaries are GitHub release assets, which 302-redirect to - # long signed CDN URLs (~900+ bytes). The default 512-byte RX buffer overflows - # ("Out of buffer"), so enlarge it. See esphome/esphome#13786. - buffer_size_rx: 4096 - buffer_size_tx: 1024 - safe_mode: improv_serial: diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 919938e..6afffb2 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,10 +1,40 @@ substitutions: - version: "26.7.8.1" + version: "26.7.12.1" + # Default update channel on first boot (no stored user choice yet, i.e. a + # fresh flash). The beta-channel builds override this to "Beta" (see + # Integrations/ESPHome/beta-channel/) so firmware obtained from the beta + # channel keeps tracking it instead of offering a stable "downgrade". + firmware_channel_default: "Stable" + # Manifest URL bases. Stable = GitHub Pages (main branch builds). + # Beta = rolling "beta-fw" pre-release assets (beta branch builds). + stable_manifest_base: "https://apolloautomation.github.io/CAST-1" + beta_manifest_base: "https://github.com/ApolloAutomation/CAST-1/releases/download/beta-fw" packages: wizmote: !include wizmote.yaml +esphome: + # List form so package merging concatenates with each variant's own on_boot + # entries (mapping form would be replaced by the variant's block instead). + on_boot: + # Point the update entity at the selected type/channel manifest. + - priority: -100 + then: + - script.execute: apply_ota_source + # Re-apply the Bluetooth Proxy switch after all components set up, so BLE + # scanning matches the persisted switch (proxy stays off by default). + - priority: -300 + then: + - if: + condition: + switch.is_on: bluetooth_proxy_switch + then: + - esp32_ble_tracker.start_scan: + continuous: true + else: + - esp32_ble_tracker.stop_scan: + esp32: variant: ESP32S3 flash_size: 8MB @@ -26,6 +56,15 @@ esp32: CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" +# BT allocations go to PSRAM first (CONFIG_BT_* options above), keeping +# internal RAM free for the audio pipelines. +esp32_ble_tracker: + id: ble_tracker + scan_parameters: + continuous: true + +bluetooth_proxy: + api: globals: @@ -67,6 +106,17 @@ web_server: port: 80 version: 3 +http_request: + verify_ssl: true + # GitHub release-asset downloads answer with a redirect carrying a + # ~3.6 KB Content-Security-Policy header; each header line must fit + # this buffer or the request fails with "HTTP_CLIENT: Out of buffer". + buffer_size_rx: 5120 + # The redirect target is a signed URL with a ~850-char query string; the + # follow-up request line must fit the TX buffer or esp_http_client_open + # fails with "Out of buffer" before sending anything. + buffer_size_tx: 2048 + i2c: sda: GPIO47 scl: GPIO48 @@ -79,11 +129,17 @@ button: - platform: template name: "Firmware Update" id: update_firmware + icon: mdi:cloud-download entity_category: config on_press: - then: - - lambda: |- - id(update_http_request).perform(true); + - logger.log: "Applying firmware update for the selected type and channel" + - delay: 3s + - script.execute: apply_ota_source + - script.wait: apply_ota_source + # The manifest fetch runs in its own task; give it a fixed window to land + # (update.is_available stays false for same-version switches). + - delay: 5s + - lambda: id(update_http_request).perform(true); binary_sensor: - platform: status @@ -139,6 +195,19 @@ switch: id: enable_dac restore_mode: ALWAYS_ON + - platform: template + name: "Bluetooth Proxy" + id: bluetooth_proxy_switch + icon: mdi:bluetooth + entity_category: "config" + restore_mode: RESTORE_DEFAULT_OFF + optimistic: true + on_turn_on: + - esp32_ble_tracker.start_scan: + continuous: true + on_turn_off: + - esp32_ble_tracker.stop_scan: + text_sensor: - platform: sendspin type: title @@ -210,7 +279,7 @@ select: options: - "Stable" - "Beta" - initial_option: "Stable" + initial_option: "${firmware_channel_default}" on_value: then: - script.execute: apply_ota_source @@ -377,7 +446,7 @@ script: - id: apply_ota_source # Sets the OTA source URL from the two selectors: Firmware Type (WiFi/Ethernet) # x Firmware Channel (Stable/Beta). Stable = GitHub Pages (main branch builds), - # Beta = GitHub release assets. + # Beta = rolling "beta-fw" release assets. then: - lambda: |- const bool eth = id(firmware_selector).current_option() == "Ethernet"; @@ -385,13 +454,14 @@ script: std::string url; if (beta) { url = eth - ? "https://github.com/ApolloAutomation/CAST-1/releases/download/beta-fw/manifest-e.json" - : "https://github.com/ApolloAutomation/CAST-1/releases/download/beta-fw/manifest-w.json"; + ? "${beta_manifest_base}/manifest-e.json" + : "${beta_manifest_base}/manifest-w.json"; } else { url = eth - ? "https://apolloautomation.github.io/CAST-1/firmware-e/manifest.json" - : "https://apolloautomation.github.io/CAST-1/firmware-w/manifest.json"; + ? "${stable_manifest_base}/firmware-e/manifest.json" + : "${stable_manifest_base}/firmware-w/manifest.json"; } + ESP_LOGI("firmware", "OTA manifest set to: %s", url.c_str()); id(update_http_request).set_source_url(url); - component.update: update_http_request - id: statusCheck diff --git a/Integrations/ESPHome/beta-channel/CAST-1_ETH.yaml b/Integrations/ESPHome/beta-channel/CAST-1_ETH.yaml new file mode 100644 index 0000000..fe73f73 --- /dev/null +++ b/Integrations/ESPHome/beta-channel/CAST-1_ETH.yaml @@ -0,0 +1,9 @@ +# Beta-channel build of CAST-1_ETH.yaml: the identical image except the Firmware +# Channel select defaults to "Beta" on first boot, so firmware obtained from +# the beta channel keeps tracking it. Built by build-beta.yml only; the +# stable (GitHub Pages) builds use CAST-1_ETH.yaml directly. +substitutions: + firmware_channel_default: "Beta" + +packages: + base: !include ../CAST-1_ETH.yaml diff --git a/Integrations/ESPHome/beta-channel/CAST-1_W.yaml b/Integrations/ESPHome/beta-channel/CAST-1_W.yaml new file mode 100644 index 0000000..9b64888 --- /dev/null +++ b/Integrations/ESPHome/beta-channel/CAST-1_W.yaml @@ -0,0 +1,9 @@ +# Beta-channel build of CAST-1_W.yaml: the identical image except the Firmware +# Channel select defaults to "Beta" on first boot, so firmware obtained from +# the beta channel keeps tracking it. Built by build-beta.yml only; the +# stable (GitHub Pages) builds use CAST-1_W.yaml directly. +substitutions: + firmware_channel_default: "Beta" + +packages: + base: !include ../CAST-1_W.yaml From b5ad23cdf6883a9ec3125dfd023eb77f3d74bd33 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:06:50 -0500 Subject: [PATCH 7/9] Auto-assign on fork PRs via pull_request_target --- .github/workflows/autoassign.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/autoassign.yml b/.github/workflows/autoassign.yml index 77bef44..03b36f5 100644 --- a/.github/workflows/autoassign.yml +++ b/.github/workflows/autoassign.yml @@ -1,9 +1,12 @@ name: Auto Assign +# pull_request_target (not pull_request) so auto-assign works on +# fork-submitted PRs; fork pull_request runs only get a read-only token. +# Safe because this workflow never checks out or executes PR code. on: issues: types: [opened] - pull_request: + pull_request_target: types: [opened] permissions: @@ -12,8 +15,6 @@ permissions: jobs: auto-assign: - # Skip auto-assign for pull requests from forks due to GITHUB_TOKEN permission restrictions - if: github.event_name == 'issues' || github.event.pull_request.head.repo.full_name == github.repository uses: ApolloAutomation/Workflows/.github/workflows/autoassign.yml@main with: assignees: bharvey88 From 4762ea131f449bcfb8158f58ac92d44823f8efea Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:53:37 -0500 Subject: [PATCH 8/9] Enable API encryption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an empty encryption: key to the api: block so ESPHome/HA provisions a per-device API key on adoption, matching MSR-1. Bump firmware version to 26.7.14.1. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Integrations/ESPHome/Core.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 6afffb2..563b23f 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,5 +1,5 @@ substitutions: - version: "26.7.12.1" + version: "26.7.14.1" # Default update channel on first boot (no stored user choice yet, i.e. a # fresh flash). The beta-channel builds override this to "Beta" (see # Integrations/ESPHome/beta-channel/) so firmware obtained from the beta @@ -66,6 +66,7 @@ esp32_ble_tracker: bluetooth_proxy: api: + encryption: globals: - id: cycleCounter From c1e24f7eb1157d1e396aa2d4c177fe8b036bd87f Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Tue, 14 Jul 2026 15:15:07 -0500 Subject: [PATCH 9/9] Remove duplicate label-check job from CI workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The label-check job in ci.yml duplicates label-check.yml and, because ci.yml runs on plain pull_request, always fails on fork-submitted PRs where the token is read-only and cannot apply labels. label-check.yml already handles this on pull_request_target. Drop the redundant job and the now-unneeded pull-requests/issues write permissions, matching the already-clean main on MSR-1 and CAST-1. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .github/workflows/ci.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cea29f3..92a99e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,15 +4,11 @@ on: pull_request: permissions: - pull-requests: write - issues: write contents: read +# Label check lives in label-check.yml on pull_request_target so it can +# label fork-submitted PRs; plain pull_request tokens are read-only there. jobs: - label-check: - name: Label Check - uses: ApolloAutomation/Workflows/.github/workflows/label-check.yml@main - ci: name: CI uses: ApolloAutomation/Workflows/.github/workflows/esphome-ci.yml@main