Merge beta into main#81
Conversation
Enable the SCD40's automatic self-calibration by default (ESPHome default) so users no longer need to manually recalibrate every 1-2 years. Adds a "CO2 Auto Calibration" switch so users in spaces that never see fresh-air CO2 levels can turn ASC off and keep using the manual 420ppm calibration button instead. The switch re-applies the saved choice after every boot, since the sensor loses the setting on power loss and the scd4x component re-applies the YAML default during setup.
Fork-submitted PRs get a read-only token on pull_request runs, so the label and assignee bots fail with 403. pull_request_target runs in the base repo context with a write token; safe here because neither job checks out or executes PR code. Build jobs stay on pull_request. Trim ci.yml permissions to what the builds need.
Port the managed firmware update system from R_PRO-1 to all MSR-2 variants: - Add http_request OTA platform alongside the existing esphome OTA - Add update component pulling the firmware manifest from GitHub Pages - Add safe_mode for recovery from failed updates
The http_request update component polls every 6h and the first poll fires before the network is up, so a freshly booted device would not see an available update for 6 hours. Trigger a manifest check as soon as WiFi connects.
Home Assistant shows only the first 255 characters of an ESPHome release summary, so boilerplate is expensive and ## headings render oversized in the update dialog: - Render category titles and What's Changed in bold instead of H2 - Drop the star-the-repo footer - Drop the Full Changelog line: it semver-truncates 4-part versions (always links ...X.Y.Z.1) - the shared build workflow now appends a correct compare link instead
Enable CO2 auto calibration by default with toggle to disable
Fix label-check and auto-assign on fork PRs
Add managed firmware update system
…-only OTA One firmware image per channel replaces the build-time _BLE split: bluetooth_proxy + esp32_ble_tracker compile into every variant and a "Bluetooth Proxy" switch (default off, persisted) starts/stops scanning at runtime. The Firmware Channel select swaps the OTA manifest URL directly - no ble_firmware selector, no manifest-matching guard, no pre-OTA BLE disable (ESPHome's OTA quiesces BLE itself). - http_request consolidated into Core.yaml with the proven buffer sizes (rx 5120 for GitHub's ~3.6KB CSP header line, tx 2048 for the ~850-char signed-redirect query) - apply_ota_source is channel-only; MSR-2_BLE.yaml keeps legacy devices on the firmware-ble manifests via substitution overrides - beta-channel/ wrappers default the select to Beta; build-beta.yml publishes manifest-standard.json + manifest-ble.json to the rolling beta-fw pre-release (absolute URLs) - build.yml now serves the end-user MSR-2.yaml image at firmware/ and moves the improv Factory image to firmware-factory/ (installer page repointed) - version 26.7.12.1 Ported from MSR-1 26.7.9.1 (ApolloAutomation/MSR-1 #100, #103, #104). Supersedes #79. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Unified firmware: runtime Bluetooth Proxy switch + Stable/Beta channel OTA
|
Warning Review limit reached
Next review available in: 44 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughThis PR adds Stable/Beta firmware-channel OTA support, HTTP-managed updates, BLE proxy and CO2 calibration controls, beta firmware publishing, expanded firmware builds, factory installer targeting, and revised GitHub automation and release formatting. ChangesFirmware delivery and OTA
GitHub automation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant GitHub
participant BetaWorkflow
participant ESPHomeBuild
participant BetaRelease
participant Device
GitHub->>BetaWorkflow: push beta branch or manual dispatch
BetaWorkflow->>ESPHomeBuild: build standard and BLE beta images
ESPHomeBuild-->>BetaWorkflow: return firmware artifacts
BetaWorkflow->>BetaRelease: rewrite manifests and upload assets
Device->>BetaRelease: request selected firmware manifest
BetaRelease-->>Device: return firmware metadata and assets
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Resolve workflow conflicts by taking main's consolidated reusable workflows (ci.yml, autoassign.yml) from #78. Brings in the MSR-2 datasheet, standardized PR template, and dependabot updates.
Switch the auto-assign trigger from pull_request to pull_request_target and drop the fork-skip guard, so PRs opened from forks get auto-assigned. Fork pull_request runs only receive a read-only GITHUB_TOKEN, which is why assignment was skipped for them. pull_request_target runs in the base-repo context with a write token; safe here because the workflow never checks out or executes PR code.
Auto-assign on fork PRs via pull_request_target
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
.github/workflows/build-beta.yml (1)
96-99: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winMake
jqmanifest rewrite resilient to missingparts.If a build does not produce factory bins (e.g., if ESPHome configuration changes or standard OTA-only builds are used), the
partsarray will be missing from the manifest. In that case,|= map(...)on a null value will causejqto throw aCannot iterate over nullerror, failing the workflow.Consider guarding the
partstransformation to ensure it only applies if the array exists.🛠️ Proposed fix
- 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" + jq --arg base "$BASE" --arg pfx "$v-" ' + .builds[0].ota.path = ($base + "/" + $pfx + (.builds[0].ota.path | sub(".*/"; ""))) + | if .builds[0].parts != null then .builds[0].parts |= map(.path = ($base + "/" + $pfx + (.path | sub(".*/"; "")))) else . end + ' "$man" > "manifest-$v.json"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/build-beta.yml around lines 96 - 99, Update the jq filter in the manifest rewrite to apply the .builds[0].parts path transformation only when parts exists as an array, leaving missing parts unchanged while preserving the existing OTA path rewrite.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/autoassign.yml:
- Line 8: Restrict the pull_request_target trigger to the opened event only by
adding its explicit event-type configuration under pull_request_target in the
workflow. Preserve the existing workflow jobs and prevent execution for
synchronize and reopened events.
In `@Integrations/ESPHome/Core.yaml`:
- Around line 775-788: Update the Firmware Update template action and the
manifest-check completion flow associated with update_http_request so lambda
id(update_http_request).perform(true) runs only after the asynchronous manifest
check completes. Remove the fixed 5s delay and trigger installation from that
completion path, ensuring it uses the newly fetched channel URL and MD5.
- Around line 837-853: Update the CO2 auto-calibration flow around the
setCo2AutoCalibration script and co2_auto_calibration switch so SCD4x stop/start
command failures are detected before committing the optimistic switch state.
Check both command results and revert or defer the switch state when either
write fails, while preserving the requested state only after the restart
succeeds.
---
Nitpick comments:
In @.github/workflows/build-beta.yml:
- Around line 96-99: Update the jq filter in the manifest rewrite to apply the
.builds[0].parts path transformation only when parts exists as an array, leaving
missing parts unchanged while preserving the existing OTA path rewrite.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 49ba3d1a-4e4c-4e36-90f8-def37a850745
📒 Files selected for processing (13)
.github/release-drafter.yml.github/workflows/autoassign.yml.github/workflows/build-beta.yml.github/workflows/build.yml.github/workflows/ci.yml.github/workflows/label-check.ymlIntegrations/ESPHome/Core.yamlIntegrations/ESPHome/MSR-2.yamlIntegrations/ESPHome/MSR-2_BLE.yamlIntegrations/ESPHome/MSR-2_Factory.yamlIntegrations/ESPHome/beta-channel/MSR-2.yamlIntegrations/ESPHome/beta-channel/MSR-2_BLE.yamlstatic/index.html
💤 Files with no reviewable changes (1)
- .github/workflows/ci.yml
| - platform: template | ||
| name: "Firmware Update" | ||
| id: update_firmware | ||
| icon: mdi:cloud-download | ||
| entity_category: "config" | ||
| on_press: | ||
| - logger.log: "Applying firmware update for the selected 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); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -C3 'min_version|apply_ota_source|update_http_request.*perform' Integrations/ESPHome
curl -fsSL \
https://raw.githubusercontent.com/esphome/esphome/2025.11.0/esphome/components/http_request/update/http_request_update.cpp |
tr '}' '}\n' |
rg -n 'xTaskCreate|HttpRequestUpdate::perform|update_info'Repository: ApolloAutomation/MSR-2
Length of output: 5210
🏁 Script executed:
#!/bin/bash
rg -n -C3 'get_update_available_trigger|update_available|on_update|perform\(true\)|apply_ota_source|update_http_request' Integrations/ESPHomeRepository: ApolloAutomation/MSR-2
Length of output: 5421
Wait for the manifest check to finish before calling perform(true)
The fixed 5s delay can still race the async update task, so perform(true) may consume the previous channel’s URL/MD5. Trigger the install from the manifest-check completion path instead of sleeping.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Integrations/ESPHome/Core.yaml` around lines 775 - 788, Update the Firmware
Update template action and the manifest-check completion flow associated with
update_http_request so lambda id(update_http_request).perform(true) runs only
after the asynchronous manifest check completes. Remove the fixed 5s delay and
trigger installation from that completion path, ensuring it uses the newly
fetched channel URL and MD5.
| - platform: template | ||
| name: "CO2 Auto Calibration" | ||
| id: co2_auto_calibration | ||
| icon: mdi:molecule-co2 | ||
| restore_mode: RESTORE_DEFAULT_ON | ||
| optimistic: true | ||
| entity_category: "config" | ||
| on_turn_on: | ||
| then: | ||
| - script.execute: | ||
| id: setCo2AutoCalibration | ||
| enable: true | ||
| on_turn_off: | ||
| then: | ||
| - script.execute: | ||
| id: setCo2AutoCalibration | ||
| enable: false |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
curl -fsSL \
https://raw.githubusercontent.com/esphome/esphome/2025.11.0/esphome/components/scd4x/scd4x.cpp |
rg -n -C3 'write_command|start_measurement|automatic self calibration'Repository: ApolloAutomation/MSR-2
Length of output: 4721
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the relevant sections with line numbers.
sed -n '820,870p' Integrations/ESPHome/Core.yaml
printf '\n---\n'
sed -n '930,970p' Integrations/ESPHome/Core.yaml
printf '\n=== script definitions ===\n'
rg -n -C4 'setCo2AutoCalibration|co2_auto_calibration|automatic self calibration|SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION|SCD4X_CMD_STOP_MEASUREMENTS|SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS' Integrations/ESPHome/Core.yamlRepository: ApolloAutomation/MSR-2
Length of output: 4029
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '820,870p' Integrations/ESPHome/Core.yaml | cat -n
printf '\n---\n'
sed -n '930,970p' Integrations/ESPHome/Core.yaml | cat -n
printf '\n=== matches ===\n'
rg -n -C3 'setCo2AutoCalibration|co2_auto_calibration|automatic self calibration|SCD4X_CMD_STOP_MEASUREMENTS|SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS' Integrations/ESPHome/Core.yamlRepository: ApolloAutomation/MSR-2
Length of output: 4484
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show just the lines around the named helper.
rg -n -C8 'setCo2AutoCalibration' Integrations/ESPHome/Core.yamlRepository: ApolloAutomation/MSR-2
Length of output: 1586
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the full helper body and surrounding logic.
sed -n '925,955p' Integrations/ESPHome/Core.yaml | cat -n
# Also inspect any direct calls to the switch state that might publish or revert it.
rg -n -C4 'co2_auto_calibration|setCo2AutoCalibration|publish_state|turn_on:|turn_off:' Integrations/ESPHome/Core.yamlRepository: ApolloAutomation/MSR-2
Length of output: 4173
Handle SCD4x command failures before leaving the optimistic switch on. setCo2AutoCalibration ignores the stop/start write results, so a failed restart can leave periodic measurements stopped while the switch still shows the requested state. Check both commands and revert or delay the state update until they succeed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Integrations/ESPHome/Core.yaml` around lines 837 - 853, Update the CO2
auto-calibration flow around the setCo2AutoCalibration script and
co2_auto_calibration switch so SCD4x stop/start command failures are detected
before committing the optimistic switch state. Check both command results and
revert or defer the switch state when either write fails, while preserving the
requested state only after the restart succeeds.
Merge
betaintomain.What's included
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements