Skip to content

Release 26.7.23.1 - Firmware update channels, sen5x model fix, API encryption#115

Merged
TrevorSchirmer merged 43 commits into
mainfrom
beta
Jul 23, 2026
Merged

Release 26.7.23.1 - Firmware update channels, sen5x model fix, API encryption#115
TrevorSchirmer merged 43 commits into
mainfrom
beta

Conversation

@bharvey88

@bharvey88 bharvey88 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Version: 26.7.23.1

What does this implement/fix?

Merges beta into main for the 26.7.23.1 release. Changes include:

Types of changes

  • Bugfix (fixed change that fixes an issue)
  • New feature (thanks!)
  • Breaking change (repair/feature that breaks existing functionality)
  • Dependency Update - Does not publish
  • Other - Does not publish
  • Website of github readme file update - Does not publish
  • Github workflows - Does not publish

Checklist / Checklijst:

  • The code change has been tested and works locally
  • The code change has not yet been tested

If user-visible functionality or configuration variables are added/modified:

  • Added/updated documentation for the web page

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Stable/Beta firmware channel selection and improved over-the-air updates, including safer recovery support.
    • Added a manual firmware update control.
    • Added configurable air-quality LED source and brightness settings.
    • Added CO₂ auto-calibration and Bluetooth Proxy controls.
    • Added beta firmware builds for standard and BLE variants.
    • Added AIR-1 standard and BLE firmware options to the build pipeline.
  • Improvements
    • Updated the web installer to use the factory firmware package.
    • Improved release presentation and pull request labeling automation.

Tycorc and others added 30 commits May 19, 2026 01:41
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
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)
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
@bharvey88 bharvey88 added new-feature New feature breaking-change Breaking change labels Jul 23, 2026
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@TrevorSchirmer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 85759f11-b3e5-4579-89fd-295df9f348bc

📥 Commits

Reviewing files that changed from the base of the PR and between e13fcf4 and 5fdf480.

📒 Files selected for processing (7)
  • .github/workflows/autoassign.yml
  • .github/workflows/build-beta.yml
  • .github/workflows/build.yml
  • .github/workflows/ci.yml
  • .github/workflows/label-check.yml
  • .github/workflows/weekly.yml
  • Integrations/ESPHome/AIR-1_BLE.yaml

Walkthrough

The 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.

Changes

ESPHome runtime and device controls

Layer / File(s) Summary
Runtime OTA configuration
Integrations/ESPHome/AIR-1*.yaml, Integrations/ESPHome/Core.yaml
ESPHome configurations add parameterized OTA credentials, HTTP-request updates, safe mode, manifest sources, channel defaults, and WiFi-triggered update checks.
Device controls and air-quality indicators
Integrations/ESPHome/Core.yaml
Core configuration adds firmware update controls, Bluetooth proxy and CO2 calibration switches, firmware-channel selection, and metric-driven LED rendering and restoration.

Beta firmware release flow

Layer / File(s) Summary
Beta firmware build and publishing
Integrations/ESPHome/beta-channel/*, .github/workflows/build*.yml
Beta wrappers select the Beta channel; workflows build standard and BLE variants and publish rewritten manifests and binaries to the rolling beta release.

Repository automation and installer

Layer / File(s) Summary
Repository automation and installer wiring
.github/workflows/*, .github/release-drafter.yml, static/index.html
Workflow triggers and permissions are updated, label checking is delegated to a reusable workflow, release output is reformatted, and the installer targets the factory manifest.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: bugfix

Suggested reviewers: trevorschirmer

Poem

A rabbit hops through YAML bright,
OTA trails glow in moonlit light.
Beta builds fly, LEDs gleam,
Stable channels join the stream.
Workflows label, manifests race—
Carrots applaud the release pace! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the release version and the main changes: update channels, SEN5x model fix, and API encryption.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bharvey88 bharvey88 removed the breaking-change Breaking change label Jul 23, 2026
@bharvey88
bharvey88 requested a review from TrevorSchirmer July 23, 2026 15:08
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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between a62f50e and e13fcf4.

📒 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.yml
  • Integrations/ESPHome/AIR-1.yaml
  • Integrations/ESPHome/AIR-1_BLE.yaml
  • Integrations/ESPHome/AIR-1_Factory.yaml
  • Integrations/ESPHome/Core.yaml
  • Integrations/ESPHome/beta-channel/AIR-1.yaml
  • Integrations/ESPHome/beta-channel/AIR-1_BLE.yaml
  • static/index.html
💤 Files with no reviewable changes (1)
  • .github/workflows/ci.yml

Comment thread .github/workflows/autoassign.yml Outdated
Comment thread Integrations/ESPHome/AIR-1_BLE.yaml Outdated
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)
bharvey88 and others added 6 commits July 23, 2026 11:11
Auto-assign on fork PRs via pull_request_target
Re-point beta-fw tag on every beta publish
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
@TrevorSchirmer
TrevorSchirmer merged commit 7b91e5d into main Jul 23, 2026
20 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new-feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants