Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b964bc5
chore(esphome): parameterize OTA password (preserve current default)
Tycorc May 18, 2026
5031b84
Merge pull request #94 from Tycorc/parameterize-ota-password
bharvey88 Jun 10, 2026
1505d31
Enable CO2 automatic self-calibration with GUI toggle
bharvey88 Jun 10, 2026
ccbfe30
Bump version to 26.6.10.1
bharvey88 Jun 10, 2026
6679375
Run label-check and auto-assign via pull_request_target
bharvey88 Jun 10, 2026
749d1a9
Add HTTP request OTA update system from R_PRO-1
bharvey88 Jun 10, 2026
eb0ef6c
Check for updates when WiFi connects
bharvey88 Jun 11, 2026
3330a79
Raise min_version to 2025.11.0 to match R_PRO-1's update-system floor
bharvey88 Jun 11, 2026
d3cf392
Release notes: compact formatting for the HA update dialog
bharvey88 Jun 11, 2026
20f0aaf
Merge pull request #98 from ApolloAutomation/fix-fork-pr-workflows
TrevorSchirmer Jun 17, 2026
0a97f50
Merge pull request #97 from bharvey88/co2-asc-toggle
TrevorSchirmer Jun 17, 2026
651019a
Merge pull request #99 from ApolloAutomation/add-managed-update-system
TrevorSchirmer Jun 17, 2026
cc1b87c
Add air quality LED indicator
bharvey88 Jun 25, 2026
6a6c388
Merge pull request #101 from bharvey88/add-air-quality-led-indicator
bharvey88 Jun 25, 2026
6257337
Restore air quality LED after status/test scripts
bharvey88 Jul 1, 2026
db35b84
Merge pull request #105 from ApolloAutomation/fix-air-quality-led-res…
bharvey88 Jul 6, 2026
e8fd0b0
esphome.yaml: Tidy boot action whitespace
gdt Jul 7, 2026
919ab0e
esphome.yaml: Regularize (whitespace, ordering)
gdt Jul 7, 2026
90834f5
Merge pull request #109 from gdt/tidy/boot-actions
bharvey88 Jul 7, 2026
1b86a0c
Merge pull request #108 from gdt/tidy/esphome-yaml
bharvey88 Jul 7, 2026
a1b9bf0
Port MSR-1 unified firmware: runtime Bluetooth Proxy switch + channel…
bharvey88 Jul 12, 2026
b134014
Auto-assign on fork PRs via pull_request_target
bharvey88 Jul 14, 2026
727b192
Enable API encryption
bharvey88 Jul 14, 2026
fd7780a
Force sen5x model to SEN55 to keep NOx enabled on misreporting units
bharvey88 Jul 21, 2026
1ab9ff2
Point sen5x override at ApolloAutomation/esphome-components
bharvey88 Jul 21, 2026
d00eb72
Use core sen5x model option, require esphome 2026.7.2
bharvey88 Jul 22, 2026
a704231
Merge pull request #111 from ApolloAutomation/unified/firmware-channel
TrevorSchirmer Jul 22, 2026
050da4c
Bump version to 26.7.23.1
bharvey88 Jul 23, 2026
1836d30
Merge beta into force-sen55-model
bharvey88 Jul 23, 2026
aa1fffc
Merge pull request #114 from ApolloAutomation/force-sen55-model
bharvey88 Jul 23, 2026
aa77627
Merge main into beta
bharvey88 Jul 23, 2026
b83a4a5
Bump version to 26.7.23.2
bharvey88 Jul 23, 2026
53784c6
Keep version at 26.7.23.1
bharvey88 Jul 23, 2026
e13fcf4
Merge pull request #116 from ApolloAutomation/beta-sync
bharvey88 Jul 23, 2026
c093926
Re-point beta-fw tag on every beta publish
bharvey88 Jul 23, 2026
d9a084a
Pin shared workflows to a commit SHA
bharvey88 Jul 23, 2026
9890f41
Fix BLE deep sleep duration 60x too long
bharvey88 Jul 23, 2026
1b4e92a
Merge beta into fix/autoassign-fork-prs
bharvey88 Jul 23, 2026
2dc37ea
Merge pull request #112 from ApolloAutomation/fix/autoassign-fork-prs
TrevorSchirmer Jul 23, 2026
6092416
Merge pull request #117 from ApolloAutomation/fix/beta-fw-tag-target
TrevorSchirmer Jul 23, 2026
5a25cd4
Merge pull request #119 from ApolloAutomation/fix/ble-sleep-multiplier
TrevorSchirmer Jul 23, 2026
2950a37
Merge beta into chore/pin-shared-workflows
bharvey88 Jul 23, 2026
5fdf480
Merge pull request #118 from ApolloAutomation/chore/pin-shared-workflows
TrevorSchirmer Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name-template: 'Release v$NEXT_PATCH_VERSION'
tag-template: "$RESOLVED_VERSION"
change-template: "- #$NUMBER $TITLE @$AUTHOR"
sort-direction: ascending
category-template: '**$TITLE**'

categories:
- title: "🚨 Breaking changes"
Expand All @@ -26,11 +27,9 @@ include-labels:

no-changes-template: '- No changes'

# The shared build workflow appends a Full Changelog compare link to the
# release body (release-drafter cannot render 4-part version tags).
template: |
## What's Changed
**What's Changed**

$CHANGES

**Full Changelog**: https://github.com/ApolloAutomation/AIR-1/compare/$PREVIOUS_TAG...$RESOLVED_VERSION.1

Be sure to 🌟 this repository for updates!
10 changes: 5 additions & 5 deletions .github/workflows/autoassign.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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:
Expand All @@ -12,9 +14,7 @@ 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
uses: ApolloAutomation/Workflows/.github/workflows/autoassign.yml@430d90dc695c6f7d1075c4e4a0df4b13a6496252 # main 2026-07-23
with:
assignees: bharvey88
num-of-assignees: 1
116 changes: 116 additions & 0 deletions .github/workflows/build-beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Build and Publish Beta

# Builds AIR-1 firmware from the beta branch and publishes it as assets on a
# rolling "beta-fw" 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
# (AIR-1.yaml), not the first-flash Factory image.
- { yaml: Integrations/ESPHome/beta-channel/AIR-1.yaml, name: firmware-standard }
- { yaml: Integrations/ESPHome/beta-channel/AIR-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-fw' pre-release exists
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
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 AIR-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-fw"
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-fw "manifest-$v.json" -R "${{ github.repository }}" --clobber
find "$src" -name '*.bin' -print -exec \
gh release upload beta-fw {} -R "${{ github.repository }}" --clobber \;
done
echo "Beta assets published."

- name: Point beta-fw tag at the built commit
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# gh release create tags default-branch HEAD, and uploads never move
# the tag, so without this the release's source commit drifts away
# from the assets actually published.
gh api -X PATCH "repos/${{ github.repository }}/git/refs/tags/beta-fw" \
-f sha="${{ github.sha }}" -F force=true
echo "beta-fw -> ${{ github.sha }}"
8 changes: 6 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@ on:

jobs:
build-and-publish:
uses: ApolloAutomation/Workflows/.github/workflows/build.yml@main
uses: ApolloAutomation/Workflows/.github/workflows/build.yml@430d90dc695c6f7d1075c4e4a0df4b13a6496252 # main 2026-07-23
permissions:
contents: write
pages: write
id-token: write
pull-requests: write
with:
device-name: air-1
# AIR-1.yaml is the end-user image served at firmware/ (OTA updates). The Factory image (improv +
# factory test) is only used for first flashes via the web installer.
yaml-files: |
Integrations/ESPHome/AIR-1.yaml
Integrations/ESPHome/AIR-1_Factory.yaml
firmware-names: "1_Factory:firmware"
Integrations/ESPHome/AIR-1_BLE.yaml
firmware-names: "1:firmware,1_Factory:firmware-factory,1_BLE:firmware-ble"
core-yaml-path: Integrations/ESPHome/Core.yaml
esphome-version: stable
# Bypass check if manually triggered with bypass option
Expand Down
8 changes: 1 addition & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@ on:
pull_request:

permissions:
pull-requests: write
issues: write
contents: read

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
uses: ApolloAutomation/Workflows/.github/workflows/esphome-ci.yml@430d90dc695c6f7d1075c4e4a0df4b13a6496252 # main 2026-07-23
with:
yaml-files: |
Integrations/ESPHome/AIR-1.yaml
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/label-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Label Check

# pull_request_target (not pull_request) so the job gets a write token on
# fork-submitted PRs too; plain pull_request runs from forks are read-only
# and cannot add labels. Safe because the called workflow only reads the PR
# body and never checks out or executes PR code. The "edited" type re-runs
# the check when the template checkboxes are changed.
on:
pull_request_target:
types: [opened, edited, reopened, synchronize]

permissions:
pull-requests: write
issues: write
contents: read

jobs:
label-check:
name: Label Check
uses: ApolloAutomation/Workflows/.github/workflows/label-check.yml@430d90dc695c6f7d1075c4e4a0df4b13a6496252 # main 2026-07-23
2 changes: 1 addition & 1 deletion .github/workflows/weekly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ permissions:
jobs:
build:
name: Weekly Build
uses: ApolloAutomation/Workflows/.github/workflows/esphome-ci.yml@main
uses: ApolloAutomation/Workflows/.github/workflows/esphome-ci.yml@430d90dc695c6f7d1075c4e4a0df4b13a6496252 # main 2026-07-23
with:
yaml-files: |
Integrations/ESPHome/AIR-1.yaml
Expand Down
48 changes: 30 additions & 18 deletions Integrations/ESPHome/AIR-1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,24 @@ esphome:
name: "ApolloAutomation.AIR-1"
version: "${version}"

min_version: 2025.2.0
min_version: 2026.7.2
on_boot:
priority: 500
then:
- text_sensor.template.publish:
id: apollo_firmware_version
state: "${version}"
- lambda: |-
id(deep_sleep_1).set_sleep_duration(id(deep_sleep_sleep_duration).state * 60 * 1000);
- if:
condition:
or:
- binary_sensor.is_on: ota_mode
- switch.is_on: prevent_sleep
then:
- lambda: |-
ESP_LOGW("Apollo", "Preventing Deep Sleep Due To OTA On Boot");
id(deep_sleep_1).prevent_deep_sleep();
- priority: 500
then:
- text_sensor.template.publish:
id: apollo_firmware_version
state: "${version}"
- lambda: |-
id(deep_sleep_1).set_sleep_duration(id(deep_sleep_sleep_duration).state * 60 * 1000);
- if:
condition:
or:
- binary_sensor.is_on: ota_mode
- switch.is_on: prevent_sleep
then:
- lambda: |-
ESP_LOGW("Apollo", "Preventing Deep Sleep Due To OTA On Boot");
id(deep_sleep_1).prevent_deep_sleep();
on_shutdown:
- light.turn_off: rgb_light

Expand All @@ -37,9 +37,21 @@ logger:
ota:
- platform: esphome
id: ota_esphome
password: "apolloautomation"
password: ${ota_password}
- platform: http_request
id: ota_managed

safe_mode:

update:
- platform: http_request
id: update_http_request
name: Firmware Update
source: https://apolloautomation.github.io/AIR-1/firmware/manifest.json

wifi:
on_connect:
- component.update: update_http_request
ap:
ssid: "Apollo AIR1 Hotspot"

Expand Down
61 changes: 36 additions & 25 deletions Integrations/ESPHome/AIR-1_BLE.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
substitutions:
# Legacy BLE devices keep updating from the firmware-ble manifest.
ota_stable_manifest: "https://apolloautomation.github.io/AIR-1/firmware-ble/manifest.json"
ota_beta_manifest: "https://github.com/ApolloAutomation/AIR-1/releases/download/beta-fw/manifest-ble.json"

esphome:
name: "${name}"
friendly_name: Apollo AIR-1
Expand All @@ -7,45 +12,51 @@ esphome:
name: "ApolloAutomation.AIR-1"
version: "${version}"

min_version: 2025.2.0
min_version: 2026.7.2
on_boot:
priority: 500
then:
- text_sensor.template.publish:
id: apollo_firmware_version
state: "${version}"
- lambda: |-
id(deep_sleep_1).set_sleep_duration(id(deep_sleep_sleep_duration).state * 60 * 60 * 1000);
- if:
condition:
or:
- binary_sensor.is_on: ota_mode
- switch.is_on: prevent_sleep
then:
- lambda: |-
ESP_LOGW("Apollo", "Preventing Deep Sleep Due To OTA On Boot");
id(deep_sleep_1).prevent_deep_sleep();
- priority: 500
then:
- text_sensor.template.publish:
id: apollo_firmware_version
state: "${version}"
- lambda: |-
id(deep_sleep_1).set_sleep_duration(id(deep_sleep_sleep_duration).state * 60 * 1000);
- if:
condition:
or:
- binary_sensor.is_on: ota_mode
- switch.is_on: prevent_sleep
then:
- lambda: |-
ESP_LOGW("Apollo", "Preventing Deep Sleep Due To OTA On Boot");
id(deep_sleep_1).prevent_deep_sleep();
on_shutdown:
- light.turn_off: rgb_light

logger:

dashboard_import:
package_import_url: github://ApolloAutomation/AIR-1/Integrations/ESPHome/AIR-1_BLE.yaml
import_full_config: false

logger:

ota:
- platform: esphome
password: "apolloautomation"
id: ota_esphome
password: ${ota_password}
- platform: http_request
id: ota_managed

safe_mode:

bluetooth_proxy:
active: true
esp32_ble_tracker:
scan_parameters:
active: false
update:
- platform: http_request
id: update_http_request
name: Firmware Update
source: https://apolloautomation.github.io/AIR-1/firmware-ble/manifest.json

wifi:
on_connect:
- component.update: update_http_request
ap:
ssid: "Apollo AIR1 Hotspot"

Expand Down
Loading