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

This commit is contained in:
Nicolas Bachschwell 2024-05-12 23:02:52 +02:00
parent a96531dda9
commit 3d7e80154a
Signed by: NBSgamesAT
GPG Key ID: 2D73288FF7AEED2F
7 changed files with 489 additions and 15 deletions

View File

@ -3,6 +3,10 @@ esphome:
project: project:
name: nbsgamesat.chlorine-pump name: nbsgamesat.chlorine-pump
version: "0.2" version: "0.2"
on_boot:
priority: 600
then:
output.turn_on: whatever
esp8266: esp8266:
board: nodemcuv2 board: nodemcuv2
@ -17,7 +21,15 @@ external_components:
- source: - source:
type: local type: local
path: external_components/ path: external_components/
components: [ analog_orp ] components: [ analog_orp, chlorine_pump ]
globals:
- id: last_pump_state
type: bool
restore_value: no
initial_value: "false"
- id: last_cycle_info
type: std::string
api: api:
encryption: encryption:
@ -27,31 +39,144 @@ wifi:
ssid: !secret wifi_ssid ssid: !secret wifi_ssid
password: !secret wifi_password password: !secret wifi_password
fast_connect: true fast_connect: true
on_connect:
output.turn_off: whatever
output: output:
- platform: gpio - platform: gpio
pin: D3 pin: D6
id: pump_switch id: pump_switch
- platform: gpio
pin:
number: D0
inverted: true
id: whatever
binary_sensor: binary_sensor:
- platform: gpio - platform: gpio
id: pool_pump id: pool_pump
pin: D2 pin:
number: D2
inverted: true
mode:
input: true
pullup: true
on_state:
- script.execute:
id: manage_power
switch_state: !lambda return id(power).state;
- platform: template
id: pump_state
name: Pump State
lambda: !lambda return id(last_pump_state);
script:
- id: manage_power
parameters:
switch_state: bool
then:
- if:
condition:
lambda: |-
return switch_state && id(pool_pump).state;
then:
- chlorine_pump.start: {}
- text_sensor.template.publish:
id: cycle_text_info
state: !lambda 'return "ON... ";'
else:
- chlorine_pump.stop: {}
- text_sensor.template.publish:
id: cycle_text_info
state: !lambda 'return "PUMP: OFF";'
switch:
- platform: template
name: Chlorine Pump Power
id: power
optimistic: true
inverted: off
restore_mode: RESTORE_DEFAULT_ON
turn_on_action:
- script.execute:
id: manage_power
switch_state: true
turn_off_action:
- script.execute:
id: manage_power
switch_state: false
button:
- platform: template
name: Prime
id: prime
on_press:
- chlorine_pump.prime: {}
- text_sensor.template.publish:
id: cycle_text_info
state: !lambda 'return "PRIMING??";'
number:
- platform: template
name: Chlorine Target
id: chlorine_target
initial_value: 700
restore_value: true
min_value: 300
max_value: 1400
optimistic: true
step: 1.0
set_action:
then:
- chlorine_pump.set_target:
target: !lambda return x;
text_sensor:
- platform: template
name: Cycle Info
id: cycle_text_info
sensor: sensor:
- platform: analog_orp - platform: analog_orp
name: Chlorine name: Chlorine
id: chlorine_sensor id: chlorine_sensor
pin: A0 pin: A0
zero_point: 640 zero_point: 673
inverted: true inverted: true
update_interval: 1s update_interval: 1s
# print_raw: true
average: average:
mesurements: 5 mesurements: 15
send_state_every: 10 send_state_every: 10
on_completely_new_value: # on_value_read:
- lambda: |- # - lambda: |-
ESP_LOGD("WHAT", "Just a test %.1f", value); # ESP_LOGD("WHAT", "Chlorine_value, %.1f", x);
# GPIO A0 to acd - platform: hx711
name: "Chlorine Canister Levels"
dout_pin: D5
clk_pin: D1
gain: 128
update_interval: 20s
accuracy_decimals: 1
filters:
- calibrate_linear:
- 47608 -> 0
- 590566 -> 100
unit_of_measurement: "%"
chlorine_pump:
pump: pump_switch
sensor: chlorine_sensor
id: chlorine_pump_component
target: 700
disable_clock: true
on_pump_value:
- lambda: |-
if(pState != id(last_pump_state)){
id(last_pump_state) = pState;
}
on_cycle_start:
- lambda: |-
id(cycle_text_info).publish_state("tOn: " + std::to_string(tOn) + "s\n tOff: " + std::to_string(tOff) + "s");
# Here is space for any display implementation that could possibliy be. Have fun OK # Here is space for any display implementation that could possibliy be. Have fun OK

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, " Inverted: %s", inverted_ ? "true" : "false");
ESP_LOGCONFIG(TAG, " Print Raw: %s", print_raw_ ? "true" : "false"); ESP_LOGCONFIG(TAG, " Print Raw: %s", print_raw_ ? "true" : "false");
} }
bool ChlorineSensor::has_averager(){
return averager_ != nullptr;
}
/* /*
Nullwert : 680 Nullwert : 680

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include "esphome.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/gpio.h" #include "esphome/core/gpio.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "Arduino.h"
namespace esphome { namespace esphome {
namespace analog_orp { 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_average_change_callback(std::function<void(float)> &&callback);
void add_new_value_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); void init_averager(int mesurements, int send_average_every);
bool has_averager();
void setup() override; void setup() override;
void update() override; void update() override;
float sample(); float sample();

View File

@ -26,7 +26,7 @@ CONF_AVERAGE_NEW = "on_completely_new_value"
UNIT_MILLI_VOLT = "mV" UNIT_MILLI_VOLT = "mV"
ICON_LIGHTNING_BOLT="mdi:lightning-bolt" ICON_TEST_TUBE="mdi:test-tube"
ChlorineValueRead = chlorine_sensor_ns.class_( ChlorineValueRead = chlorine_sensor_ns.class_(
"ChlorineValueReadTrigger", automation.Trigger.template(cg.float_) "ChlorineValueReadTrigger", automation.Trigger.template(cg.float_)
@ -45,7 +45,7 @@ CONFIG_SCHEMA = cv.All(
accuracy_decimals=2, accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE, device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
icon=ICON_LIGHTNING_BOLT icon=ICON_TEST_TUBE
).extend({ ).extend({
cv.Required(CONF_SENSOR_PIN): adc.validate_adc_pin, cv.Required(CONF_SENSOR_PIN): adc.validate_adc_pin,
cv.Required(CONF_ZERO_POINT): cv.Range(min=0, max=1023), 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, []): for conf in averager_config.get(CONF_ON_READ_VALUE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) 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, []): for conf in averager_config.get(CONF_AVERAGE_ON_VALUE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) 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, []): for conf in averager_config.get(CONF_AVERAGE_NEW, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) 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_;
};
}
}