#pragma once #include "esphome/core/automation.h" #include "esphome/components/gpio/output/gpio_binary_output.h" #include "esphome/components/analog_orp/analog_orp.h" static const char *const TAG = "chlorine_pump"; namespace esphome { namespace chlorine_pump { class ChlorinePump : public Component { protected: analog_orp::ChlorineSensor *sensor_; gpio::GPIOBinaryOutput *pump_out; int cycle_time; int prop_band; bool state = true; unsigned long last_action; bool disable_clock_ = false; CallbackManager callback_pump_; CallbackManager callback_cycle_; int tOn = 0; int tOff = 0; float target_ = 700.0f; public: ChlorinePump(){ last_action = 0; } void disable_clock(bool disable_clock){ this->disable_clock_ = disable_clock; } void set_sensor(analog_orp::ChlorineSensor *sensor){ this->sensor_ = sensor; } void set_pump_out(gpio::GPIOBinaryOutput *pump_out){ this->pump_out = pump_out; } void set_cycle_time(int time_in_sec){ this->cycle_time = time_in_sec; } void set_prop_band(int prop_band){ this->prop_band = prop_band; } void set_target(int target){ this->target_ = (float) target; } void setup() override { last_action = millis(); if(disable_clock_ && sensor_ != nullptr){ if(!sensor_->has_averager()) sensor_->add_on_state_callback([=](float val) -> void { this->tick_time(val); }); if(sensor_->has_averager()) sensor_->add_average_change_callback([=](float val) -> void { this->tick_time(val); }); } } void prime() { tOn = 30; tOff = 2; this->pump_out->turn_on(); } void start() { this->state = true; } void stop() { this->state = false; tOn = 0; tOff = 0; this->pump_out->turn_off(); } bool get_state(){ return state; } void setMillisPrecies(unsigned long waitPeriod){ if(last_action + waitPeriod + waitPeriod > millis()){ last_action += waitPeriod; } else{ ESP_LOGW(TAG, "Reset millis(). Did the system experience a halt for some reason?"); last_action = millis(); } } int calculatePumpTimeSeconds(float last_mesurement){ if(last_mesurement >= target_) return 0; float currentVal = target_ - last_mesurement; float proportionalValue = (currentVal / (float) prop_band); float timeInSeconds = proportionalValue * (float) cycle_time; if(timeInSeconds > cycle_time) timeInSeconds = cycle_time; return (int) timeInSeconds; } void add_on_pump_callback(std::function &&callback){ this->callback_pump_.add(std::move(callback)); } void add_on_cycle_callback(std::function &&callback){ this->callback_cycle_.add(std::move(callback)); } void tick_time(float last_mesurement){ if(tOn == 0 && tOff == 0 && state){ int seconds = calculatePumpTimeSeconds(last_mesurement); if(seconds == 0){ tOn = 0; tOff = 30; } tOn = seconds; tOff = cycle_time - tOn; ESP_LOGD(TAG, "Time => tOn: %d, tOff: %d", tOn, tOff); this->callback_cycle_.call(tOn, tOff); } else if(tOn > 0) { this->callback_pump_.call(true, tOn); --tOn; pump_out->turn_on(); } else if(tOff > 0) { this->callback_pump_.call(false, tOff); --tOff; pump_out->turn_off(); } } void loop() override{ if(disable_clock_) return; if(millis() - last_action > 1000){ tick_time(sensor_->get_state()); setMillisPrecies(1000); } } }; class ChlorSensorOutputTrigger : public Trigger { public: explicit ChlorSensorOutputTrigger(ChlorinePump *parent) { parent->add_on_pump_callback([this](bool output_state, int value) { this->trigger(output_state, value); }); } }; class ChlorSensorCycleTrigger : public Trigger { public: explicit ChlorSensorCycleTrigger(ChlorinePump *parent) { parent->add_on_cycle_callback([this](int on_time, int off_time) { this->trigger(on_time, off_time); }); } }; template class ChlorinePrime : public Action { public: ChlorinePrime(ChlorinePump *pump) : pump_(pump) {} void play(Ts... x) override { this->pump_->prime(); } protected: ChlorinePump *pump_; }; template class ChlorineStart : public Action { public: ChlorineStart(ChlorinePump *pump) : pump_(pump) {} void play(Ts... x) override { this->pump_->start(); } protected: ChlorinePump *pump_; }; template class ChlorineStop : public Action { public: ChlorineStop(ChlorinePump *pump) : pump_(pump) {} void play(Ts... x) override { this->pump_->stop(); } protected: ChlorinePump *pump_; }; template class ChlorineSetTarget : public Action { public: ChlorineSetTarget(ChlorinePump *pump) : pump_(pump) {} TEMPLATABLE_VALUE(float, value) void play(Ts... x) override { this->pump_->set_target((int) this->value_.value(x...)); } protected: ChlorinePump *pump_; }; } }