From b60b7d9d186e636d25cda3a6a04f95aad65d28ca Mon Sep 17 00:00:00 2001 From: Jadehawk <333657+jadehawk@users.noreply.github.com> Date: Tue, 26 Aug 2025 00:19:50 -0400 Subject: [PATCH 1/3] Refactor Core.yaml: added interval logic --- Integrations/ESPHome/Core.yaml | 296 ++++++++++++++++++++++++++++++--- 1 file changed, 273 insertions(+), 23 deletions(-) diff --git a/Integrations/ESPHome/Core.yaml b/Integrations/ESPHome/Core.yaml index b480ae2..014e83c 100644 --- a/Integrations/ESPHome/Core.yaml +++ b/Integrations/ESPHome/Core.yaml @@ -1,5 +1,6 @@ substitutions: name: apollo-pump-1 + friendly_name: Apollo Pump-1 version: "25.8.6.1" device_description: ${name} made by Apollo Automation - version ${version}. @@ -9,8 +10,11 @@ esp32: flash_size: 8MB framework: type: esp-idf - + esphome: + name: ${name} + name_add_mac_suffix: false + friendly_name: ${friendly_name} on_boot: - priority: 600 then: @@ -18,9 +22,17 @@ esphome: id(pump_start_time) = 0; id(safety_alert_active) = false; - script.execute: pump_safety_check + + - priority: -100 + then: + - lambda: |- + if (id(sntp_time).now().timestamp == 0) { + id(last_run_timestamp) = 0; // discard stale value + } + - priority: -100 then: - - delay: 1000ms + - delay: 1000ms - script.execute: statusCheck - priority: -10 then: @@ -30,6 +42,18 @@ esphome: then: - lambda: "id(testScript).execute();" +# Enable logging +logger: + +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password + + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + ssid: "Apollo-Pump-1 Fallback Hotspot" + password: "D9xJDUpZF6EP" + api: services: - service: play_buzzer @@ -37,27 +61,45 @@ api: song_str: string then: - rtttl.play: - rtttl: !lambda 'return song_str;' + rtttl: !lambda "return song_str;" - service: run_pump_for_seconds variables: seconds: int then: - switch.turn_on: pump_control - - delay: !lambda 'return seconds * 1000;' + - delay: !lambda "return seconds * 1000;" - switch.turn_off: pump_control - service: run_pump_until_full - then: + then: - script.execute: pumpUntilFull +time: + - platform: sntp #<<<<<<<<<<<<<<<< Added for Interval Functionality + id: sntp_time + timezone: "America/New_York" + globals: + - id: last_run_timestamp #<<<<<<<<<<<<<<<< Added for Interval Functionality + type: uint32_t + restore_value: yes + initial_value: "0" + - id: pump_cycle_count #<<<<<<<<<<<<<<<< Added for Interval Functionality + type: int + restore_value: yes + initial_value: "0" + - id: alert_active #<<<<<<<<<<<<<<<< Added for Interval Functionality + type: bool + restore_value: no + initial_value: "false" + - id: pump_start_time restore_value: no type: uint32_t - initial_value: '0' + initial_value: "0" - id: safety_alert_active restore_value: no type: bool - initial_value: 'false' + initial_value: "false" - id: runTest restore_value: yes type: bool @@ -71,12 +113,13 @@ web_server: port: 80 # Buzzer + output: - platform: ledc pin: GPIO5 id: buzzer rtttl: - output: buzzer + output: buzzer button: - platform: restart @@ -87,28 +130,57 @@ button: disabled_by_default: True name: "Factory Reset ESP" id: factory_reset_all - + - platform: template name: "Run Pump" id: run_pump_button icon: mdi:pump on_press: then: + - lambda: |- #<<<<<<<<<<<<<<<< Added for Interval Functionality + id(pump_cycle_count) += 1; + ESP_LOGD("pump", "Cycle count: %d", id(pump_cycle_count)); - switch.turn_on: pump_control - - delay: !lambda 'return (int)id(pump_run_seconds).state * 1000;' + - delay: !lambda "return (int)id(pump_run_seconds).state * 1000;" - switch.turn_off: pump_control - + - platform: template name: "Run Pump Until Output Wet" id: run_pump_until_out_wet icon: mdi:pump disabled_by_default: true - on_press: + on_press: then: script.execute: pumpUntilFull + - platform: template + name: "Play Buzzer ↯" # Added to confirm Buzzer works + id: play_buzzer_button + icon: mdi:surround-sound + on_press: + then: + - rtttl.play: !lambda return "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"; + + - platform: template #<<<<<<<<<<<<<<<< Added for Interval Functionality + name: "Confirm Refill (Resets Pump Cycles) ↯" + icon: mdi:check-circle + on_press: + then: + - lambda: |- + id(pump_cycle_count) = 0; + id(pump_cycle_sensor).publish_state(0); + id(alert_active) = false; switch: + - platform: template #<<<<<<<<<<<<<<<< Added for Interval Functionality + name: "Enable Pump Interval Runs ↯" + id: enable_pump_interval + optimistic: true + restore_mode: RESTORE_DEFAULT_OFF + on_turn_off: + - lambda: |- + id(alert_active) = false; + - platform: factory_reset id: factory_reset_switch internal: true @@ -171,7 +243,7 @@ binary_sensor: id: ink_ha_connected - platform: gpio id: reset_button - pin: + pin: number: GPIO9 inverted: true mode: @@ -225,9 +297,40 @@ binary_sensor: pullup: true inverted: true + - platform: template #<<<<<<<<<<<<<<<< Added for Interval Functionality + name: "Pump Alert Active ↯" + lambda: |- + return id(alert_active); + icon: mdi:alert + number: + - platform: template #<<<<<<<<<<<<<<<< Added for Interval Functionality + name: "Pump Interval (in Hours) ↯" + id: pump_interval_hours + icon: mdi:timer + min_value: 1 + max_value: 720 + step: 1 + mode: box + initial_value: 12 + unit_of_measurement: "hours" + restore_value: true + optimistic: true + - platform: template #<<<<<<<<<<<<<<<< Added for Interval Functionality + name: "Tank Size (oz) ↯" + id: tank_size_oz + icon: mdi:cup + min_value: 1 + max_value: 128 + step: 1 + mode: box + initial_value: 64 + unit_of_measurement: "oz" + restore_value: true + optimistic: true + - platform: template - name: "Pump Run Seconds" + name: "Pump Run Seconds (1s = 15mL)" id: pump_run_seconds icon: mdi:timer optimistic: true @@ -269,6 +372,32 @@ sensor: update_interval: 60s entity_category: "diagnostic" + - platform: template #<<<<<<<<<<<<<<<< Added for Interval Functionality + name: "Pump Cycle Count ↯" + id: pump_cycle_sensor + lambda: |- + return id(pump_cycle_count); + update_interval: 30s + accuracy_decimals: 0 + icon: mdi:counter + - platform: template #<<<<<<<<<<<<<<<< Added for Interval Functionality + name: "Pump Cycle Threshold (Computed) ↯" + id: pump_cycle_threshold + lambda: |- + // Flow: 900 mL/min → 15 mL/s → ~0.507 oz/s + const float flow_oz_per_sec = 900.0f / 60.0f / 29.5735f; + const float run_s = id(pump_run_seconds).state; + const float tank_oz = id(tank_size_oz).state; + + if (run_s <= 0.0f || tank_oz <= 0.0f) return 0.0f; + + const float dose_oz_per_cycle = flow_oz_per_sec * run_s; + return ceilf(tank_oz / dose_oz_per_cycle); + update_interval: 10s + unit_of_measurement: "Cycles" + accuracy_decimals: 0 + icon: mdi:counter + light: - platform: esp32_rmt_led_strip id: rgb_light @@ -298,17 +427,17 @@ script: - id: pumpUntilFull then: - switch.turn_on: stop_pump_when_full - - delay: 100ms # Small delay to ensure switch state is updated + - delay: 100ms # Small delay to ensure switch state is updated - switch.turn_on: pump_control - + - id: statusCheck 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% @@ -320,7 +449,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% @@ -328,7 +457,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% @@ -339,7 +468,7 @@ script: - id: run_pump_timed then: - switch.turn_on: pump_control - - delay: !lambda 'return (int)id(pump_run_seconds).state * 1000;' + - delay: !lambda "return (int)id(pump_run_seconds).state * 1000;" - switch.turn_off: pump_control - id: pump_safety_check mode: restart @@ -376,7 +505,7 @@ script: - delay: 1s - id: testScript then: - if: + if: condition: - lambda: "return id(runTest) == true;" then: @@ -395,4 +524,125 @@ script: green: 0% blue: 0% - light.turn_off: - id: rgb_light \ No newline at end of file + id: rgb_light + + - id: run_buzzer_alert #<<<<<<<<<<<<<<<< Added for Interval Functionality + mode: restart + then: + - output.ledc.set_frequency: + id: buzzer + frequency: 1500Hz + - output.set_level: + id: buzzer + level: 50% + - delay: 500ms + - output.set_level: + id: buzzer + level: 0% + +#====================================================================== +# Interval schedulers for Intervals, alerts, and LED state. +#====================================================================== +interval: + # Scheduler: run at set interval if enabled and fluid is present or cycle threshold not exceeded. + - interval: 60s + then: + - lambda: |- + if (!id(enable_pump_interval).state) { + ESP_LOGD("pump", "Interval skipped: pump interval disabled"); + return; + } + + // 🔍 Primary check: ultrasonic fluid sensor + if (id(stop_pump_when_dry).state && !id(fluid_input_sensor).state) { + id(alert_active) = true; + ESP_LOGD("pump", "Interval blocked: fluid sensor reports dry and stop_pump_when_dry is enabled"); + return; + } + + // 🧮 Fallback check: cycle threshold + if (id(pump_cycle_count) >= (int) id(pump_cycle_threshold).state) { + id(alert_active) = true; + ESP_LOGD("pump", "Interval blocked: cycle threshold reached, Tank Might be Empty!"); + return; + } + + // 🕒 Time-based interval logic + uint32_t now = id(sntp_time).now().timestamp; + if (now == 0) { + now = millis() / 1000; + ESP_LOGD("pump", "Using millis() as fallback timestamp"); + } + + uint32_t interval_s = (uint32_t)(id(pump_interval_hours).state * 3600); + if ((now - id(last_run_timestamp)) >= interval_s) { + id(last_run_timestamp) = now; + id(run_pump_button).press(); + id(pump_cycle_count) += 1; + id(pump_cycle_sensor).publish_state(id(pump_cycle_count)); + ESP_LOGD("pump", "Cycle count: %d", id(pump_cycle_count)); + } + + # Alert buzzer reminder while enabled and alert is active. + - interval: 60s + then: + - lambda: |- + if (id(alert_active) && id(enable_pump_interval).state) { + id(run_buzzer_alert).execute(); + } + + # LED feedback: fluid sensor is primary; warn near threshold; red pulse on alert. + - interval: 60s + then: + - lambda: |- + auto count = id(pump_cycle_count); + auto threshold = (int) id(pump_cycle_threshold).state; + + if (!id(enable_pump_interval).state) { + id(rgb_light).turn_off(); + return; + } + + // 🔍 Primary check: ultrasonic fluid sensor + if (!id(fluid_input_sensor).state) { + id(rgb_light).turn_on(); // Red alert will be handled below + return; + } + + // 🧮 Fallback: warn if near threshold + if (id(alert_active)) { + id(rgb_light).turn_on(); + } else if (count >= threshold - 6) { + id(rgb_light).turn_on(); + } else { + id(rgb_light).turn_off(); + } + + - if: + condition: + lambda: |- + // Red pulse if alert is active or fluid is missing + return id(alert_active) || !id(fluid_input_sensor).state; + then: + - light.turn_on: + id: rgb_light + brightness: 100% + red: 100% + green: 0% + blue: 0% + effect: "Slow Pulse" + + - if: + condition: + lambda: |- + auto count = id(pump_cycle_count); + auto threshold = (int) id(pump_cycle_threshold).state; + return count >= threshold - 6 && id(fluid_input_sensor).state && !id(alert_active); + then: + - light.turn_on: + id: rgb_light + brightness: 100% + red: 100% + green: 100% + blue: 0% + effect: "Slow Pulse" From dfe982be28347a7942c782800911435a25552698 Mon Sep 17 00:00:00 2001 From: Jadehawk <333657+jadehawk@users.noreply.github.com> Date: Tue, 26 Aug 2025 00:34:39 -0400 Subject: [PATCH 2/3] Added Apollo Automation Logo --- apollo-automations-logo.png | Bin 0 -> 6565 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apollo-automations-logo.png diff --git a/apollo-automations-logo.png b/apollo-automations-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0d5cf46b6a26461b957724089610bbcec3def0bb GIT binary patch literal 6565 zcma)A_di_U)88d}^woPOR_~(M)fN#oQKHxAy|a2-d`NUc^tMDV5tb!t5Tdi9B~ca$ z66D!@|AOa-d+(Wf&78S2XU_Y~nG_?aHYqV9F#rG{)q!X{!G6d8J%sq!cauePDF6VB zc2ie3@_(YO#;&8OE-fY}EiEK2ECvAZ&*o*%3ThaGP|D8Zr&`%yu+1D zq}0`F(jqTv+|f&ditLg91&QuYiH>*Z%F>v6%dx}DOYZ2*tH+O?M=yNeM@LsGH9uo~ zx=5Jin&?K7MPk^znmD;OEM%pdK~hG1_4-NqSyKaw`1Xfi+fbQs(I*I@g2AwETe^c& z26QDBx=d4LtMmaY|h;$ti;hPEHPcU_IE3?gDM_3;RmT|ZJ1y2vT*HYTuc9M1hf3i zGGz$c4(GGfS(c%>qQjel_rN4y+?oH*VH+wtQ1$vV^lvK*K(cIhc)d>V{1axShw3&Gv~ zd|W5u)>lU%I-f}N>XBJ}le;3AA2rJ;hJqbmpUz@2eX3=mPmD9Zp!D}HyT7`w8f}&y zHIpNcV?g)kl@>E!+U;�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 Date: Tue, 26 Aug 2025 00:39:37 -0400 Subject: [PATCH 3/3] Update README.md Missing Logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e428e0..1029b26 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PUMP-1 Smart Pump Controller -![Apollo Automation Logo](path/to/logo.png) +![Apollo Automation Logo](https://github.com/jadehawk/IconsBackup/blob/main/Icons/apollo-automations-logo.png) An ESPHome-based intelligent pump controller with advanced safety features, water level monitoring, and flexible control options. The PUMP-1 is designed for reliable water management in various applications including drainage, filling, and automated irrigation systems.