diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index 8626f08..08b4cb4 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,20 +1,55 @@ 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 + reduced_reporting_heartbeat_ms: "3600000UL" + temperature_reporting_delta: "0.2" + humidity_reporting_delta: "2.0" + pressure_reporting_delta: "0.5" + 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: "100.0" + ammonia_reporting_delta: "0.1" + + # Sensor decimal places + temperature_decimal_places: "1" + humidity_decimal_places: "1" + pressure_decimal_places: "1" + pm_decimal_places: "1" + voc_decimal_places: "0" + aqi_decimal_places: "0" + co2_decimal_places: "0" + co_decimal_places: "1" + nox_decimal_places: "0" + nitrogen_dioxide_decimal_places: "2" + hydrogen_decimal_places: "1" + ethanol_decimal_places: "1" + 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 # 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. @@ -86,9 +121,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(); @@ -118,11 +153,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 @@ -136,7 +171,6 @@ globals: type: bool initial_value: "false" - i2c: sda: GPIO1 scl: GPIO0 @@ -224,25 +258,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: |- @@ -270,15 +303,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: @@ -299,68 +332,89 @@ 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: + - !include { file: reduce_filter.yaml, vars: { delta: "${co2_reporting_delta}" } } 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; + 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 - update_interval: 30s - i2c_id: bus_a + accuracy_decimals: ${temperature_decimal_places} + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${temperature_reporting_delta}" } } - platform: sen5x id: sen55 pm_1_0: name: "PM <1µm Weight concentration" id: pm_1_0 - accuracy_decimals: 1 + accuracy_decimals: ${pm_decimal_places} + filters: + - !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: 1 + accuracy_decimals: ${pm_decimal_places} + filters: + - !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: 1 + accuracy_decimals: ${pm_decimal_places} + filters: + - !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: 1 + accuracy_decimals: ${pm_decimal_places} + filters: + - !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; - accuracy_decimals: 1 + - !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; - accuracy_decimals: 1 + - !include { file: reduce_filter.yaml, vars: { delta: "${humidity_reporting_delta}" } } voc_index: name: "SEN55 VOC" id: sen55_voc + accuracy_decimals: ${voc_decimal_places} + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${voc_reporting_delta}" } } on_value: - if: condition: @@ -378,6 +432,7 @@ sensor: nox_index: name: "SEN55 NOX" id: sen55_nox + accuracy_decimals: ${nox_decimal_places} acceleration_mode: low store_baseline: true # Some units misreport SEN54 on the product name register even though the @@ -395,8 +450,11 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: ${pm_decimal_places} update_interval: 10s - + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } + - platform: template name: "PM 1 To 2.5 µm" id: pm1_to_2_5 @@ -405,8 +463,11 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: ${pm_decimal_places} update_interval: 10s - + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } + - platform: template name: "PM 2.5 To 4 µm" id: pm2_5_to_4 @@ -415,7 +476,10 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: ${pm_decimal_places} update_interval: 10s + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } - platform: template name: "PM 4 To 10 µm" @@ -425,7 +489,10 @@ sensor: unit_of_measurement: "µg/m³" state_class: measurement icon: mdi:air-filter + accuracy_decimals: ${pm_decimal_places} update_interval: 10s + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${pm_reporting_delta}" } } - platform: aqi name: "NowCast AQI" @@ -434,6 +501,7 @@ sensor: pm_10_0: pm_10_0 calculation_type: AQI device_class: aqi + accuracy_decimals: ${aqi_decimal_places} on_value: - if: condition: @@ -443,25 +511,43 @@ sensor: - platform: mics_4514 id: mics4514 + update_interval: 60s nitrogen_dioxide: name: Nitrogen Dioxide id: nitrogen_dioxide + accuracy_decimals: ${nitrogen_dioxide_decimal_places} + filters: + - !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: + - !include { file: reduce_filter.yaml, vars: { delta: "${co_reporting_delta}" } } hydrogen: name: Hydrogen id: hydrogen + accuracy_decimals: ${hydrogen_decimal_places} + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${hydrogen_reporting_delta}" } } ethanol: name: Ethanol id: ethanol + accuracy_decimals: ${ethanol_decimal_places} + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${ethanol_reporting_delta}" } } methane: name: Methane id: methane + accuracy_decimals: ${methane_decimal_places} + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${methane_reporting_delta}" } } ammonia: name: Ammonia id: ammonia - update_interval: 60s + accuracy_decimals: ${ammonia_decimal_places} + filters: + - !include { file: reduce_filter.yaml, vars: { delta: "${ammonia_reporting_delta}" } } light: - platform: esp32_rmt_led_strip @@ -539,9 +625,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 @@ -580,6 +666,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 @@ -705,7 +799,7 @@ script: - id: testScript then: - if: + if: condition: - lambda: "return id(runTest) == true;" then: @@ -724,7 +818,7 @@ script: - lambda: "return id(dps310pressure).state > 800 && id(dps310pressure).state < 1200;" then: - lambda: "id(dps310Passed) = true;" - + - if: condition: - lambda: "return id(dps310Passed);" @@ -733,7 +827,7 @@ script: - lambda: "id(runTest) = false;" - delay: 1s - lambda: "id(testCycleCount) += 1;" - + #Check If Test Passed To Trigger Lights - if: condition: @@ -753,7 +847,7 @@ script: blue: 0% - light.turn_off: id: rgb_light - + else: - lambda: "id(runTest) = false;" - light.turn_on: @@ -776,10 +870,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% @@ -791,7 +885,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% @@ -799,7 +893,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% 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 {};