-
Notifications
You must be signed in to change notification settings - Fork 25
Add Firmware Channel switching from HA #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| name: Build and Publish Beta | ||
|
|
||
| # Builds MSR-1 firmware from the beta branch and publishes it as assets on a | ||
| # rolling "beta" pre-release. The on-device "Firmware Channel" select points | ||
| # OTA updates at these assets. Stable firmware is built/published separately | ||
| # by build.yml (push to main -> GitHub Pages). | ||
|
|
||
| on: | ||
| push: | ||
| branches: [beta] | ||
| paths: | ||
| - 'Integrations/ESPHome/**' | ||
| workflow_dispatch: | ||
|
|
||
| # Least privilege: read-only by default; only publish-beta is elevated to write. | ||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| version: | ||
| name: Read version | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| v: ${{ steps.read.outputs.v }} | ||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| persist-credentials: false | ||
| - id: read | ||
| run: | | ||
| v=$(awk '/substitutions:/ {f=1} f && /version:/ {print $2; exit}' \ | ||
| Integrations/ESPHome/Core.yaml | tr -d '"') | ||
| echo "v=$v" >> "$GITHUB_OUTPUT" | ||
| echo "Beta version: $v" | ||
|
|
||
| build: | ||
| name: Build ${{ matrix.name }} | ||
| needs: version | ||
| strategy: | ||
| matrix: | ||
| include: | ||
| # Beta serves OTA updates only, so it builds the end-user image | ||
| # (MSR-1.yaml), not the first-flash Factory image. | ||
| - { yaml: Integrations/ESPHome/MSR-1.yaml, name: firmware-standard } | ||
| - { yaml: Integrations/ESPHome/MSR-1_BLE.yaml, name: firmware-ble-beta } | ||
| uses: esphome/workflows/.github/workflows/build.yml@025a1e6255610c498ed590403b7e510b69e474df # 2026.4.1 | ||
| with: | ||
| files: ${{ matrix.yaml }} | ||
| esphome-version: stable | ||
| combined-name: ${{ matrix.name }} | ||
| release-version: ${{ needs.version.outputs.v }} | ||
|
|
||
| publish-beta: | ||
| name: Publish beta release assets | ||
| needs: [version, build] | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - name: Download firmware artifacts | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| path: fw | ||
| pattern: firmware* | ||
|
|
||
| - name: Ensure rolling 'beta' 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 }}" \ | ||
| --prerelease --title "Beta (rolling)" \ | ||
| --notes "Latest MSR-1 beta firmware. Auto-updated on every push to the beta branch." | ||
|
|
||
| - name: Rewrite manifests to absolute URLs and upload assets | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| BASE="https://github.com/${{ github.repository }}/releases/download/beta" | ||
| declare -A DIRS=( [standard]=firmware-standard [ble]=firmware-ble-beta ) | ||
| for v in standard ble; do | ||
| src="fw/${DIRS[$v]}" | ||
| man=$(find "$src" -name manifest.json | head -1) | ||
| if [ -z "$man" ]; then | ||
| echo "::error::manifest.json not found for ${DIRS[$v]}" | ||
| exit 1 | ||
| fi | ||
| # Both variants share a device name, so their bin filenames match. | ||
| # Release assets are a flat namespace: prefix per variant. | ||
| find "$src" -name '*.bin' | while read -r bin; do | ||
| mv "$bin" "$(dirname "$bin")/$v-$(basename "$bin")" | ||
| done | ||
| echo "Rewriting $man" | ||
| # Make ota.path and parts[].path absolute release-asset URLs so the | ||
| # device never has to resolve a relative path against a redirect. | ||
| jq --arg base "$BASE" --arg pfx "$v-" ' | ||
| .builds[0].ota.path = ($base + "/" + $pfx + (.builds[0].ota.path | sub(".*/"; ""))) | ||
| | .builds[0].parts |= map(.path = ($base + "/" + $pfx + (.path | sub(".*/"; "")))) | ||
| ' "$man" > "manifest-$v.json" | ||
| cat "manifest-$v.json" | ||
| gh release upload beta "manifest-$v.json" -R "${{ github.repository }}" --clobber | ||
| find "$src" -name '*.bin' -print -exec \ | ||
| gh release upload beta {} -R "${{ github.repository }}" --clobber \; | ||
| done | ||
| echo "Beta assets published." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| substitutions: | ||
| ble_firmware: "true" | ||
|
|
||
| esphome: | ||
| name: apollo-msr-1 | ||
| friendly_name: Apollo Multisensor Mk1 (MSR-1) | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
ESPHome update.perform http_request blocking or async behavior💡 Result:
In ESPHome, the behavior of http_request and its associated update mechanism involves important distinctions between blocking and asynchronous execution to ensure system stability. HTTP Request Component The underlying http_request component has historically been known for its blocking nature, as it often relies on underlying libraries (like C++ httplib or standard Arduino WiFiClient) that wait for the full response before proceeding [1][2][3]. This behavior can block the main execution loop if a request takes too long, potentially causing issues like lost data or system instability [1]. Efforts are ongoing within the ESPHome project to move these operations toward a more asynchronous execution model to prevent blocking the main loop [1]. update.perform Action When using the update.perform action (specifically in the context of an http_request update source), the behavior is designed to avoid blocking the system during the actual firmware installation [4]. 1. Initiation: When perform is called, the component validates the update state [4]. 2. Deferred Execution: Rather than performing the heavy lifting of the OTA (Over-the-Air) flash process immediately and synchronously, the implementation uses a deferred execution pattern [4]. The flashing process is scheduled to occur in the next loop cycle using a defer call, ensuring the current execution context is not stalled by the initiation of the update [4]. Summary While standard http_request send actions can be blocking in their communication phase, the update.perform action is specifically implemented to be non-blocking by deferring the heavy flash operation to subsequent loop cycles [1][4]. Users should be aware that standard HTTP request actions in automations still carry a risk of blocking the loop if the remote server is unresponsive [1].
Citations:
🏁 Script executed:
Repository: ApolloAutomation/MSR-1
Length of output: 2576
🌐 Web query:
ESPHome http_request update perform source deferred action returns before completion lambda after update.perform💡 Result:
In ESPHome, the
http_request.sendaction (used byhttp_request.get,http_request.post, etc.) historically behaves as a blocking operation within the automation chain [1][2][3]. Because it is designed as a synchronous action, the execution of any subsequent lambdas or actions defined in your script will not occur until the HTTP request has fully completed and the response has been processed [1]. In the context of ESPHome automations, actions are generally executed in the order they appear. For an action to be non-blocking and allow the automation to "continue" before completion, it must be explicitly designed as an asynchronous action (whereplay_next_()is deferred and called later via a timer, loop callback, or event handler) [4][5]. Thehttp_requestaction, however, does not defer its completion in this manner; it waits for the underlying network library (such ascpp-httplib) to return [1]. If you find that your code is executing unexpectedly or you need to perform actions in parallel, consider the following: 1. Blocking Nature: Thehttp_requestaction blocks the main ESPHome loop until the request completes [1][3]. If your request takes a long time, it can cause watchdog timeouts or impact other system functions [1]. 2. Script Modes: If you are using scripts, themode(single, restart, parallel) affects how multiple instances of the same script interact [6]. If a script instance is triggered while another is still blocked by a long-running HTTP request, the script's behavior will depend on its defined mode [6]. 3. Architecture: ESPHome actions are typically synchronous by default unless they specifically implement the asynchronous pattern (registering callbacks to callplay_next_()) [4][5]. Sincehttp_requestdoes not currently offload the request to an asynchronous worker that returns control immediately, the "completion lambda" or subsequent actions will always wait for the request to conclude [1]. If you require non-blocking execution, you would typically need to move the request into a custom component that handles the network communication in a separate task or uses an asynchronous networking library that does not block the main loop [1]. Using standardhttp_requestactions will remain synchronous and block the progression of the current script's action list [1].Citations:
🌐 Web query:
site:github.com/esphome/esphome "update.perform" deferred play_next_ action http_request_update.cpp💡 Result:
In the context of ESPHome's source code, specifically within esphome/components/http_request/update/http_request_update.cpp, the use of.defer with actions like play_next_ is a pattern used to ensure thread safety when code runs in a background FreeRTOS task [1][2]. The.defer method schedules a callback to be executed on the ESPHome main loop thread [1][2]. This is critical in http_request_update.cpp because the update check typically runs in a dedicated background task (to prevent watchdog timeouts during network operations) [3][4]. Accessing shared components, state, or triggering automations directly from this background task can lead to data races or memory corruption if those objects are not thread-safe or if they perform non-atomic read-modify-write operations on components [1][2]. By wrapping operations—such as calling play_next_ after an action completes, updating status errors, or triggering events—inside a.defer lambda, the code ensures that these state changes occur exclusively on the main loop [1][2]. This pattern prevents concurrent access to shared resources like the component's state or the global protobuf buffer [1][2]. Similar patterns are seen in core automation components, where play_next_ is often called from deferred callbacks (e.g., via timers or event handlers) to maintain the state machine integrity of the automation [5][6].
Citations:
Lines 694-698: restore BLE only if you disabled it here. The trailing lambda re-enables BLE unconditionally on a failed update; if BLE was already off, this turns it back on unexpectedly. Capture the original state and only re-enable when you actually disabled it.
🤖 Prompt for AI Agents