All the code changes to the chlorine pump... More will follow
This commit is contained in:
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@nbsgames"]
|
@ -109,6 +109,9 @@ void ChlorineSensor::dump_config(){
|
||||
ESP_LOGCONFIG(TAG, " Inverted: %s", inverted_ ? "true" : "false");
|
||||
ESP_LOGCONFIG(TAG, " Print Raw: %s", print_raw_ ? "true" : "false");
|
||||
}
|
||||
bool ChlorineSensor::has_averager(){
|
||||
return averager_ != nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
Nullwert : 680
|
||||
|
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/gpio.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace analog_orp {
|
||||
@ -33,6 +33,7 @@ class ChlorineSensor: public PollingComponent, public sensor::Sensor {
|
||||
void add_average_change_callback(std::function<void(float)> &&callback);
|
||||
void add_new_value_callback(std::function<void(float)> &&callback);
|
||||
void init_averager(int mesurements, int send_average_every);
|
||||
bool has_averager();
|
||||
void setup() override;
|
||||
void update() override;
|
||||
float sample();
|
||||
|
@ -26,7 +26,7 @@ CONF_AVERAGE_NEW = "on_completely_new_value"
|
||||
|
||||
UNIT_MILLI_VOLT = "mV"
|
||||
|
||||
ICON_LIGHTNING_BOLT="mdi:lightning-bolt"
|
||||
ICON_TEST_TUBE="mdi:test-tube"
|
||||
|
||||
ChlorineValueRead = chlorine_sensor_ns.class_(
|
||||
"ChlorineValueReadTrigger", automation.Trigger.template(cg.float_)
|
||||
@ -45,7 +45,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
icon=ICON_LIGHTNING_BOLT
|
||||
icon=ICON_TEST_TUBE
|
||||
).extend({
|
||||
cv.Required(CONF_SENSOR_PIN): adc.validate_adc_pin,
|
||||
cv.Required(CONF_ZERO_POINT): cv.Range(min=0, max=1023),
|
||||
@ -88,12 +88,12 @@ async def to_code(config):
|
||||
|
||||
for conf in averager_config.get(CONF_ON_READ_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "value")], conf)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
|
||||
for conf in averager_config.get(CONF_AVERAGE_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "value")], conf)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
|
||||
for conf in averager_config.get(CONF_AVERAGE_NEW, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "value")], conf)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
128
external_components/chlorine_pump/__init__.py
Normal file
128
external_components/chlorine_pump/__init__.py
Normal file
@ -0,0 +1,128 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import output
|
||||
from esphome.components.analog_orp.sensor import ChlorineSensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_TRIGGER_ID
|
||||
)
|
||||
|
||||
|
||||
DEPENDENCIES=["sensor"]
|
||||
|
||||
chlorine_pump_ns = cg.esphome_ns.namespace('chlorine_pump')
|
||||
ChlorinePump = chlorine_pump_ns.class_('ChlorinePump', cg.Component)
|
||||
|
||||
CONF_SENSOR = "sensor"
|
||||
CONF_PUMP_OUT = "pump"
|
||||
CONF_PUMP_CYCLE_TIME = "cycle_time"
|
||||
CONF_PUMP_PROPORTIONAL_BAND = "proportional_band"
|
||||
CONF_PUMP_VALUE = "on_pump_value"
|
||||
CONF_CYCLE_START = "on_cycle_start"
|
||||
CONF_DISABLE_CLOCK="disable_clock"
|
||||
CONF_TARGET="target"
|
||||
|
||||
def to_proportional_band(value):
|
||||
try:
|
||||
value = int(value)
|
||||
except (TypeError, ValueError):
|
||||
# pylint: disable=raise-missing-from
|
||||
raise cv.Invalid(f"")
|
||||
return cv.one_of(20, 50, 100, 150, 200, 250)(value)
|
||||
|
||||
ChlorSensorOutputTrigger = chlorine_pump_ns.class_(
|
||||
"ChlorSensorOutputTrigger", automation.Trigger.template(cg.bool_, cg.int_)
|
||||
)
|
||||
ChlorSensorCycleTrigger = chlorine_pump_ns.class_(
|
||||
"ChlorSensorCycleTrigger", automation.Trigger.template(cg.int_, cg.int_)
|
||||
)
|
||||
|
||||
PrimeAction = chlorine_pump_ns.class_("ChlorinePrime", automation.Action)
|
||||
StartAction = chlorine_pump_ns.class_("ChlorineStart", automation.Action)
|
||||
StopAction = chlorine_pump_ns.class_("ChlorineStop", automation.Action)
|
||||
SetTargetAction = chlorine_pump_ns.class_("ChlorineSetTarget", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(ChlorinePump),
|
||||
cv.Required(CONF_PUMP_OUT): cv.use_id(output.BinaryOutput),
|
||||
cv.Optional(CONF_SENSOR): cv.use_id(ChlorineSensor),
|
||||
cv.Optional(CONF_PUMP_CYCLE_TIME, default=360): cv.int_range(min=30, max=1400),
|
||||
cv.Optional(CONF_PUMP_PROPORTIONAL_BAND, default=200): to_proportional_band,
|
||||
cv.Optional(CONF_DISABLE_CLOCK, default=False): cv.boolean,
|
||||
cv.Optional(CONF_TARGET, 700): cv.int_range(300, 1400),
|
||||
cv.Optional(CONF_PUMP_VALUE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChlorSensorOutputTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_CYCLE_START): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChlorSensorCycleTrigger),
|
||||
}
|
||||
),
|
||||
})
|
||||
|
||||
|
||||
|
||||
ACTION_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.use_id(ChlorinePump)
|
||||
})
|
||||
|
||||
@automation.register_action("chlorine_pump.prime", PrimeAction, ACTION_SCHEMA)
|
||||
async def prime_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
@automation.register_action("chlorine_pump.start", StartAction, ACTION_SCHEMA)
|
||||
async def start_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
@automation.register_action("chlorine_pump.stop", StopAction, ACTION_SCHEMA)
|
||||
async def stop_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
@automation.register_action("chlorine_pump.set_target", SetTargetAction, ACTION_SCHEMA.extend({
|
||||
cv.Required(CONF_TARGET): cv.templatable(cv.float_),
|
||||
}))
|
||||
async def number_set_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_TARGET], args, float)
|
||||
cg.add(var.set_value(template_))
|
||||
return var
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
#yield mqtt.register_mqtt_component(var, config)
|
||||
|
||||
#btnReset = yield cg.gpio_pin_expression(config[CONF_SENSOR_PIN])
|
||||
#cg.add(var.set_input_pin(btnReset))
|
||||
|
||||
|
||||
sensor = await cg.get_variable(config[CONF_SENSOR])
|
||||
cg.add(var.set_sensor(sensor))
|
||||
|
||||
pump_out = await cg.get_variable(config[CONF_PUMP_OUT])
|
||||
cg.add(var.set_pump_out(pump_out))
|
||||
|
||||
cg.add(var.set_cycle_time(config[CONF_PUMP_CYCLE_TIME])) # Needs be configured before sensor
|
||||
|
||||
cg.add(var.disable_clock(config[CONF_DISABLE_CLOCK]))
|
||||
#cycle_time = yield cg.get_variable(config[CONF_PUMP_CYCLE_TIME])
|
||||
|
||||
#prop_band = yield cg.get_variable(config[CONF_PUMP_PROPORTIONAL_BAND])
|
||||
cg.add(var.set_prop_band(config[CONF_PUMP_PROPORTIONAL_BAND]))
|
||||
|
||||
#cg.add(var.set_state(config[CONF_STATE]))
|
||||
|
||||
|
||||
if CONF_PUMP_VALUE in config:
|
||||
for conf in config.get(CONF_PUMP_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(bool, "pState"), (int, "pTime")], conf)
|
||||
|
||||
for conf in config.get(CONF_CYCLE_START, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(int, "tOn"), (int, "tOff")], conf)
|
216
external_components/chlorine_pump/pump_cycle.h
Normal file
216
external_components/chlorine_pump/pump_cycle.h
Normal file
@ -0,0 +1,216 @@
|
||||
#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<void(bool, int)> callback_pump_;
|
||||
CallbackManager<void(int, int)> 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<void(bool, int)> &&callback){
|
||||
this->callback_pump_.add(std::move(callback));
|
||||
}
|
||||
void add_on_cycle_callback(std::function<void(int, int)> &&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<bool, int> {
|
||||
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<int, int> {
|
||||
public:
|
||||
explicit ChlorSensorCycleTrigger(ChlorinePump *parent) {
|
||||
parent->add_on_cycle_callback([this](int on_time, int off_time) { this->trigger(on_time, off_time); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class ChlorinePrime : public Action<Ts...> {
|
||||
public:
|
||||
ChlorinePrime(ChlorinePump *pump) : pump_(pump) {}
|
||||
|
||||
void play(Ts... x) override { this->pump_->prime(); }
|
||||
|
||||
protected:
|
||||
ChlorinePump *pump_;
|
||||
};
|
||||
template<typename... Ts> class ChlorineStart : public Action<Ts...> {
|
||||
public:
|
||||
ChlorineStart(ChlorinePump *pump) : pump_(pump) {}
|
||||
|
||||
void play(Ts... x) override { this->pump_->start(); }
|
||||
|
||||
protected:
|
||||
ChlorinePump *pump_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class ChlorineStop : public Action<Ts...> {
|
||||
public:
|
||||
ChlorineStop(ChlorinePump *pump) : pump_(pump) {}
|
||||
|
||||
void play(Ts... x) override { this->pump_->stop(); }
|
||||
|
||||
protected:
|
||||
ChlorinePump *pump_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class ChlorineSetTarget : public Action<Ts...> {
|
||||
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_;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user