From bcb43748b57dec9fa1a48dc6b80b521f10a76fd5 Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sat, 15 Aug 2026 15:42:36 +0300 Subject: [PATCH 01/14] add accuracy_decimals for each environmental sensor --- Integrations/ESPHome/Core.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 8626f08..1eec33f 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -302,6 +302,7 @@ sensor: co2: name: "CO2" id: "co2" + accuracy_decimals: 0 on_value: - if: condition: @@ -319,12 +320,14 @@ sensor: pressure: name: "DPS310 Pressure" id: dps310pressure + accuracy_decimals: 1 filters: - lambda: |- float offset = id(dps310_pressure_offset).state; return isnan(offset) ? x : x + offset; temperature: id: dps310temperature + accuracy_decimals: 1 update_interval: 30s i2c_id: bus_a @@ -361,6 +364,7 @@ sensor: voc_index: name: "SEN55 VOC" id: sen55_voc + accuracy_decimals: 0 on_value: - if: condition: @@ -378,6 +382,7 @@ sensor: nox_index: name: "SEN55 NOX" id: sen55_nox + accuracy_decimals: 0 acceleration_mode: low store_baseline: true # Some units misreport SEN54 on the product name register even though the @@ -395,6 +400,7 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: 1 update_interval: 10s - platform: template @@ -405,6 +411,7 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: 1 update_interval: 10s - platform: template @@ -415,6 +422,7 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: 1 update_interval: 10s - platform: template @@ -425,6 +433,7 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: 1 update_interval: 10s - platform: aqi @@ -434,6 +443,7 @@ sensor: pm_10_0: pm_10_0 calculation_type: AQI device_class: aqi + accuracy_decimals: 0 on_value: - if: condition: @@ -446,21 +456,27 @@ sensor: nitrogen_dioxide: name: Nitrogen Dioxide id: nitrogen_dioxide + accuracy_decimals: 2 carbon_monoxide: name: Carbon Monoxide id: carbon_monoxide + accuracy_decimals: 2 hydrogen: name: Hydrogen id: hydrogen + accuracy_decimals: 2 ethanol: name: Ethanol id: ethanol + accuracy_decimals: 1 methane: name: Methane id: methane + accuracy_decimals: 1 ammonia: name: Ammonia id: ammonia + accuracy_decimals: 1 update_interval: 60s light: From a83b9f666593580fc558d07ddcee938b1ea40c7c Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sat, 15 Aug 2026 16:35:28 +0300 Subject: [PATCH 02/14] add round filter matching accuracy_decimals --- Integrations/ESPHome/Core.yaml | 43 +++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 1eec33f..14ffa59 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -302,6 +302,8 @@ sensor: co2: name: "CO2" id: "co2" + filters: + - round: 0 accuracy_decimals: 0 on_value: - if: @@ -320,13 +322,16 @@ sensor: pressure: name: "DPS310 Pressure" id: dps310pressure - accuracy_decimals: 1 filters: - lambda: |- float offset = id(dps310_pressure_offset).state; return isnan(offset) ? x : x + offset; + - round: 1 + accuracy_decimals: 1 temperature: id: dps310temperature + filters: + - round: 1 accuracy_decimals: 1 update_interval: 30s i2c_id: bus_a @@ -336,34 +341,46 @@ sensor: pm_1_0: name: "PM <1µm Weight concentration" id: pm_1_0 + filters: + - round: 1 accuracy_decimals: 1 pm_2_5: name: "PM <2.5µm Weight concentration" id: pm_2_5 + filters: + - round: 1 accuracy_decimals: 1 pm_4_0: name: "PM <4µm Weight concentration" id: pm_4_0 + filters: + - round: 1 accuracy_decimals: 1 pm_10_0: name: "PM <10µm Weight concentration" id: pm_10_0 + filters: + - round: 1 accuracy_decimals: 1 temperature: name: "SEN55 Temperature" id: sen55_temperature filters: - lambda: return x - id(sen55_temperature_offset).state; + - round: 1 accuracy_decimals: 1 humidity: name: "SEN55 Humidity" id: sen55_humidity filters: - lambda: return x - id(sen55_humidity_offset).state; + - round: 1 accuracy_decimals: 1 voc_index: name: "SEN55 VOC" id: sen55_voc + filters: + - round: 0 accuracy_decimals: 0 on_value: - if: @@ -382,6 +399,8 @@ sensor: nox_index: name: "SEN55 NOX" id: sen55_nox + filters: + - round: 0 accuracy_decimals: 0 acceleration_mode: low store_baseline: true @@ -400,6 +419,8 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + filters: + - round: 1 accuracy_decimals: 1 update_interval: 10s @@ -411,6 +432,8 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + filters: + - round: 1 accuracy_decimals: 1 update_interval: 10s @@ -422,6 +445,8 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + filters: + - round: 1 accuracy_decimals: 1 update_interval: 10s @@ -433,6 +458,8 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + filters: + - round: 1 accuracy_decimals: 1 update_interval: 10s @@ -443,6 +470,8 @@ sensor: pm_10_0: pm_10_0 calculation_type: AQI device_class: aqi + filters: + - round: 0 accuracy_decimals: 0 on_value: - if: @@ -456,26 +485,38 @@ sensor: nitrogen_dioxide: name: Nitrogen Dioxide id: nitrogen_dioxide + filters: + - round: 2 accuracy_decimals: 2 carbon_monoxide: name: Carbon Monoxide id: carbon_monoxide + filters: + - round: 2 accuracy_decimals: 2 hydrogen: name: Hydrogen id: hydrogen + filters: + - round: 2 accuracy_decimals: 2 ethanol: name: Ethanol id: ethanol + filters: + - round: 1 accuracy_decimals: 1 methane: name: Methane id: methane + filters: + - round: 1 accuracy_decimals: 1 ammonia: name: Ammonia id: ammonia + filters: + - round: 1 accuracy_decimals: 1 update_interval: 60s From e66d3ace312def965b3943c6778a3f1e1f5fcf1f Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sat, 15 Aug 2026 13:39:22 +0300 Subject: [PATCH 03/14] add reduce DB reporting option --- Integrations/ESPHome/Core.yaml | 217 ++++++++++++++++++++++++++++++++- 1 file changed, 216 insertions(+), 1 deletion(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 14ffa59..c6144ea 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -325,7 +325,22 @@ sensor: filters: - lambda: |- float offset = id(dps310_pressure_offset).state; - return isnan(offset) ? x : x + offset; + float current_value = isnan(offset) ? x : x + offset; + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = current_value; + last_report_time = millis(); + return current_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; + } + return {}; - round: 1 accuracy_decimals: 1 temperature: @@ -342,24 +357,88 @@ sensor: name: "PM <1µm Weight concentration" id: pm_1_0 filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 pm_2_5: name: "PM <2.5µm Weight concentration" id: pm_2_5 filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 pm_4_0: name: "PM <4µm Weight concentration" id: pm_4_0 filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 pm_10_0: name: "PM <10µm Weight concentration" id: pm_10_0 filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 temperature: @@ -367,6 +446,22 @@ sensor: id: sen55_temperature filters: - lambda: return x - id(sen55_temperature_offset).state; + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 0.5 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 humidity: @@ -374,12 +469,44 @@ sensor: id: sen55_humidity filters: - lambda: return x - id(sen55_humidity_offset).state; + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 1.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 voc_index: name: "SEN55 VOC" id: sen55_voc filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 0 accuracy_decimals: 0 on_value: @@ -400,6 +527,22 @@ sensor: name: "SEN55 NOX" id: sen55_nox filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 10.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 0 accuracy_decimals: 0 acceleration_mode: low @@ -420,6 +563,22 @@ sensor: state_class: measurement icon: mdi:air-filter filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 update_interval: 10s @@ -433,6 +592,22 @@ sensor: state_class: measurement icon: mdi:air-filter filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 update_interval: 10s @@ -446,6 +621,22 @@ sensor: state_class: measurement icon: mdi:air-filter filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 update_interval: 10s @@ -459,6 +650,22 @@ sensor: state_class: measurement icon: mdi:air-filter filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = millis(); + return x; + } + uint32_t now = millis(); + if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || + now - last_report_time >= 3600000UL) { + last_reported_value = x; + last_report_time = now; + return x; + } + return {}; - round: 1 accuracy_decimals: 1 update_interval: 10s @@ -637,6 +844,14 @@ switch: id: factory_reset_switch internal: true + - platform: template + name: "Reduce DB Reporting" + id: reduce_db_reporting + icon: mdi:database + restore_mode: RESTORE_DEFAULT_OFF + optimistic: true + entity_category: "config" + - platform: template name: "Prevent Sleep" id: prevent_sleep From df26ae70d83a4d338a44992d1e7d3ca46db82e52 Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sun, 16 Aug 2026 16:07:06 +0300 Subject: [PATCH 04/14] extract reduced reporting thresholds to variables --- Integrations/ESPHome/Core.yaml | 179 +++++++++++++++++++++++---------- 1 file changed, 127 insertions(+), 52 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index c6144ea..7327c27 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -2,6 +2,16 @@ substitutions: name: apollo-air-1 version: "26.10.16.1" device_description: ${name} made by Apollo Automation - version ${version}. + + # Reduced DB Reporting thresholds + reduced_reporting_heartbeat_ms: "3600000UL" + pressure_reporting_delta: "0.5" + pm_reporting_delta: "5.0" + temperature_reporting_delta: "0.5" + humidity_reporting_delta: "1.0" + voc_reporting_delta: "5.0" + nox_reporting_delta: "10.0" + # Default OTA password. Override in your device YAML by re-declaring # `substitutions: { ota_password: !secret _ota_password }` so each # device on your network uses a unique secret instead of the shared default. @@ -328,18 +338,23 @@ sensor: float current_value = isnan(offset) ? x : x + offset; static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = current_value; - last_report_time = millis(); + last_report_time = now; return current_value; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(current_value - last_reported_value) >= 0.5 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(current_value - last_reported_value) >= ${pressure_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = current_value; last_report_time = now; return current_value; } + return {}; - round: 1 accuracy_decimals: 1 @@ -360,18 +375,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -382,18 +402,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -404,18 +429,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -426,18 +456,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -449,18 +484,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 0.5 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${temperature_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -472,18 +512,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 1.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${humidity_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -494,18 +539,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${voc_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 0 accuracy_decimals: 0 @@ -530,18 +580,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 10.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${nox_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 0 accuracy_decimals: 0 @@ -566,18 +621,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -595,18 +655,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -624,18 +689,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 @@ -653,18 +723,23 @@ sensor: - lambda: |- static float last_reported_value = NAN; static uint32_t last_report_time = 0; + const uint32_t now = millis(); + if (!id(reduce_db_reporting).state) { last_reported_value = x; - last_report_time = millis(); + last_report_time = now; return x; } - uint32_t now = millis(); - if (isnan(last_reported_value) || abs(x - last_reported_value) >= 5.0 || - now - last_report_time >= 3600000UL) { + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; last_report_time = now; return x; } + return {}; - round: 1 accuracy_decimals: 1 From 2a53bcc9e19bbd5235be46e747ba0f28bcc21f9c Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sun, 16 Aug 2026 16:13:43 +0300 Subject: [PATCH 05/14] extract display decimal places to variables --- Integrations/ESPHome/Core.yaml | 104 +++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 7327c27..538b19c 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -12,6 +12,22 @@ substitutions: voc_reporting_delta: "5.0" nox_reporting_delta: "10.0" + # Sensor decimal places + co2_decimal_places: "0" + pressure_decimal_places: "1" + temperature_decimal_places: "1" + humidity_decimal_places: "1" + pm_decimal_places: "1" + voc_decimal_places: "0" + nox_decimal_places: "0" + aqi_decimal_places: "0" + nitrogen_dioxide_decimal_places: "2" + carbon_monoxide_decimal_places: "2" + hydrogen_decimal_places: "2" + ethanol_decimal_places: "1" + methane_decimal_places: "1" + ammonia_decimal_places: "1" + # Default OTA password. Override in your device YAML by re-declaring # `substitutions: { ota_password: !secret _ota_password }` so each # device on your network uses a unique secret instead of the shared default. @@ -313,8 +329,8 @@ sensor: name: "CO2" id: "co2" filters: - - round: 0 - accuracy_decimals: 0 + - round: ${co2_decimal_places} + accuracy_decimals: ${co2_decimal_places} on_value: - if: condition: @@ -356,13 +372,13 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pressure_decimal_places} + accuracy_decimals: ${pressure_decimal_places} temperature: id: dps310temperature filters: - - round: 1 - accuracy_decimals: 1 + - round: ${temperature_decimal_places} + accuracy_decimals: ${temperature_decimal_places} update_interval: 30s i2c_id: bus_a @@ -393,8 +409,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pm_decimal_places} + accuracy_decimals: ${pm_decimal_places} pm_2_5: name: "PM <2.5µm Weight concentration" id: pm_2_5 @@ -420,8 +436,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pm_decimal_places} + accuracy_decimals: ${pm_decimal_places} pm_4_0: name: "PM <4µm Weight concentration" id: pm_4_0 @@ -447,8 +463,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pm_decimal_places} + accuracy_decimals: ${pm_decimal_places} pm_10_0: name: "PM <10µm Weight concentration" id: pm_10_0 @@ -474,8 +490,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pm_decimal_places} + accuracy_decimals: ${pm_decimal_places} temperature: name: "SEN55 Temperature" id: sen55_temperature @@ -502,8 +518,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${temperature_decimal_places} + accuracy_decimals: ${temperature_decimal_places} humidity: name: "SEN55 Humidity" id: sen55_humidity @@ -530,8 +546,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${humidity_decimal_places} + accuracy_decimals: ${humidity_decimal_places} voc_index: name: "SEN55 VOC" id: sen55_voc @@ -557,8 +573,8 @@ sensor: } return {}; - - round: 0 - accuracy_decimals: 0 + - round: ${voc_decimal_places} + accuracy_decimals: ${voc_decimal_places} on_value: - if: condition: @@ -598,8 +614,8 @@ sensor: } return {}; - - round: 0 - accuracy_decimals: 0 + - round: ${nox_decimal_places} + accuracy_decimals: ${nox_decimal_places} acceleration_mode: low store_baseline: true # Some units misreport SEN54 on the product name register even though the @@ -639,8 +655,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pm_decimal_places} + accuracy_decimals: ${pm_decimal_places} update_interval: 10s - platform: template @@ -673,8 +689,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pm_decimal_places} + accuracy_decimals: ${pm_decimal_places} update_interval: 10s - platform: template @@ -707,8 +723,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pm_decimal_places} + accuracy_decimals: ${pm_decimal_places} update_interval: 10s - platform: template @@ -741,8 +757,8 @@ sensor: } return {}; - - round: 1 - accuracy_decimals: 1 + - round: ${pm_decimal_places} + accuracy_decimals: ${pm_decimal_places} update_interval: 10s - platform: aqi @@ -753,8 +769,8 @@ sensor: calculation_type: AQI device_class: aqi filters: - - round: 0 - accuracy_decimals: 0 + - round: ${aqi_decimal_places} + accuracy_decimals: ${aqi_decimal_places} on_value: - if: condition: @@ -768,38 +784,38 @@ sensor: name: Nitrogen Dioxide id: nitrogen_dioxide filters: - - round: 2 - accuracy_decimals: 2 + - round: ${nitrogen_dioxide_decimal_places} + accuracy_decimals: ${nitrogen_dioxide_decimal_places} carbon_monoxide: name: Carbon Monoxide id: carbon_monoxide filters: - - round: 2 - accuracy_decimals: 2 + - round: ${carbon_monoxide_decimal_places} + accuracy_decimals: ${carbon_monoxide_decimal_places} hydrogen: name: Hydrogen id: hydrogen filters: - - round: 2 - accuracy_decimals: 2 + - round: ${hydrogen_decimal_places} + accuracy_decimals: ${hydrogen_decimal_places} ethanol: name: Ethanol id: ethanol filters: - - round: 1 - accuracy_decimals: 1 + - round: ${ethanol_decimal_places} + accuracy_decimals: ${ethanol_decimal_places} methane: name: Methane id: methane filters: - - round: 1 - accuracy_decimals: 1 + - round: ${methane_decimal_places} + accuracy_decimals: ${methane_decimal_places} ammonia: name: Ammonia id: ammonia filters: - - round: 1 - accuracy_decimals: 1 + - round: ${ammonia_decimal_places} + accuracy_decimals: ${ammonia_decimal_places} update_interval: 60s light: From 48f0f8550e35006dbdf1c83ef7236c26bcc4d2be Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sun, 16 Aug 2026 16:18:21 +0300 Subject: [PATCH 06/14] add reduced reporting for remaining sensors --- Integrations/ESPHome/Core.yaml | 177 ++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 1 deletion(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 538b19c..ecf7ff5 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -10,7 +10,14 @@ substitutions: temperature_reporting_delta: "0.5" humidity_reporting_delta: "1.0" voc_reporting_delta: "5.0" - nox_reporting_delta: "10.0" + nox_reporting_delta: "1.0" + co2_reporting_delta: "10.0" + nitrogen_dioxide_reporting_delta: "0.05" + carbon_monoxide_reporting_delta: "1.0" + hydrogen_reporting_delta: "1.0" + ethanol_reporting_delta: "0.5" + methane_reporting_delta: "100.0" + ammonia_reporting_delta: "1.0" # Sensor decimal places co2_decimal_places: "0" @@ -329,6 +336,27 @@ sensor: name: "CO2" id: "co2" filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${co2_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; - round: ${co2_decimal_places} accuracy_decimals: ${co2_decimal_places} on_value: @@ -377,6 +405,27 @@ sensor: temperature: id: dps310temperature filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${temperature_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; - round: ${temperature_decimal_places} accuracy_decimals: ${temperature_decimal_places} update_interval: 30s @@ -784,36 +833,162 @@ sensor: name: Nitrogen Dioxide id: nitrogen_dioxide filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${nitrogen_dioxide_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; - round: ${nitrogen_dioxide_decimal_places} accuracy_decimals: ${nitrogen_dioxide_decimal_places} carbon_monoxide: name: Carbon Monoxide id: carbon_monoxide filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${carbon_monoxide_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; - round: ${carbon_monoxide_decimal_places} accuracy_decimals: ${carbon_monoxide_decimal_places} hydrogen: name: Hydrogen id: hydrogen filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${hydrogen_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; - round: ${hydrogen_decimal_places} accuracy_decimals: ${hydrogen_decimal_places} ethanol: name: Ethanol id: ethanol filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${ethanol_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; - round: ${ethanol_decimal_places} accuracy_decimals: ${ethanol_decimal_places} methane: name: Methane id: methane filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${methane_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; - round: ${methane_decimal_places} accuracy_decimals: ${methane_decimal_places} ammonia: name: Ammonia id: ammonia filters: + - lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${ammonia_reporting_delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; - round: ${ammonia_decimal_places} accuracy_decimals: ${ammonia_decimal_places} update_interval: 60s From 167a6566fa7ab1acc27ac2f22134a9061c386ee6 Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sun, 16 Aug 2026 15:54:14 +0300 Subject: [PATCH 07/14] format --- Integrations/ESPHome/Core.yaml | 51 +++++++++++++++++----------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index ecf7ff5..742b658 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -39,15 +39,18 @@ substitutions: # `substitutions: { ota_password: !secret _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-fw" pre-release assets (beta branch builds). stable_manifest_base: "https://apolloautomation.github.io/AIR-1" beta_manifest_base: "https://github.com/ApolloAutomation/AIR-1/releases/download/beta-fw" + # Per-variant OTA manifest URLs. Core defaults to the unified (standard) # image; AIR-1_BLE.yaml overrides these so legacy BLE devices stay on the # firmware-ble manifest. @@ -119,9 +122,9 @@ api: - binary_sensor.is_on: ota_mode - switch.is_on: prevent_sleep then: - - lambda: |- + - lambda: |- ESP_LOGW("Apollo", "Preventing Deep Sleep Due To OTA Or Switch"); - id(deep_sleep_1).prevent_deep_sleep(); + id(deep_sleep_1).prevent_deep_sleep(); else: - lambda: |- id(reportAllValues).execute(); @@ -151,11 +154,11 @@ globals: - id: cycleCounter type: int restore_value: no - initial_value: '0' + initial_value: "0" - id: button_press_timestamp restore_value: no type: uint32_t - initial_value: '0' + initial_value: "0" - id: runTest restore_value: yes type: bool @@ -169,7 +172,6 @@ globals: type: bool initial_value: "false" - i2c: sda: GPIO1 scl: GPIO0 @@ -257,25 +259,24 @@ number: then: - lambda: |- id(deep_sleep_1).set_sleep_duration(x * 60 * 1000); - binary_sensor: - platform: status name: Online id: ink_ha_connected - platform: gpio - pin: + pin: number: GPIO9 inverted: true mode: input: true pullup: true ignore_strapping_warning: true - id: reset_button + id: reset_button on_press: then: - lambda: |- - id(button_press_timestamp) = millis(); + id(button_press_timestamp) = millis(); on_release: then: - lambda: |- @@ -303,15 +304,15 @@ binary_sensor: entity_id: input_boolean.apollo_ota_mode on_press: then: - - lambda: |- + - lambda: |- id(deep_sleep_1).prevent_deep_sleep(); - on_release: + on_release: then: - if: condition: switch.is_off: prevent_sleep then: - - lambda: |- + - lambda: |- id(deep_sleep_1).allow_deep_sleep(); sensor: @@ -707,7 +708,7 @@ sensor: - round: ${pm_decimal_places} accuracy_decimals: ${pm_decimal_places} update_interval: 10s - + - platform: template name: "PM 1 To 2.5 µm" id: pm1_to_2_5 @@ -741,7 +742,7 @@ sensor: - round: ${pm_decimal_places} accuracy_decimals: ${pm_decimal_places} update_interval: 10s - + - platform: template name: "PM 2.5 To 4 µm" id: pm2_5_to_4 @@ -1069,9 +1070,9 @@ button: id(deep_sleep_1).allow_deep_sleep(); text_sensor: - # Convert VOC Index To Text: - # https://sensirion.com/media/documents/02232963/6294E043/Info_Note_VOC_Index.pdf - # https://sensirion.com/media/documents/ACD82D45/6294DFC0/Info_Note_Integration_VOC_NOx_Sensor.pdf + # Convert VOC Index To Text: + # https://sensirion.com/media/documents/02232963/6294E043/Info_Note_VOC_Index.pdf + # https://sensirion.com/media/documents/ACD82D45/6294DFC0/Info_Note_Integration_VOC_NOx_Sensor.pdf - platform: template name: "VOC Quality" id: voc_quality @@ -1243,7 +1244,7 @@ script: - id: testScript then: - if: + if: condition: - lambda: "return id(runTest) == true;" then: @@ -1262,7 +1263,7 @@ script: - lambda: "return id(dps310pressure).state > 800 && id(dps310pressure).state < 1200;" then: - lambda: "id(dps310Passed) = true;" - + - if: condition: - lambda: "return id(dps310Passed);" @@ -1271,7 +1272,7 @@ script: - lambda: "id(runTest) = false;" - delay: 1s - lambda: "id(testCycleCount) += 1;" - + #Check If Test Passed To Trigger Lights - if: condition: @@ -1291,7 +1292,7 @@ script: blue: 0% - light.turn_off: id: rgb_light - + else: - lambda: "id(runTest) = false;" - light.turn_on: @@ -1314,10 +1315,10 @@ script: then: - if: condition: - - lambda: 'return id(ink_ha_connected).state;' + - lambda: "return id(ink_ha_connected).state;" then: - logger.log: "Apollo Automation: Connected To HA" - - light.turn_on: + - light.turn_on: id: rgb_light brightness: 100% red: 0% @@ -1329,7 +1330,7 @@ script: - wifi.connected then: - logger.log: "Apollo Automation: Connected To Wifi" - - light.turn_on: + - light.turn_on: id: rgb_light brightness: 100% red: 0% @@ -1337,7 +1338,7 @@ script: blue: 0% else: - logger.log: "Apollo Automation: Not Connected To Wifi" - - light.turn_on: + - light.turn_on: id: rgb_light brightness: 100% red: 100% From 006b556d30985a67a7d801436ca41a674f1b5677 Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Mon, 24 Aug 2026 21:59:44 +0300 Subject: [PATCH 08/14] remove reduced reporting filter from NOX --- Integrations/ESPHome/Core.yaml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 742b658..be7ac23 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -642,28 +642,6 @@ sensor: nox_index: name: "SEN55 NOX" id: sen55_nox - filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${nox_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; - round: ${nox_decimal_places} accuracy_decimals: ${nox_decimal_places} acceleration_mode: low From aa4aab1eaae4e4a6c56a672d74c020800a745962 Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Mon, 17 Aug 2026 17:45:10 +0300 Subject: [PATCH 09/14] remove rounding filters --- Integrations/ESPHome/Core.yaml | 87 +++++++++++++--------------------- 1 file changed, 32 insertions(+), 55 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index be7ac23..376705c 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -333,9 +333,15 @@ sensor: - platform: scd4x id: scd40 + automatic_self_calibration: true + update_interval: 60s + measurement_mode: "periodic" + i2c_id: bus_a + ambient_pressure_compensation_source: dps310pressure co2: name: "CO2" id: "co2" + accuracy_decimals: ${co2_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -358,25 +364,21 @@ sensor: } return {}; - - round: ${co2_decimal_places} - accuracy_decimals: ${co2_decimal_places} on_value: - if: condition: lambda: 'return id(air_quality_led_source).current_option() == "CO2";' then: - script.execute: update_air_quality_led - automatic_self_calibration: true - update_interval: 60s - measurement_mode: "periodic" - i2c_id: bus_a - ambient_pressure_compensation_source: dps310pressure - platform: dps310 id: dps_310 + i2c_id: bus_a + update_interval: 30s pressure: name: "DPS310 Pressure" id: dps310pressure + accuracy_decimals: ${pressure_decimal_places} filters: - lambda: |- float offset = id(dps310_pressure_offset).state; @@ -401,10 +403,9 @@ sensor: } return {}; - - round: ${pressure_decimal_places} - accuracy_decimals: ${pressure_decimal_places} temperature: id: dps310temperature + accuracy_decimals: ${temperature_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -427,16 +428,13 @@ sensor: } return {}; - - round: ${temperature_decimal_places} - accuracy_decimals: ${temperature_decimal_places} - update_interval: 30s - i2c_id: bus_a - platform: sen5x id: sen55 pm_1_0: name: "PM <1µm Weight concentration" id: pm_1_0 + accuracy_decimals: ${pm_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -459,11 +457,10 @@ sensor: } return {}; - - round: ${pm_decimal_places} - accuracy_decimals: ${pm_decimal_places} pm_2_5: name: "PM <2.5µm Weight concentration" id: pm_2_5 + accuracy_decimals: ${pm_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -486,11 +483,10 @@ sensor: } return {}; - - round: ${pm_decimal_places} - accuracy_decimals: ${pm_decimal_places} pm_4_0: name: "PM <4µm Weight concentration" id: pm_4_0 + accuracy_decimals: ${pm_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -513,11 +509,10 @@ sensor: } return {}; - - round: ${pm_decimal_places} - accuracy_decimals: ${pm_decimal_places} pm_10_0: name: "PM <10µm Weight concentration" id: pm_10_0 + accuracy_decimals: ${pm_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -540,11 +535,10 @@ sensor: } return {}; - - round: ${pm_decimal_places} - accuracy_decimals: ${pm_decimal_places} temperature: name: "SEN55 Temperature" id: sen55_temperature + accuracy_decimals: ${temperature_decimal_places} filters: - lambda: return x - id(sen55_temperature_offset).state; - lambda: |- @@ -568,11 +562,10 @@ sensor: } return {}; - - round: ${temperature_decimal_places} - accuracy_decimals: ${temperature_decimal_places} humidity: name: "SEN55 Humidity" id: sen55_humidity + accuracy_decimals: ${humidity_decimal_places} filters: - lambda: return x - id(sen55_humidity_offset).state; - lambda: |- @@ -596,11 +589,10 @@ sensor: } return {}; - - round: ${humidity_decimal_places} - accuracy_decimals: ${humidity_decimal_places} voc_index: name: "SEN55 VOC" id: sen55_voc + accuracy_decimals: ${voc_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -623,8 +615,6 @@ sensor: } return {}; - - round: ${voc_decimal_places} - accuracy_decimals: ${voc_decimal_places} on_value: - if: condition: @@ -642,7 +632,6 @@ sensor: nox_index: name: "SEN55 NOX" id: sen55_nox - - round: ${nox_decimal_places} accuracy_decimals: ${nox_decimal_places} acceleration_mode: low store_baseline: true @@ -661,6 +650,8 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: ${pm_decimal_places} + update_interval: 10s filters: - lambda: |- static float last_reported_value = NAN; @@ -683,9 +674,6 @@ sensor: } return {}; - - round: ${pm_decimal_places} - accuracy_decimals: ${pm_decimal_places} - update_interval: 10s - platform: template name: "PM 1 To 2.5 µm" @@ -695,6 +683,8 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: ${pm_decimal_places} + update_interval: 10s filters: - lambda: |- static float last_reported_value = NAN; @@ -717,9 +707,6 @@ sensor: } return {}; - - round: ${pm_decimal_places} - accuracy_decimals: ${pm_decimal_places} - update_interval: 10s - platform: template name: "PM 2.5 To 4 µm" @@ -729,6 +716,8 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: ${pm_decimal_places} + update_interval: 10s filters: - lambda: |- static float last_reported_value = NAN; @@ -751,9 +740,6 @@ sensor: } return {}; - - round: ${pm_decimal_places} - accuracy_decimals: ${pm_decimal_places} - update_interval: 10s - platform: template name: "PM 4 To 10 µm" @@ -763,6 +749,8 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: ${pm_decimal_places} + update_interval: 10s filters: - lambda: |- static float last_reported_value = NAN; @@ -785,9 +773,6 @@ sensor: } return {}; - - round: ${pm_decimal_places} - accuracy_decimals: ${pm_decimal_places} - update_interval: 10s - platform: aqi name: "NowCast AQI" @@ -796,8 +781,6 @@ sensor: pm_10_0: pm_10_0 calculation_type: AQI device_class: aqi - filters: - - round: ${aqi_decimal_places} accuracy_decimals: ${aqi_decimal_places} on_value: - if: @@ -808,9 +791,11 @@ sensor: - platform: mics_4514 id: mics4514 + update_interval: 60s nitrogen_dioxide: name: Nitrogen Dioxide id: nitrogen_dioxide + accuracy_decimals: ${nitrogen_dioxide_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -833,11 +818,10 @@ sensor: } return {}; - - round: ${nitrogen_dioxide_decimal_places} - accuracy_decimals: ${nitrogen_dioxide_decimal_places} carbon_monoxide: name: Carbon Monoxide id: carbon_monoxide + accuracy_decimals: ${carbon_monoxide_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -860,11 +844,10 @@ sensor: } return {}; - - round: ${carbon_monoxide_decimal_places} - accuracy_decimals: ${carbon_monoxide_decimal_places} hydrogen: name: Hydrogen id: hydrogen + accuracy_decimals: ${hydrogen_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -887,11 +870,10 @@ sensor: } return {}; - - round: ${hydrogen_decimal_places} - accuracy_decimals: ${hydrogen_decimal_places} ethanol: name: Ethanol id: ethanol + accuracy_decimals: ${ethanol_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -914,11 +896,10 @@ sensor: } return {}; - - round: ${ethanol_decimal_places} - accuracy_decimals: ${ethanol_decimal_places} methane: name: Methane id: methane + accuracy_decimals: ${methane_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -941,11 +922,10 @@ sensor: } return {}; - - round: ${methane_decimal_places} - accuracy_decimals: ${methane_decimal_places} ammonia: name: Ammonia id: ammonia + accuracy_decimals: ${ammonia_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -968,9 +948,6 @@ sensor: } return {}; - - round: ${ammonia_decimal_places} - accuracy_decimals: ${ammonia_decimal_places} - update_interval: 60s light: - platform: esp32_rmt_led_strip From a37c170aa16740eaa5ef4a747f8bec23652621cc Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:34:23 +0300 Subject: [PATCH 10/14] lower methane, hydrogen, and ethanol accuracy --- Integrations/ESPHome/Core.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 376705c..799698e 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -14,10 +14,10 @@ substitutions: co2_reporting_delta: "10.0" nitrogen_dioxide_reporting_delta: "0.05" carbon_monoxide_reporting_delta: "1.0" - hydrogen_reporting_delta: "1.0" + hydrogen_reporting_delta: "0.1" ethanol_reporting_delta: "0.5" - methane_reporting_delta: "100.0" - ammonia_reporting_delta: "1.0" + methane_reporting_delta: "0.1" + ammonia_reporting_delta: "0.1" # Sensor decimal places co2_decimal_places: "0" @@ -32,8 +32,8 @@ substitutions: carbon_monoxide_decimal_places: "2" hydrogen_decimal_places: "2" ethanol_decimal_places: "1" - methane_decimal_places: "1" - ammonia_decimal_places: "1" + methane_decimal_places: "2" + ammonia_decimal_places: "2" # Default OTA password. Override in your device YAML by re-declaring # `substitutions: { ota_password: !secret _ota_password }` so each From f66d611b714265d23c197b944731e75d8343d3b2 Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sun, 27 Sep 2026 19:59:25 +0300 Subject: [PATCH 11/14] reorder and rename variable names/definitions --- Integrations/ESPHome/Core.yaml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 799698e..f006efe 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -5,31 +5,30 @@ substitutions: # Reduced DB Reporting thresholds reduced_reporting_heartbeat_ms: "3600000UL" - pressure_reporting_delta: "0.5" - pm_reporting_delta: "5.0" temperature_reporting_delta: "0.5" humidity_reporting_delta: "1.0" + pressure_reporting_delta: "0.5" + pm_reporting_delta: "5.0" voc_reporting_delta: "5.0" - nox_reporting_delta: "1.0" co2_reporting_delta: "10.0" + co_reporting_delta: "1.0" nitrogen_dioxide_reporting_delta: "0.05" - carbon_monoxide_reporting_delta: "1.0" hydrogen_reporting_delta: "0.1" ethanol_reporting_delta: "0.5" methane_reporting_delta: "0.1" ammonia_reporting_delta: "0.1" # Sensor decimal places - co2_decimal_places: "0" - pressure_decimal_places: "1" temperature_decimal_places: "1" humidity_decimal_places: "1" + pressure_decimal_places: "1" pm_decimal_places: "1" voc_decimal_places: "0" - nox_decimal_places: "0" aqi_decimal_places: "0" + co2_decimal_places: "0" + co_decimal_places: "2" + nox_decimal_places: "0" nitrogen_dioxide_decimal_places: "2" - carbon_monoxide_decimal_places: "2" hydrogen_decimal_places: "2" ethanol_decimal_places: "1" methane_decimal_places: "2" @@ -821,7 +820,7 @@ sensor: carbon_monoxide: name: Carbon Monoxide id: carbon_monoxide - accuracy_decimals: ${carbon_monoxide_decimal_places} + accuracy_decimals: ${co_decimal_places} filters: - lambda: |- static float last_reported_value = NAN; @@ -835,7 +834,7 @@ sensor: } const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${carbon_monoxide_reporting_delta}; + const bool has_meaningful_change = abs(x - last_reported_value) >= ${co_reporting_delta}; const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; if (is_first_report || has_meaningful_change || heartbeat_due) { last_reported_value = x; From 3c3dd8109b509b155be363921a2743967c237a9a Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sun, 27 Sep 2026 21:52:03 +0300 Subject: [PATCH 12/14] update reporting deltas & decimal places --- Integrations/ESPHome/Core.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index f006efe..2fcc8a8 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -5,17 +5,17 @@ substitutions: # Reduced DB Reporting thresholds reduced_reporting_heartbeat_ms: "3600000UL" - temperature_reporting_delta: "0.5" - humidity_reporting_delta: "1.0" + temperature_reporting_delta: "0.2" + humidity_reporting_delta: "2.0" pressure_reporting_delta: "0.5" - pm_reporting_delta: "5.0" + pm_reporting_delta: "0.5" voc_reporting_delta: "5.0" co2_reporting_delta: "10.0" co_reporting_delta: "1.0" nitrogen_dioxide_reporting_delta: "0.05" hydrogen_reporting_delta: "0.1" ethanol_reporting_delta: "0.5" - methane_reporting_delta: "0.1" + methane_reporting_delta: "100.0" ammonia_reporting_delta: "0.1" # Sensor decimal places @@ -26,13 +26,13 @@ substitutions: voc_decimal_places: "0" aqi_decimal_places: "0" co2_decimal_places: "0" - co_decimal_places: "2" + co_decimal_places: "1" nox_decimal_places: "0" nitrogen_dioxide_decimal_places: "2" - hydrogen_decimal_places: "2" + hydrogen_decimal_places: "1" ethanol_decimal_places: "1" - methane_decimal_places: "2" - ammonia_decimal_places: "2" + methane_decimal_places: "0" + ammonia_decimal_places: "1" # Default OTA password. Override in your device YAML by re-declaring # `substitutions: { ota_password: !secret _ota_password }` so each From dcdc40267161155010c871d80353a49502f63242 Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sat, 3 Oct 2026 10:44:37 +0300 Subject: [PATCH 13/14] review: deduplicate reduced reporting filters --- Integrations/ESPHome/Core.yaml | 443 ++---------------------- Integrations/ESPHome/reduce_filter.yaml | 21 ++ 2 files changed, 43 insertions(+), 421 deletions(-) create mode 100644 Integrations/ESPHome/reduce_filter.yaml diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 2fcc8a8..a939775 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -342,27 +342,7 @@ sensor: id: "co2" accuracy_decimals: ${co2_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${co2_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${co2_reporting_delta}" } } on_value: - if: condition: @@ -380,53 +360,14 @@ sensor: accuracy_decimals: ${pressure_decimal_places} filters: - lambda: |- - float offset = id(dps310_pressure_offset).state; - float current_value = isnan(offset) ? x : x + offset; - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = current_value; - last_report_time = now; - return current_value; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(current_value - last_reported_value) >= ${pressure_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = current_value; - last_report_time = now; - return current_value; - } - - return {}; + const float offset = id(dps310_pressure_offset).state; + return isnan(offset) ? x : x + offset; + - !include { file: reduce_filter.yaml, vars: { delta: "${pressure_reporting_delta}" } } temperature: id: dps310temperature accuracy_decimals: ${temperature_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${temperature_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${temperature_reporting_delta}" } } - platform: sen5x id: sen55 @@ -435,185 +376,45 @@ sensor: id: pm_1_0 accuracy_decimals: ${pm_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } pm_2_5: name: "PM <2.5µm Weight concentration" id: pm_2_5 accuracy_decimals: ${pm_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } pm_4_0: name: "PM <4µm Weight concentration" id: pm_4_0 accuracy_decimals: ${pm_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } pm_10_0: name: "PM <10µm Weight concentration" id: pm_10_0 accuracy_decimals: ${pm_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } temperature: name: "SEN55 Temperature" id: sen55_temperature accuracy_decimals: ${temperature_decimal_places} filters: - lambda: return x - id(sen55_temperature_offset).state; - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${temperature_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${temperature_reporting_delta}" } } humidity: name: "SEN55 Humidity" id: sen55_humidity accuracy_decimals: ${humidity_decimal_places} filters: - lambda: return x - id(sen55_humidity_offset).state; - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${humidity_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${humidity_reporting_delta}" } } voc_index: name: "SEN55 VOC" id: sen55_voc accuracy_decimals: ${voc_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${voc_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${voc_reporting_delta}" } } on_value: - if: condition: @@ -652,27 +453,7 @@ sensor: accuracy_decimals: ${pm_decimal_places} update_interval: 10s filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } - platform: template name: "PM 1 To 2.5 µm" @@ -685,27 +466,7 @@ sensor: accuracy_decimals: ${pm_decimal_places} update_interval: 10s filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } - platform: template name: "PM 2.5 To 4 µm" @@ -718,27 +479,7 @@ sensor: accuracy_decimals: ${pm_decimal_places} update_interval: 10s filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } - platform: template name: "PM 4 To 10 µm" @@ -751,27 +492,7 @@ sensor: accuracy_decimals: ${pm_decimal_places} update_interval: 10s filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${pm_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } - platform: aqi name: "NowCast AQI" @@ -796,157 +517,37 @@ sensor: id: nitrogen_dioxide accuracy_decimals: ${nitrogen_dioxide_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${nitrogen_dioxide_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${nitrogen_dioxide_reporting_delta}" } } carbon_monoxide: name: Carbon Monoxide id: carbon_monoxide accuracy_decimals: ${co_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${co_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${co_reporting_delta}" } } hydrogen: name: Hydrogen id: hydrogen accuracy_decimals: ${hydrogen_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${hydrogen_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${hydrogen_reporting_delta}" } } ethanol: name: Ethanol id: ethanol accuracy_decimals: ${ethanol_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${ethanol_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${ethanol_reporting_delta}" } } methane: name: Methane id: methane accuracy_decimals: ${methane_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${methane_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${methane_reporting_delta}" } } ammonia: name: Ammonia id: ammonia accuracy_decimals: ${ammonia_decimal_places} filters: - - lambda: |- - static float last_reported_value = NAN; - static uint32_t last_report_time = 0; - const uint32_t now = millis(); - - if (!id(reduce_db_reporting).state) { - last_reported_value = x; - last_report_time = now; - return x; - } - - const bool is_first_report = isnan(last_reported_value); - const bool has_meaningful_change = abs(x - last_reported_value) >= ${ammonia_reporting_delta}; - const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; - if (is_first_report || has_meaningful_change || heartbeat_due) { - last_reported_value = x; - last_report_time = now; - return x; - } - - return {}; + - !include { file: reduce_filter.yaml, vars: { delta: "${ammonia_reporting_delta}" } } light: - platform: esp32_rmt_led_strip diff --git a/Integrations/ESPHome/reduce_filter.yaml b/Integrations/ESPHome/reduce_filter.yaml new file mode 100644 index 0000000..d83c562 --- /dev/null +++ b/Integrations/ESPHome/reduce_filter.yaml @@ -0,0 +1,21 @@ +lambda: |- + static float last_reported_value = NAN; + static uint32_t last_report_time = 0; + const uint32_t now = millis(); + + if (!id(reduce_db_reporting).state) { + last_reported_value = x; + last_report_time = now; + return x; + } + + const bool is_first_report = isnan(last_reported_value); + const bool has_meaningful_change = abs(x - last_reported_value) >= ${delta}; + const bool heartbeat_due = now - last_report_time >= ${reduced_reporting_heartbeat_ms}; + if (is_first_report || has_meaningful_change || heartbeat_due) { + last_reported_value = x; + last_report_time = now; + return x; + } + + return {}; From f314810c3b02f9ccb60099c2fd47cc254d01b1ca Mon Sep 17 00:00:00 2001 From: Jaakko Paju <36770267+JPaju@users.noreply.github.com> Date: Sat, 3 Oct 2026 10:45:31 +0300 Subject: [PATCH 14/14] bump version --- 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 a939775..08b4cb4 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,6 +1,6 @@ substitutions: name: apollo-air-1 - version: "26.10.16.1" + version: "26.10.27.1" device_description: ${name} made by Apollo Automation - version ${version}. # Reduced DB Reporting thresholds