Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
17acd21
chore(esphome): parameterize OTA password (preserve current default)
Tycorc May 18, 2026
90a6706
Run label-check and auto-assign via pull_request_target
bharvey88 Jun 10, 2026
5dafde0
Merge pull request #81 from Tycorc/parameterize-ota-password
bharvey88 Jun 10, 2026
98865af
Add HTTP request OTA update system from R_PRO-1
bharvey88 Jun 11, 2026
64494c6
Check for updates when WiFi connects
bharvey88 Jun 11, 2026
250f272
Raise min_version to 2025.11.0 to match R_PRO-1's update-system floor
bharvey88 Jun 11, 2026
3c6eb13
Build and publish the BLE variant to its own firmware-ble manifest
bharvey88 Jun 11, 2026
b6efb98
Release notes: compact formatting for the HA update dialog
bharvey88 Jun 11, 2026
7711c8a
Merge pull request #83 from ApolloAutomation/fix-fork-pr-workflows
TrevorSchirmer Jun 12, 2026
ad32901
Merge pull request #84 from ApolloAutomation/add-managed-update-system
TrevorSchirmer Jun 17, 2026
c624ed0
Bump Core.yaml version to 26.6.17.1
bharvey88 Jun 17, 2026
e0e7c29
Merge pull request #85 from ApolloAutomation/bump-version-26.6.17.1
bharvey88 Jun 17, 2026
7616da5
Add Firmware Channel switching from HA
bharvey88 Jul 7, 2026
73fd740
Merge pull request #89 from ApolloAutomation/firmware-channel
TrevorSchirmer Jul 8, 2026
6d3ebcf
Rename rolling beta release tag to beta-fw (branch/tag collision)
bharvey88 Jul 8, 2026
f802653
Merge pull request #91 from ApolloAutomation/fix-beta-tag-collision
bharvey88 Jul 8, 2026
289f939
Default the Firmware Channel select to Beta on beta-channel builds
bharvey88 Jul 8, 2026
1cc0b3a
Merge pull request #92 from ApolloAutomation/beta-channel-default
bharvey88 Jul 8, 2026
3f5d351
Add Bluetooth Proxy firmware switching from HA
bharvey88 Jul 8, 2026
ddec791
Merge pull request #90 from ApolloAutomation/ble-proxy-select
TrevorSchirmer Jul 8, 2026
3635ac7
Fix beta manifest fetch and guard force-install against stale manifests
bharvey88 Jul 8, 2026
34c7677
Merge pull request #93 from ApolloAutomation/fix-beta-fetch-and-guard
bharvey88 Jul 8, 2026
0b7f590
Raise http_request rx buffer to 5120 for GitHub's CSP header
bharvey88 Jul 8, 2026
b5a8de4
Merge pull request #94 from ApolloAutomation/fix-buffer-size
bharvey88 Jul 8, 2026
8ea86a8
Set http_request buffer_size_tx: 2048 - the real Out of buffer fix
bharvey88 Jul 8, 2026
a798e0a
Merge pull request #95 from ApolloAutomation/fix-tx-buffer
bharvey88 Jul 8, 2026
6673941
Bump Core.yaml version to 26.7.9.1 for release
bharvey88 Jul 9, 2026
adf9bd8
Merge pull request #96 from ApolloAutomation/bump-version-26.7.9.1
bharvey88 Jul 9, 2026
c686c79
Merge main into beta; converge CI on shared reusable workflows
bharvey88 Jul 9, 2026
2581189
Merge pull request #98 from ApolloAutomation/sync-main-into-beta
bharvey88 Jul 9, 2026
9a00325
Add API encryption with no pre-configured key
bharvey88 Jul 9, 2026
9134ab2
Merge pull request #99 from ApolloAutomation/add-api-encryption
bharvey88 Jul 9, 2026
a6d9f2a
Unify BLE into one firmware: runtime Bluetooth Proxy switch + channel…
bharvey88 Jul 9, 2026
bd24876
Merge pull request #100 from ApolloAutomation/unified/ble-switch
bharvey88 Jul 9, 2026
502c4e7
Remove vestigial ble_firmware substitution; fix build.yml device-name
bharvey88 Jul 9, 2026
4be81c3
Consolidate http_request block into Core.yaml
bharvey88 Jul 9, 2026
9de835c
Remove pre-OTA BLE stack disable/re-enable from Firmware Update
bharvey88 Jul 9, 2026
b46e1c5
Merge pull request #101 from ApolloAutomation/cleanup/vestigial-ble-f…
bharvey88 Jul 9, 2026
3736a18
Merge pull request #102 from ApolloAutomation/consolidate/http-request
bharvey88 Jul 9, 2026
6885b3f
Merge pull request #103 from ApolloAutomation/remove/ble-stop-before-ota
bharvey88 Jul 9, 2026
a5200c5
Drop redundant bluetooth_proxy active: true
bharvey88 Jul 9, 2026
0b69a22
Merge pull request #104 from ApolloAutomation/remove/bluetooth-proxy-…
bharvey88 Jul 9, 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 @@ -25,11 +26,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/MSR-1/compare/$PREVIOUS_TAG...$RESOLVED_VERSION.1

Be sure to 🌟 this repository for updates!
7 changes: 4 additions & 3 deletions .github/workflows/autoassign.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
name: Auto Assign

# pull_request_target (not pull_request) so auto-assign works on fork PRs too:
# forks get a read-only token on pull_request and cannot be assigned. Safe here
# because this only assigns a user and never checks out or runs PR code.
on:
issues:
types: [opened]
pull_request:
pull_request_target:
types: [opened]

permissions:
Expand All @@ -12,8 +15,6 @@ 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
with:
assignees: bharvey88
Expand Down
105 changes: 105 additions & 0 deletions .github/workflows/build-beta.yml
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-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
# (MSR-1.yaml), not the first-flash Factory image.
- { yaml: Integrations/ESPHome/beta-channel/MSR-1.yaml, name: firmware-standard }
- { yaml: Integrations/ESPHome/beta-channel/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-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 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-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."
8 changes: 6 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ jobs:
id-token: write
pull-requests: write
with:
device-name: msr-2
device-name: msr-1
# MSR-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/MSR-1.yaml
Integrations/ESPHome/MSR-1_Factory.yaml
firmware-names: "1_Factory:firmware"
Integrations/ESPHome/MSR-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
7 changes: 0 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@ 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
Expand All @@ -21,4 +15,3 @@ jobs:
Integrations/ESPHome/MSR-1.yaml
Integrations/ESPHome/MSR-1_BLE.yaml
Integrations/ESPHome/MSR-1_Factory.yaml

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@main
116 changes: 115 additions & 1 deletion Integrations/ESPHome/Core.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,77 @@
substitutions:
version: "26.3.2.1"
version: "26.7.9.1"
device_description: ${name} made by Apollo Automation - version ${version}.
# Default OTA password. Override in your device YAML by re-declaring
# `substitutions: { ota_password: !secret <name>_ota_password }` so each
# device on your network uses a unique secret instead of the shared default.
ota_password: "apolloautomation"
# Default update channel on first boot (no stored user choice yet, i.e. a
# fresh flash). The beta-channel builds override this to "Beta" (see
# Integrations/ESPHome/beta-channel/) so firmware obtained from the beta
# channel keeps tracking it instead of offering a stable "downgrade".
firmware_channel_default: "Stable"
# Manifest URL bases. Stable = GitHub Pages (main branch builds).
# Beta = rolling "beta" pre-release assets (beta branch builds).
stable_manifest_base: "https://apolloautomation.github.io/MSR-1"
beta_manifest_base: "https://github.com/ApolloAutomation/MSR-1/releases/download/beta-fw"
# Per-variant OTA manifest URLs. Core defaults to the unified (standard)
# image; MSR-1_BLE.yaml overrides these so legacy BLE devices stay on the
# firmware-ble manifest.
ota_stable_manifest: "${stable_manifest_base}/firmware/manifest.json"
ota_beta_manifest: "${beta_manifest_base}/manifest-standard.json"

esphome:
# List form so package merging concatenates with each variant's own on_boot
# entries (mapping form would be replaced by the variant's block instead).
on_boot:
- priority: -100
then:
- script.execute: apply_ota_source
# Re-apply the Bluetooth Proxy switch after all components set up, so BLE
# scanning matches the persisted switch (proxy stays off by default).
- priority: -300
then:
- if:
condition:
switch.is_on: bluetooth_proxy_switch
then:
- esp32_ble_tracker.start_scan:
continuous: true
else:
- esp32_ble_tracker.stop_scan:

esp32:
variant: esp32c3
flash_size: 4MB
framework:
type: esp-idf

esp32_ble_tracker:
id: ble_tracker
scan_parameters:
continuous: true

bluetooth_proxy:

captive_portal:

web_server:
port: 80
version: 3

http_request:
verify_ssl: true
# GitHub release-asset downloads answer with a redirect carrying a
# ~3.6 KB Content-Security-Policy header; each header line must fit
# this buffer or the request fails with "HTTP_CLIENT: Out of buffer".
buffer_size_rx: 5120
# The redirect target is a signed URL with a ~850-char query string; the
# follow-up request line must fit the TX buffer or esp_http_client_open
# fails with "Out of buffer" before sending anything.
buffer_size_tx: 2048

api:
encryption:
on_client_connected:
- delay: 1s
- light.turn_off: rgb_light
Expand Down Expand Up @@ -651,6 +709,21 @@ button:
value: 420
id: scd40

- 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);
Comment on lines +720 to +725

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Avoid racing the manifest refresh before starting OTA.

This flow schedules the manifest refresh, waits a fixed 5s, then forces perform(true). Since the comment says the fetch runs in another task, slow TLS/GitHub responses can leave this using stale or missing manifest data after a channel switch. Gate the install on refresh completion, or add a bounded retry/state check before forcing the update.

🤖 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 721 - 726, The OTA path in
apply_ota_source is still forcing update_http_request.perform(true) after a
fixed delay, which can race the async manifest refresh. Update the flow so the
install waits for the manifest refresh to finish before continuing, or add a
bounded retry/state check around update_http_request and the
script.execute/script.wait sequence to verify fresh manifest data is present
before starting OTA.


Comment thread
coderabbitai[bot] marked this conversation as resolved.

switch:
- platform: ld2410
Expand All @@ -677,6 +750,19 @@ switch:
optimistic: true
entity_category: "config"

- platform: template
name: "Bluetooth Proxy"
id: bluetooth_proxy_switch
icon: mdi:bluetooth
entity_category: "config"
restore_mode: RESTORE_DEFAULT_OFF
optimistic: true
on_turn_on:
- esp32_ble_tracker.start_scan:
continuous: true
on_turn_off:
- esp32_ble_tracker.stop_scan:

text_sensor:
- platform: ld2410
version:
Expand All @@ -702,6 +788,34 @@ select:
name: "Radar Distance Resolution"
disabled_by_default: true

- platform: template
name: "Firmware Channel"
id: firmware_channel
icon: mdi:source-branch
entity_category: "config"
optimistic: true
restore_value: true
options:
- "Stable"
- "Beta"
initial_option: "${firmware_channel_default}"
on_value:
then:
- script.execute: apply_ota_source

script:
- id: apply_ota_source
# Sets the OTA manifest URL from the Firmware Channel select (Stable/Beta).
# The manifest base is per-variant (ota_*_manifest substitutions) so legacy
# MSR-1_BLE builds keep their firmware-ble manifest.
then:
- lambda: |-
const bool beta = id(firmware_channel).current_option() == "Beta";
std::string url = beta ? "${ota_beta_manifest}" : "${ota_stable_manifest}";
ESP_LOGI("firmware", "OTA manifest set to: %s", url.c_str());
id(update_http_request).set_source_url(url);
- component.update: update_http_request



#Used To Control RGB Light On Startup. Tells User Status Of Device
Expand Down
Loading
Loading