diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4eafca5..8be767b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,18 +14,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + + - name: Set uppercase DEVICE_NAME + run: | + echo "UPPERCASE_DEVICE_NAME=$(echo $DEVICE_NAME | tr '[:lower:]' '[:upper:]')" >> $GITHUB_ENV + - name: Build Firmware uses: esphome/build-action@v3.0.0 id: esphome-build with: - yaml_file: Integrations/ESPHome/${{ env.DEVICE_NAME }}.yaml + yaml_file: Integrations/ESPHome/${{ env.UPPERCASE_DEVICE_NAME }}_Factory.yaml version: 'latest' cache: true - name: Read version from YAML file id: read_version run: | - version=$(awk '/substitutions:/ {found=1} found && /version:/ {print $2; exit}' Integrations/ESPHome/${{ env.DEVICE_NAME }}.yaml | tr -d '"') + version=$(awk '/substitutions:/ {found=1} found && /version:/ {print $2; exit}' Integrations/ESPHome/${{ env.UPPERCASE_DEVICE_NAME }}_Factory.yaml | tr -d '"') echo "project_version=$version" >> $GITHUB_ENV - name: Move generated files to output diff --git a/Integrations/ESPHome/AIR-1_Factory.yaml b/Integrations/ESPHome/AIR-1_Factory.yaml new file mode 100644 index 0000000..f9c8952 --- /dev/null +++ b/Integrations/ESPHome/AIR-1_Factory.yaml @@ -0,0 +1,407 @@ +#Define Project +substitutions: + name: apollo-air-1 + version: "24.7.6.1" + device_description: ${name} made by Apollo Automation - version ${version}. + +esphome: + name: "${name}" + friendly_name: Apollo AIR-1 + comment: Apollo AIR-1 + name_add_mac_suffix: true + platformio_options: + board_build.flash_mode: dio + + project: + name: "ApolloAutomation.AIR-1" + version: "${version}" + + + min_version: 2024.6.0 + +esp32: + board: esp32-c3-devkitm-1 + framework: + type: arduino + +dashboard_import: + package_import_url: github://ApolloAutomation/AIR-1_Test/Integrations/ESPHome/air-1.yaml + import_full_config: false + +globals: + - id: cycleCounter + type: int + restore_value: no + initial_value: '0' + - id: button_press_timestamp + restore_value: no + type: uint32_t + initial_value: '0' + +# Enable Home Assistant API +api: + on_client_connected: + - delay: 1s + - light.turn_off: rgb_light + - lambda: 'id(cycleCounter) = 30;' + services: + #Co2 Calibration Service + - service: calibrate_co2_value + variables: + co2_ppm: float + then: + - scd4x.perform_forced_calibration: + value: !lambda "return co2_ppm;" + id: scd40 + - service: sen55_clean + then: + - sen5x.start_fan_autoclean: sen55 + +ota: + - platform: esphome + password: "apolloautomation" + id: ota_default + - platform: http_request + id: ota_managed + +http_request: + useragent: esphome/device + timeout: 10s + verify_ssl: false + +update: + - platform: http_request + id: firmware_update + name: Firmware Update + source: https://apolloautomation.github.io/AIR-1_Test/artifact/manifest.json + update_interval: 60s + + + +wifi: + on_connect: + - delay: 5s # Gives time for improv results to be transmitted + + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + ssid: "Apollo AIR1 Hotspot" + +captive_portal: + + +web_server: + port: 80 + +logger: + level: NONE + +i2c: + sda: GPIO1 + scl: GPIO0 + id: bus_a + +number: + - platform: template + name: SEN55 Temperature Offset + id: sen55_temperature_offset + restore_value: true + initial_value: 6.0 + min_value: -70.0 + max_value: 70.0 + entity_category: "CONFIG" + unit_of_measurement: "°C" + optimistic: true + update_interval: never + step: 0.1 + mode: box + - platform: template + name: SEN55 Humidity Offset + id: sen55_humidity_offset + restore_value: true + initial_value: 0 + min_value: -70.0 + max_value: 70.0 + entity_category: "CONFIG" + unit_of_measurement: "%" + optimistic: true + update_interval: never + step: 0.1 + mode: box + + +binary_sensor: + - platform: status + name: Online + id: ink_ha_connected + - platform: gpio + pin: + number: GPIO9 + inverted: true + mode: + input: true + pullup: true + id: reset_button + on_press: + then: + - lambda: |- + id(button_press_timestamp) = millis(); + on_release: + then: + - lambda: |- + if (millis() - id(button_press_timestamp) >= 5000) { + // Remove Wifi + id(factory_reset_switch).turn_on(); + } + +sensor: + - platform: internal_temperature + name: "ESP Temperature" + id: sys_esp_temperature + + - platform: uptime + name: Uptime + id: sys_uptime + update_interval: 60s + + - platform: wifi_signal + name: RSSI + id: wifi_signal_db + update_interval: 60s + entity_category: "diagnostic" + + - platform: scd4x + id: scd40 + co2: + name: "CO2" + id: "co2" + automatic_self_calibration: false + update_interval: 60s + measurement_mode: "periodic" + i2c_id: bus_a + ambient_pressure_compensation_source: dps310pressure + + - platform: dps310 + id: dps_310 + pressure: + name: "DPS310 Pressure" + id: dps310pressure + temperature: + id: dps310temperature + 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: 1 + pm_2_5: + name: "PM <2.5µm Weight concentration" + id: pm_2_5 + accuracy_decimals: 1 + pm_4_0: + name: "PM <4µm Weight concentration" + id: pm_4_0 + accuracy_decimals: 1 + pm_10_0: + name: "PM <10µm Weight concentration" + id: pm_10_0 + accuracy_decimals: 1 + temperature: + name: "SEN55 Temperature" + accuracy_decimals: 1 + filters: + - lambda: return x - id(sen55_temperature_offset).state; + humidity: + name: "SEN55 Humidity" + filters: + - lambda: return x - id(sen55_humidity_offset).state; + accuracy_decimals: 0 + voc: + name: "SEN55 VOC" + id: sen55_voc + algorithm_tuning: + #https://sensirion.com/media/documents/25AB572C/62B463AA/Sensirion_Engineering_Guidelines_SEN5x.pdf + index_offset: 100 + learning_time_offset_hours: 72 + learning_time_gain_hours: 72 + gating_max_duration_minutes: 180 + std_initial: 50 + gain_factor: 230 + nox: + name: "SEN55 NOX" + id: sen55_nox + acceleration_mode: low + store_baseline: true + address: 0x69 + update_interval: 10s + + - platform: template + name: "PM 0.3 To 1 µm" + id: pm0_3_to_1 + disabled_by_default: true + lambda: return id(pm_1_0).state; + unit_of_measurement: "µg/m³" + state_class: measurement + icon: mdi:air-filter + update_interval: 10s + + - platform: template + name: "PM 1 To 2.5 µm" + id: pm1_to_2_5 + disabled_by_default: true + lambda: return id(pm_2_5).state - id(pm_1_0).state; + unit_of_measurement: "µg/m³" + state_class: measurement + icon: mdi:air-filter + update_interval: 10s + + - platform: template + name: "PM 2.5 To 4 µm" + id: pm2_5_to_4 + disabled_by_default: true + lambda: return id(pm_4_0).state - id(pm_2_5).state; + unit_of_measurement: "µg/m³" + state_class: measurement + icon: mdi:air-filter + update_interval: 10s + + - platform: template + name: "PM 4 To 10 µm" + id: pm4_to_10 + disabled_by_default: true + lambda: return id(pm_10_0).state - id(pm_4_0).state; + unit_of_measurement: "µg/m³" + icon: mdi:air-filter + update_interval: 10s + + - platform: mics_4514 + id: mics4514 + nitrogen_dioxide: + name: Nitrogen Dioxide + carbon_monoxide: + name: Carbon Monoxide + hydrogen: + name: Hydrogen + ethanol: + name: Ethanol + methane: + name: Methane + ammonia: + name: Ammonia + update_interval: 10s + +light: + - platform: esp32_rmt_led_strip + id: rgb_light + name: "RGB Light New 2" + pin: GPIO3 + rmt_channel: 0 + default_transition_length: 0s + chipset: WS2812 + num_leds: 3 + rgb_order: grb + effects: + - pulse: + name: "Slow Pulse" + transition_length: 250ms + update_interval: 250ms + min_brightness: 50% + max_brightness: 100% + - pulse: + name: "Fast Pulse" + transition_length: 100ms + update_interval: 100ms + min_brightness: 50% + max_brightness: 100% + + + +button: + - platform: restart + icon: mdi:power-cycle + name: "ESP Reboot" + + - platform: factory_reset + disabled_by_default: True + name: "Factory Reset ESP" + id: factory_reset_all + + - platform: template + name: "Calibrate SCD40 To 420ppm" + id: set_SCD40_calibrate + on_press: + - scd4x.perform_forced_calibration: + value: 420 + id: scd40 + + - platform: template + name: "Clean SEN55" + id: clean_sen55 + on_press: + - sen5x.start_fan_autoclean: sen55 + +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 + - platform: template + name: "VOC Quality" + id: voc_quality + icon: mdi:air-filter + lambda: |- + if (id(sen55_voc).state < 80) { + return std::string("Improved");} + else if (id(sen55_voc).state < 150) { + return std::string("Normal");} + else if (id(sen55_voc).state < 250) { + return std::string("Abnormal");} + else if (id(sen55_voc).state < 400) { + return std::string("Very abnormal");} + else { + return std::string("Extremely abnormal");} + +switch: + - platform: template + name: "Startup Light Blink" + id: startup_light_blink + icon: mdi:lightbulb + restore_mode: RESTORE_DEFAULT_ON + optimistic: true + entity_category: "config" + - platform: factory_reset + id: factory_reset_switch + internal: true + +#Used To Control RGB Light On Startup. Tells User Status Of Device +interval: + - interval: 1s + then: + - if: + condition: + - binary_sensor.is_off: ink_ha_connected + - lambda: 'return id(cycleCounter) < 30;' + - switch.is_on: startup_light_blink + + then: + - light.toggle: + id: rgb_light + - lambda: 'id(cycleCounter) += 1;' + + - interval: 1s + then: + - if: + condition: + - binary_sensor.is_off: ink_ha_connected + - lambda: 'return id(cycleCounter) > 30;' + - lambda: 'return id(cycleCounter) < 31;' + - switch.is_on: startup_light_blink + + then: + - light.turn_off: + id: rgb_light + - lambda: 'id(cycleCounter) += 1;' + + +