From 36f5a2863e5952b99a3fae2ce96a7e71d033fb14 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:11:28 -0500 Subject: [PATCH 1/8] Fix Reduce DB Reporting freezing DPS310 pressure and ambient sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Reduce DB Reporting was on, the DPS310 pressure sensor only reported on a >= 5.0 hPa change. Barometric pressure normally drifts 1-3 hPa a day, so the reading could freeze for hours and look like a dead sensor. Give the reduce-gated ambient sensors (pressure, ESP temp, LTR390 light/UV) a delta-OR-hourly-heartbeat floor, and publish the first post-boot reading so a value always shows on boot. Lower the pressure delta to 0.5 hPa and move the DPS310 poll to 60s. Radar filters and the toggle-off path are unchanged. Fixes #70 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Integrations/ESPHome/Core.yaml | 85 +++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 64bcb20..0fbf2a0 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,6 +1,6 @@ substitutions: name: apollo-msr-2 - version: "26.7.12.1" + version: "26.7.14.1" device_description: ${name} made by Apollo Automation - version ${version}. # 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 @@ -426,22 +426,24 @@ sensor: id: sys_esp_temperature filters: - lambda: |- - static float last_reported_value = -6.0; + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; float current_value = x; - // Check if the reduce_db_reporting switch is on + // Reduce DB Reporting: report on a meaningful change, but at least hourly so the sensor never looks frozen if (id(reduce_db_reporting).state) { - // Apply delta filter: only report if the value has changed by 5 or more - if (abs(current_value - last_reported_value) >= 5.0) { - last_reported_value = current_value; // Update the last reported value + uint32_t now = millis(); + if (isnan(last_reported_value) || + abs(current_value - last_reported_value) >= 5.0 || + (now - last_report_time) >= 3600000UL) { + last_reported_value = current_value; + last_report_time = now; return current_value; - } else { - // Return the last reported value without updating if change is less than 5 - return {}; // Discard the update } + return {}; // Within delta and under the hourly heartbeat -> discard } else { - // If reduce_db_reporting is off, report the current value normally last_reported_value = current_value; + last_report_time = millis(); return current_value; } @@ -644,22 +646,24 @@ sensor: id: ltr390light filters: - lambda: |- - static float last_reported_value = -21.0; + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; float current_value = x; - // Check if the reduce_db_reporting switch is on + // Reduce DB Reporting: report on a meaningful change, but at least hourly so the sensor never looks frozen if (id(reduce_db_reporting).state) { - // Apply delta filter: only report if the value has changed by 5 or more - if (abs(current_value - last_reported_value) >= 5.0) { - last_reported_value = current_value; // Update the last reported value + uint32_t now = millis(); + if (isnan(last_reported_value) || + abs(current_value - last_reported_value) >= 5.0 || + (now - last_report_time) >= 3600000UL) { + last_reported_value = current_value; + last_report_time = now; return current_value; - } else { - // Return the last reported value without updating if change is less than 20 - return {}; // Discard the update } + return {}; // Within delta and under the hourly heartbeat -> discard } else { - // If reduce_db_reporting is off, report the current value normally last_reported_value = current_value; + last_report_time = millis(); return current_value; } uv_index: @@ -667,22 +671,24 @@ sensor: id: ltr390uvindex filters: - lambda: |- - static float last_reported_value = -21.0; + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; float current_value = x; - // Check if the reduce_db_reporting switch is on + // Reduce DB Reporting: report on a meaningful change, but at least hourly so the sensor never looks frozen if (id(reduce_db_reporting).state) { - // Apply delta filter: only report if the value has changed by 1 or more - if (abs(current_value - last_reported_value) >= 1.0) { - last_reported_value = current_value; // Update the last reported value + uint32_t now = millis(); + if (isnan(last_reported_value) || + abs(current_value - last_reported_value) >= 1.0 || + (now - last_report_time) >= 3600000UL) { + last_reported_value = current_value; + last_report_time = now; return current_value; - } else { - // Return the last reported value without updating if change is less than 20 - return {}; // Discard the update } + return {}; // Within delta and under the hourly heartbeat -> discard } else { - // If reduce_db_reporting is off, report the current value normally last_reported_value = current_value; + last_report_time = millis(); return current_value; } @@ -693,26 +699,29 @@ sensor: id: dps310pressure filters: - lambda: |- - static float last_reported_value = -6.0; + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; float current_value = x; float offset = id(dps310_pressure_offset).state; if (!isnan(offset)) { current_value += offset; } - // Check if the reduce_db_reporting switch is on + // Reduce DB Reporting: report on a meaningful change, but at least hourly so the sensor never looks frozen. + // Barometric pressure normally drifts only 1-3 hPa a day, so the old 5 hPa delta could freeze the reading for hours. if (id(reduce_db_reporting).state) { - // Apply delta filter: only report if the value has changed by 5 or more - if (abs(current_value - last_reported_value) >= 5.0) { - last_reported_value = current_value; // Update the last reported value + uint32_t now = millis(); + if (isnan(last_reported_value) || + abs(current_value - last_reported_value) >= 0.5 || + (now - last_report_time) >= 3600000UL) { + last_reported_value = current_value; + last_report_time = now; return current_value; - } else { - // Return the last reported value without updating if change is less than 5 - return {}; // Discard the update } + return {}; // Within delta and under the hourly heartbeat -> discard } else { - // If reduce_db_reporting is off, report the current value normally last_reported_value = current_value; + last_report_time = millis(); return current_value; } temperature: @@ -720,7 +729,7 @@ sensor: id: dps310temperature filters: - lambda: return x - id(dps310_temperature_offset).state; - update_interval: 30s + update_interval: 60s i2c_id: bus_a From 54c9d944294c2936f0788809fd9d50051bd36af9 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Tue, 28 Jul 2026 19:44:52 -0500 Subject: [PATCH 2/8] Keep Bluetooth Proxy on for legacy BLE builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Bluetooth Proxy switch does restore from flash, but the _OFF half of RESTORE_DEFAULT_OFF decides the boot where nothing is stored yet, which is the first boot after the switch shipped. MSR-2_BLE.yaml inherited that mode from Core, so legacy BLE devices that had always proxied came up with the proxy off after updating. Core now reads the mode from a bluetooth_proxy_restore_mode substitution defaulting to RESTORE_DEFAULT_OFF, and MSR-2_BLE.yaml overrides it to RESTORE_DEFAULT_ON, the same override pattern the file already uses for its OTA manifest URLs. The unified image is unchanged and a user's saved choice still wins on every later boot. Refs ApolloAutomation/MSR-2#92 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Integrations/ESPHome/Core.yaml | 12 +++++++++--- Integrations/ESPHome/MSR-2_BLE.yaml | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index acc9363..34fcfe6 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,6 +1,6 @@ substitutions: name: apollo-msr-2 - version: "26.7.14.1" + version: "26.7.29.1" device_description: ${name} made by Apollo Automation - version ${version}. # 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 @@ -17,6 +17,12 @@ substitutions: ota_stable_manifest: "${stable_manifest_base}/firmware/manifest.json" ota_beta_manifest: "${beta_manifest_base}/manifest-standard.json" + # Bluetooth Proxy switch state on a device with no stored preference yet + # (first boot after this switch was added, or after a factory reset). The + # unified image ships the proxy off; MSR-2_BLE.yaml overrides this to ON so + # legacy BLE devices keep proxying instead of going quiet after an update. + bluetooth_proxy_restore_mode: RESTORE_DEFAULT_OFF + 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). @@ -25,7 +31,7 @@ esphome: 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). + # scanning matches the switch state restored from flash. - priority: -300 then: - if: @@ -867,7 +873,7 @@ switch: id: bluetooth_proxy_switch icon: mdi:bluetooth entity_category: "config" - restore_mode: RESTORE_DEFAULT_OFF + restore_mode: ${bluetooth_proxy_restore_mode} optimistic: true on_turn_on: - esp32_ble_tracker.start_scan: diff --git a/Integrations/ESPHome/MSR-2_BLE.yaml b/Integrations/ESPHome/MSR-2_BLE.yaml index 7e1e13a..2b0c1db 100644 --- a/Integrations/ESPHome/MSR-2_BLE.yaml +++ b/Integrations/ESPHome/MSR-2_BLE.yaml @@ -3,6 +3,10 @@ substitutions: ota_stable_manifest: "https://apolloautomation.github.io/MSR-2/firmware-ble/manifest.json" ota_beta_manifest: "https://github.com/ApolloAutomation/MSR-2/releases/download/beta-fw/manifest-ble.json" + # Legacy BLE devices proxied unconditionally before the Bluetooth Proxy + # switch existed, so default it on rather than dropping the proxy on update. + bluetooth_proxy_restore_mode: RESTORE_DEFAULT_ON + esphome: name: "${name}" friendly_name: Apollo MSR-2 From cd59f31438018f47be83c8a28fe34a98a1870fcf Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Tue, 18 Aug 2026 10:23:02 -0500 Subject: [PATCH 3/8] Bump version to 26.8.18.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-dated for the merge day per versioning policy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Integrations/ESPHome/Core.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 34fcfe6..eefcf7b 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,6 +1,6 @@ substitutions: name: apollo-msr-2 - version: "26.7.29.1" + version: "26.8.18.1" device_description: ${name} made by Apollo Automation - version ${version}. # 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 From 46947ae8236ae9633d5ae1a9bec48396aed4e0cf Mon Sep 17 00:00:00 2001 From: Justin Bunton Date: Thu, 27 Aug 2026 23:33:10 -0400 Subject: [PATCH 4/8] Replace deprecated rgb_order with channel_colors ESPHome 2026.8.0 replaces rgb_order/is_rgbw/is_wrgb with a single channel_colors key on esp32_rmt_led_strip (esphome#18474). --- Integrations/ESPHome/Core.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index eefcf7b..5a1c814 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -748,7 +748,7 @@ light: default_transition_length: 0s chipset: WS2812 num_leds: 3 - rgb_order: grb + channel_colors: GRB effects: - pulse: name: "Slow Pulse" From 5f165dc9489ad63ee0c03c77c98ce6a4501ecffd Mon Sep 17 00:00:00 2001 From: Justin Bunton Date: Thu, 27 Aug 2026 23:36:23 -0400 Subject: [PATCH 5/8] Bump version to 26.8.27.1 --- Integrations/ESPHome/Core.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 5a1c814..14d4ccc 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,6 +1,6 @@ substitutions: name: apollo-msr-2 - version: "26.8.18.1" + version: "26.8.27.1" device_description: ${name} made by Apollo Automation - version ${version}. # 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 From 752307b3d1d2cfbcfc712b577fe803ded840520d Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Sun, 30 Aug 2026 11:39:26 -0500 Subject: [PATCH 6/8] Require ESPHome 2026.8.0 for channel_colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `channel_colors` only exists from ESPHome 2026.8.0 (esphome#18474), but these YAMLs still declared an older min_version. Devices adopted through the ESPHome dashboard track this repo at HEAD with no pinned ref, so on an older ESPHome the next recompile fails with "Unknown option channel_colors" pointing into a file the user never wrote, instead of a clear version error. ESPHome merges remote packages and checks min_version in the preload phase, before component schemas load, so the bump produces the right message. No firmware change: min_version is validation-only and emits no code, so the compiled binary is unaffected. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Integrations/ESPHome/MSR-2.yaml | 2 +- Integrations/ESPHome/MSR-2_BLE.yaml | 2 +- Integrations/ESPHome/MSR-2_Factory.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Integrations/ESPHome/MSR-2.yaml b/Integrations/ESPHome/MSR-2.yaml index e234925..080ce67 100644 --- a/Integrations/ESPHome/MSR-2.yaml +++ b/Integrations/ESPHome/MSR-2.yaml @@ -25,7 +25,7 @@ esphome: name: "ApolloAutomation.MSR-2" version: "${version}" - min_version: 2025.11.0 + min_version: 2026.8.0 dashboard_import: package_import_url: github://ApolloAutomation/MSR-2/Integrations/ESPHome/MSR-2.yaml diff --git a/Integrations/ESPHome/MSR-2_BLE.yaml b/Integrations/ESPHome/MSR-2_BLE.yaml index 2b0c1db..3a44049 100644 --- a/Integrations/ESPHome/MSR-2_BLE.yaml +++ b/Integrations/ESPHome/MSR-2_BLE.yaml @@ -34,7 +34,7 @@ esphome: name: "ApolloAutomation.MSR-2_BLE" version: "${version}" - min_version: 2025.11.0 + min_version: 2026.8.0 logger: diff --git a/Integrations/ESPHome/MSR-2_Factory.yaml b/Integrations/ESPHome/MSR-2_Factory.yaml index 4bb51ee..015761f 100644 --- a/Integrations/ESPHome/MSR-2_Factory.yaml +++ b/Integrations/ESPHome/MSR-2_Factory.yaml @@ -25,7 +25,7 @@ esphome: name: "ApolloAutomation.MSR-2_Factory" version: "${version}" - min_version: 2025.11.0 + min_version: 2026.8.0 dashboard_import: From b30a956eb6986a649a24d952c7487bad25665bfd Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Tue, 29 Sep 2026 17:03:28 -0500 Subject: [PATCH 7/8] Fix OTA manifest lambda for ESPHome dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Integrations/ESPHome/Core.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 13f8313..e3fcc2f 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,6 +1,6 @@ substitutions: name: apollo-msr-2 - version: "26.7.14.1" + version: "26.9.29.1" device_description: ${name} made by Apollo Automation - version ${version}. # 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 @@ -918,8 +918,8 @@ script: 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()); + const char *url = beta ? "${ota_beta_manifest}" : "${ota_stable_manifest}"; + ESP_LOGI("firmware", "OTA manifest set to: %s", url); id(update_http_request).set_source_url(url); - component.update: update_http_request From 0f44ee56e454c92b8f0a7aa5610b144d3569d6ff Mon Sep 17 00:00:00 2001 From: Trevor Schirmer <24777085+TrevorSchirmer@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:46:34 -0400 Subject: [PATCH 8/8] Bump version to 26.10.12.1 --- Integrations/ESPHome/Core.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index b1e1101..12b6cc4 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,6 +1,6 @@ substitutions: name: apollo-msr-2 - version: "26.9.29.1" + version: "26.10.12.1" device_description: ${name} made by Apollo Automation - version ${version}. # 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