All the code changes to the chlorine pump... More will follow

This commit is contained in:
2024-05-12 23:02:52 +02:00
parent a96531dda9
commit 3d7e80154a
7 changed files with 489 additions and 15 deletions

View File

@ -0,0 +1 @@
CODEOWNERS = ["@nbsgames"]

View File

@ -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

View File

@ -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();

View File

@ -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)

View 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)

View 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_;
};
}
}