Release 26.7.23.1 - Firmware update channels, sen5x model fix, API encryption#115
Conversation
Promote the hardcoded `password: "apolloautomation"` literal to a
`${ota_password}` substitution in `Core.yaml`, defaulting to the same value
so existing consumers see no behavior change. Consumers who want a
per-device password can override via `substitutions:` in their device YAML
and stop sharing a fleet-wide credential.
Why
----
Every AIR-1 worldwide currently boots with the same OTA password (introduced
in #34, "Fix ota password", to make OTA upgrades work after the prior
no-password attempts in #20 / #27). Consumers who include this file via
`packages:` cannot override from their device YAML because ESPHome packages
extend list-valued keys (a second `ota: - platform: esphome` block produces
a duplicate-platform validation error) and `ota: !remove` cannot be paired
with a top-level redeclaration in the same YAML doc. Newer Apollo product
configs (MSR-2, MTR-1, PWR-1, TEMP-1, PLT-1, BTN-1, RLY-1, H-1, H-2, PUMP-1)
already avoid the literal — this brings AIR-1 in line.
Backwards compat
----------------
100% — devices that don't set `ota_password` get `apolloautomation` exactly
as today. CI / firmware builds unchanged.
Override example (in a consumer device YAML):
substitutions:
ota_password: !secret living_room_air_1_ota_password
packages:
ApolloAutomation.AIR-1: github://ApolloAutomation/AIR-1/Integrations/ESPHome/AIR-1.yaml
chore(esphome): parameterize OTA password (preserve current default)
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 AIR-1 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 - Build and publish the BLE variant (firmware-ble) so BLE devices update from their own manifest instead of being converted to the factory firmware
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
Fix label-check and auto-assign on fork PRs
Enable CO2 auto calibration by default with toggle to disable
Add managed firmware update system
Drive the AIR-1's onboard RGB LEDs from air-quality readings. Everything lives in Core.yaml, so all variants pick it up via the core package. - "Air Quality LED Source" select (Off / NowCast AQI / CO2 / VOC Index), default Off. A selected source colors the LEDs on a six-step green->maroon severity scale; thresholds match each metric's range, and the VOC bands line up with the VOC Quality text sensor. - "Air Quality LED Brightness" slider (5-100%, default 100%). Event-driven: each source sensor's on_value fires the updater only when it is the selected source, so the LED tracks readings at their real cadence with no polling. Selecting Off clears the LED once, then leaves it for manual use. A millis() guard skips the early-boot window so the select's restore-publish can't write the light before it is initialized, and the updater defers while statusCheck/testScript own the LED. An unavailable source (NaN, e.g. CO2 with no module) clears the LED instead of showing a stale color. Bumps version to 26.6.25.1. Verified on hardware and with a full esphome 2026.6.2 compile. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add air quality LED indicator
statusCheck and testScript both end by turning rgb_light off, and update_air_quality_led defers while either is running, so after a button status flash or the boot self-test the air-quality color stays gone until the next sensor on_value (up to 60s with CO2 as the source). Add a restore_air_quality_led helper (mode: restart) that delays 200ms then executes update_air_quality_led, called as the last action of both scripts. The delay lets the calling script finish first so update_air_quality_led's is_running() guard passes. Bump version to 26.7.1.1. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…tore Restore air quality LED after status/test scripts
Make the single action a list of one action, rather than an action not in a list. This is semantically identical, and greatly reduces diffs among the 3 config files.
Adjust ordering of esphome yaml, and regularize whitespace. This commit makes no functional changes, and serves only to reduce the diffs from the base config to the BLE and Factory variants.
esphome.yaml: Tidy boot action whitespace
esphome.yaml: Regularize (whitespace, ordering)
…-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). - Firmware Update button holds prevent_deep_sleep for the download and re-arms sleep on the not-started path (unless ota_mode/Prevent Sleep is holding the device awake) - 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; AIR-1_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 AIR-1.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 #107 and #110. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
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)
Some AIR-1 units report SEN54 on the sen5x product name register even
though the NOx element is present and working, which makes ESPHome
disable the NOx sensor ("NOx requires a SEN55"). Pull the sen5x
component with a model override from bharvey88/esphome and set
model: sen55 so NOx stays enabled. Temporary until the model option
lands in upstream esphome. Bump version to 26.7.21.1.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Host the component override under the org instead of a personal fork. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The model option merged upstream (esphome/esphome#17764, milestoned 2026.7.2), so drop the temporary external_components pull and use the core component. Bump min_version to 2026.7.2. CI stays red on the stable and beta legs until 2026.7.2 releases; merge after that. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Unified firmware: runtime Bluetooth Proxy switch + Stable/Beta channel OTA
esphome 2026.7.2 is released, so the core model option this PR requires is now available on stable. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Resolve version conflict in favor of 26.7.23.1. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Force sen5x model to SEN55 to keep NOx enabled on misreporting units
|
Warning Review limit reached
Next review available in: 38 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 Plus Run ID: 📒 Files selected for processing (7)
WalkthroughThe PR updates AIR-1 ESPHome configurations with managed OTA, firmware channels, air-quality LED controls, and device switches. It adds beta firmware build/publishing automation, expands firmware artifacts, adjusts workflow permissions and triggers, and repoints the installer to the factory manifest. ChangesESPHome runtime and device controls
Beta firmware release flow
Repository automation and installer
Estimated code review effort: 4 (Complex) | ~45 minutes 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 |
Reconcile for the 26.7.23.1 release PR: keep version 26.7.23.1, fold the factory/BLE firmware version publish fix (#96) into list-form on_boot, keep the standalone label-check workflow (no duplicate job in ci.yml). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The main merge adds the firmware version publish fix (#96) to the BLE and Factory variants, so the built firmware changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Merge main into beta for the 26.7.23.1 release
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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:
- Around line 2-8: Pin the reusable autoassign workflow reference from the
mutable main branch to a reviewed commit SHA before using pull_request_target,
and narrow the workflow permissions to only issues: write and pull-requests:
write required for assignment. Update the workflow’s reusable-workflow
invocation while preserving its existing triggers and assignment behavior.
- Around line 2-8: Pin the privileged reusable workflow references in
.github/workflows/autoassign.yml at lines 2-8 and
.github/workflows/label-check.yml at lines 3-10 to reviewed full commit SHAs
instead of main, and retain only the write token permissions required by each
reused workflow. Update the corresponding uses entries at autoassign.yml#L19 and
label-check.yml#L20; both sites require direct changes.
In `@Integrations/ESPHome/AIR-1_BLE.yaml`:
- Line 23: Update the deep_sleep_1 sleep-duration calculation in the BLE variant
to convert the minute-based deep_sleep_sleep_duration value to milliseconds
using 60 * 1000, matching AIR-1.yaml and AIR-1_Factory.yaml; remove the extra
hour multiplier.
🪄 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 Plus
Run ID: 692ea994-7170-4102-bbbf-b0aeb597e5c6
📒 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/AIR-1.yamlIntegrations/ESPHome/AIR-1_BLE.yamlIntegrations/ESPHome/AIR-1_Factory.yamlIntegrations/ESPHome/Core.yamlIntegrations/ESPHome/beta-channel/AIR-1.yamlIntegrations/ESPHome/beta-channel/AIR-1_BLE.yamlstatic/index.html
💤 Files with no reviewable changes (1)
- .github/workflows/ci.yml
gh release create tagged default-branch HEAD once and uploads never move the tag, so the release source commit drifted away from the published assets. PATCH the tag ref to the built commit after upload. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The reusable workflows from ApolloAutomation/Workflows ran from the mutable main ref, including in pull_request_target contexts with write tokens. Pin all five references to the current reviewed commit. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The Sleep Duration number is minutes, but the BLE on_boot restore converted it with a 60 * 60 * 1000 multiplier, treating minutes as hours. A 5 minute setting slept 5 hours. Match Core and Factory. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Auto-assign on fork PRs via pull_request_target
Re-point beta-fw tag on every beta publish
Fix BLE deep sleep duration 60x too long
Keep the fork-guard removal from beta and the pinned SHA. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Pin shared reusable workflows to a commit SHA
Version: 26.7.23.1
What does this implement/fix?
Merges beta into main for the 26.7.23.1 release. Changes include:
modeloption (Force sen5x model to SEN55 to keep NOx enabled on misreporting units #114, [sen5x] Add model option to override autodetection esphome/esphome#17764)min_versionbumped in all variants; anyone building the YAML themselves needs to update ESPHome (Force sen5x model to SEN55 to keep NOx enabled on misreporting units #114)Types of changes
Checklist / Checklijst:
If user-visible functionality or configuration variables are added/modified:
🤖 Generated with Claude Code
Summary by CodeRabbit