Compare commits
39 Commits
a837b8494a
...
main
Author | SHA1 | Date | |
---|---|---|---|
8198d8f62e | |||
3d06338782
|
|||
3b2414c3c4 | |||
db2cb2a556 | |||
051c6f32ce | |||
47335abc89 | |||
6fd235c2f9
|
|||
8c9c9f403c
|
|||
a9bc9d15cd
|
|||
a28af51304
|
|||
e1a7d166e5
|
|||
1d0c3fd07b
|
|||
8b11af147f
|
|||
f3d7f4183a
|
|||
30db0802a0
|
|||
f0be663618
|
|||
82d3a7458a
|
|||
53831f23f3
|
|||
cfd7c86235
|
|||
d21229fa73
|
|||
1bd3b7eb90
|
|||
4b7195a037
|
|||
fd6d54697c
|
|||
cb6a250d29
|
|||
d38b9b38d7
|
|||
1e35bff570
|
|||
12f9dea01b
|
|||
01548813dc
|
|||
3d7e80154a
|
|||
a96531dda9
|
|||
d6c8d35385
|
|||
056990076e
|
|||
9fac30e714
|
|||
cdcd297d68
|
|||
42f9224c05
|
|||
264bcb66f6
|
|||
8ed0efd32c
|
|||
5e89b866b4
|
|||
bdb3ec110f
|
87
a3pool.yaml
87
a3pool.yaml
@ -1,10 +1,12 @@
|
||||
esphome:
|
||||
name: a3pool
|
||||
|
||||
esp8266:
|
||||
board: nodemcuv2
|
||||
platform: ESP8266
|
||||
|
||||
ota:
|
||||
password: !secret a3poolpasswd
|
||||
- platform: esphome
|
||||
password: !secret a3poolpasswd
|
||||
|
||||
api:
|
||||
encryption:
|
||||
@ -16,6 +18,12 @@ wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
ap:
|
||||
ssid: a3pool_rescue
|
||||
password: !secret rescue_ap_password
|
||||
|
||||
captive_portal:
|
||||
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
@ -27,47 +35,21 @@ binary_sensor:
|
||||
pullup: true
|
||||
id: pump_state
|
||||
|
||||
number:
|
||||
datetime:
|
||||
- platform: template
|
||||
name: "Start Hour"
|
||||
id: start_h
|
||||
name: Pool Pump Start Time
|
||||
id: pool_start
|
||||
type: time
|
||||
optimistic: true
|
||||
min_value: 0
|
||||
max_value: 23
|
||||
restore_value: true
|
||||
initial_value: 8
|
||||
mode: box
|
||||
step: 1
|
||||
initial_value: "08:00:00"
|
||||
- platform: template
|
||||
name: "Start Minute"
|
||||
id: start_m
|
||||
name: Pool Pump End Time
|
||||
id: pool_end
|
||||
type: time
|
||||
optimistic: true
|
||||
min_value: 0
|
||||
max_value: 59
|
||||
initial_value: 0
|
||||
restore_value: true
|
||||
mode: box
|
||||
step: 1
|
||||
- platform: template
|
||||
name: "End Hour"
|
||||
id: end_h
|
||||
optimistic: true
|
||||
min_value: 0
|
||||
max_value: 23
|
||||
restore_value: true
|
||||
initial_value: 20
|
||||
mode: box
|
||||
step: 1
|
||||
- platform: template
|
||||
name: "End Minute"
|
||||
id: end_m
|
||||
optimistic: true
|
||||
min_value: 0
|
||||
max_value: 59
|
||||
restore_value: true
|
||||
initial_value: 0
|
||||
mode: box
|
||||
step: 1
|
||||
initial_value: "20:00:00"
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
@ -75,25 +57,25 @@ button:
|
||||
on_press:
|
||||
then:
|
||||
- lambda: |-
|
||||
int startMins = (int) (id(start_h).state * 60) + id(start_m).state;
|
||||
int endMins = (int) (id(end_h).state * 60) + id(end_m).state;
|
||||
int nowMins = (id(a3poolTime).now().hour * 60) + id(a3poolTime).now().minute;
|
||||
int startSec = (int) (id(pool_start).hour * 3600) + (id(pool_start).minute * 60) + id(pool_start).second;
|
||||
int endSec = (int) (id(pool_end).hour * 3600) + (id(pool_end).minute * 60) + id(pool_end).second;
|
||||
int nowSec = (id(a3poolTime).now().hour * 3600) + (id(a3poolTime).now().minute * 60) + id(a3poolTime).now().second;
|
||||
|
||||
if(!id(control).state){
|
||||
// Make sure wierd error or whatever
|
||||
return;
|
||||
}
|
||||
|
||||
if(startMins < endMins){
|
||||
if(startMins < nowMins && nowMins < endMins){
|
||||
if(startSec < endSec){
|
||||
if(startSec < nowSec && nowSec < endSec){
|
||||
id(pump).turn_on();
|
||||
}
|
||||
else{
|
||||
id(pump).turn_off();
|
||||
}
|
||||
}
|
||||
else if(startMins > endMins){
|
||||
if(endMins < nowMins && nowMins < startMins){
|
||||
else if(startSec > endSec){
|
||||
if(endSec < nowSec && nowSec < startSec){
|
||||
id(pump).turn_off();
|
||||
}
|
||||
else{
|
||||
@ -151,7 +133,7 @@ time:
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(a3poolTime).now().hour == (int) id(start_h).state && id(a3poolTime).now().minute == (int) id(start_m).state && id(control).state;'
|
||||
lambda: 'return id(a3poolTime).now().hour == id(pool_start).hour && id(a3poolTime).now().minute == id(pool_start).minute/* && id(a3poolTime).now().second == id(pool_start).second*/ && id(control).state;'
|
||||
then:
|
||||
- switch.turn_on: pump
|
||||
- seconds: 0
|
||||
@ -159,18 +141,21 @@ time:
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(a3poolTime).now().hour == (int) id(end_h).state && id(a3poolTime).now().minute == (int) id(end_m).state && id(control).state;'
|
||||
lambda: 'return id(a3poolTime).now().hour == id(pool_end).hour && id(a3poolTime).now().minute == id(pool_end).minute/* && id(a3poolTime).now().second == id(pool_end).second*/ && id(control).state;'
|
||||
then:
|
||||
- switch.turn_off: pump
|
||||
|
||||
dallas:
|
||||
- pin: D4
|
||||
update_interval: 10s
|
||||
one_wire:
|
||||
- platform: gpio
|
||||
pin: D4
|
||||
|
||||
sensor:
|
||||
- platform: dallas
|
||||
- platform: dallas_temp
|
||||
address: 0xd1000007494b3628
|
||||
name: "Pool Temperature"
|
||||
- platform: dallas
|
||||
update_interval: 10s
|
||||
- platform: dallas_temp
|
||||
address: 0x440000073de2e728
|
||||
name: "Solar Temperature"
|
||||
update_interval: 10s
|
||||
|
||||
|
205
chlorPump.yaml
Normal file
205
chlorPump.yaml
Normal file
@ -0,0 +1,205 @@
|
||||
esphome:
|
||||
name: chlorine-pump
|
||||
project:
|
||||
name: nbsgamesat.chlorine-pump
|
||||
version: "0.7"
|
||||
on_boot:
|
||||
priority: 600
|
||||
then:
|
||||
- output.turn_on: wifi_status_led
|
||||
- chlorine_pump.set_target:
|
||||
target: !lambda return id(chlorine_target).state;
|
||||
|
||||
|
||||
esp8266:
|
||||
board: nodemcuv2
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret cp_password
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: external_components/
|
||||
components: [ chlorine_pump, ezo_orp_i2c]
|
||||
|
||||
globals:
|
||||
- id: last_pump_state
|
||||
type: bool
|
||||
restore_value: no
|
||||
initial_value: "false"
|
||||
- id: last_cycle_info
|
||||
type: std::string
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret cp_key
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
on_connect:
|
||||
output.turn_off: wifi_status_led
|
||||
on_disconnect:
|
||||
output.turn_on: wifi_status_led
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin: D6
|
||||
id: pump_switch
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: D0
|
||||
inverted: true
|
||||
id: wifi_status_led
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
id: pool_pump
|
||||
pin:
|
||||
number: D5
|
||||
inverted: true
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
on_state:
|
||||
- script.execute:
|
||||
id: manage_power
|
||||
switch_state: !lambda return id(power).state;
|
||||
- platform: gpio
|
||||
id: calibrater
|
||||
pin:
|
||||
number: GPIO0
|
||||
inverted: true
|
||||
on_press:
|
||||
- sensor.ezo_orp_i2c.print_device_info:
|
||||
id: chlorine_sensor
|
||||
on_click:
|
||||
min_length: 5s
|
||||
max_length: 10s
|
||||
then:
|
||||
- sensor.ezo_orp_i2c.calibrate:
|
||||
id: chlorine_sensor
|
||||
calibrate_target: 475
|
||||
- output.turn_on: wifi_status_led
|
||||
- delay: 2s
|
||||
- output.turn_off: wifi_status_led
|
||||
- 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
|
||||
|
||||
i2c:
|
||||
|
||||
sensor:
|
||||
- platform: ezo_orp_i2c
|
||||
id: chlorine_sensor
|
||||
name: Chlorine
|
||||
update_interval: 5s
|
||||
- platform: hx711
|
||||
name: "Chlorine Canister Levels"
|
||||
dout_pin: D7
|
||||
clk_pin: D8
|
||||
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
|
||||
disable_clock: false
|
||||
proportional_band: 200
|
||||
cycle_time: 300
|
||||
target: 700
|
||||
# get_target: !lambda return id(chlorine_target).state;
|
||||
cycle_modifiers:
|
||||
min_on_time: 30
|
||||
max_on_time: 240
|
||||
on_time_multiplier: 0.5
|
||||
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
|
@ -1,37 +0,0 @@
|
||||
esphome:
|
||||
name: Clor_Pump
|
||||
platform: ESP8266
|
||||
board: nodemcuv2
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
|
||||
ota:
|
||||
password: !secret cp_password
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret cp_key
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_passwd
|
||||
fast_connect: true
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
pin: D3
|
||||
id: pump_switch
|
||||
- platform: custom
|
||||
name: Clor Pump
|
||||
|
||||
button:
|
||||
- platform: custom
|
||||
name: prime
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
id: pool_pump
|
||||
pin: D2
|
||||
|
||||
# Here is space for any display implementation that could possibliy be. Have fun OK
|
1
external_components/analog_orp/__init__.py
Normal file
1
external_components/analog_orp/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@nbsgames"]
|
139
external_components/analog_orp/analog_orp.cpp
Normal file
139
external_components/analog_orp/analog_orp.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
#include "analog_orp.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace analog_orp {
|
||||
|
||||
static const char *const TAG = "sensor.analog_orp";
|
||||
|
||||
FloatAverager::FloatAverager(size_t size): size_(size), vector_(size_){
|
||||
this->index_ = 0;
|
||||
}
|
||||
|
||||
void FloatAverager::add_value(float value){
|
||||
vector_.at(index_++) = value;
|
||||
if(index_ == size_){
|
||||
index_ = 0;
|
||||
has_filled_once_ = true;
|
||||
}
|
||||
}
|
||||
float FloatAverager::create_average(){
|
||||
float sum = 0;
|
||||
for(auto point : vector_){
|
||||
sum += point;
|
||||
}
|
||||
return sum / (float) size_;
|
||||
}
|
||||
bool FloatAverager::isFull(){
|
||||
return index_ == 0;
|
||||
}
|
||||
bool FloatAverager::has_filled_once(){
|
||||
return has_filled_once_;
|
||||
}
|
||||
|
||||
void ChlorineSensor::set_pin(InternalGPIOPin *pin){
|
||||
this->pin_ = pin;
|
||||
}
|
||||
|
||||
void ChlorineSensor::set_zero_point(int zero_point){
|
||||
this->zero_point_int_ = zero_point;
|
||||
this->zero_point_ = ((float) zero_point / 1024.0f) * 3.3f; // Base Value for Clorine in Volt
|
||||
}
|
||||
|
||||
void ChlorineSensor::set_print_raw(bool print_raw){
|
||||
this->print_raw_ = print_raw;
|
||||
}
|
||||
void ChlorineSensor::set_inverted(bool inverted){
|
||||
this->inverted_ = inverted;
|
||||
}
|
||||
|
||||
void ChlorineSensor::init_averager(int mesurements, int send_average_every){
|
||||
this->averager_ = new FloatAverager(mesurements);
|
||||
this->send_averager_every_ = send_average_every;
|
||||
}
|
||||
|
||||
void ChlorineSensor::add_read_callback(std::function<void(float)> &&callback){
|
||||
this->on_value_read_.add(std::move(callback));
|
||||
}
|
||||
void ChlorineSensor::add_average_change_callback(std::function<void(float)> &&callback){
|
||||
this->on_average_changed_.add(std::move(callback));
|
||||
}
|
||||
void ChlorineSensor::add_new_value_callback(std::function<void(float)> &&callback){
|
||||
this->new_value_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void ChlorineSensor::setup(){
|
||||
ESP_LOGCONFIG(TAG, "Setting up AnalogOrp '%s'...", this->get_name().c_str());
|
||||
pin_->setup();
|
||||
ESP_LOGCONFIG(TAG, "AnalogOrp '%s' setup finished!", this->get_name().c_str());
|
||||
}
|
||||
|
||||
|
||||
void ChlorineSensor::update() {
|
||||
float value_v = this->sample();
|
||||
ESP_LOGV(TAG, "'%s': Got orp value=%.4fmV", this->get_name().c_str(), value_v);
|
||||
if(averager_ != nullptr){
|
||||
averager_->add_value(value_v);
|
||||
on_value_read_.call(value_v);
|
||||
|
||||
if(++averager_send_counter_ >= send_averager_every_) averager_send_counter_ = 0;
|
||||
|
||||
if(!averager_->has_filled_once()) return;
|
||||
float average = averager_->create_average();
|
||||
on_average_changed_.call(average);
|
||||
|
||||
if(send_averager_every_ == -1){
|
||||
if(averager_->isFull()){
|
||||
this->publish_state(average);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(averager_->isFull()){
|
||||
this->new_value_callback_.call(average);
|
||||
}
|
||||
if(averager_send_counter_ == 0){
|
||||
this->publish_state(average);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
this->publish_state(value_v);
|
||||
}
|
||||
|
||||
float ChlorineSensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void ChlorineSensor::dump_config(){
|
||||
LOG_SENSOR("", "AnalogOrp Sensor", this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_PIN(" Pin: ", pin_);
|
||||
ESP_LOGCONFIG(TAG, " Zero Point: %d", zero_point_int_);
|
||||
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
|
||||
*/
|
||||
float ChlorineSensor::sample(){
|
||||
|
||||
float raw_read = (float) analogRead(pin_->get_pin());
|
||||
if(print_raw_){
|
||||
ESP_LOGD(TAG, "analogRead read a raw value of: %.0f", raw_read);
|
||||
}
|
||||
|
||||
float raw_voltage = (raw_read / 1024.0f) * 3.3f;
|
||||
float messured_voltage = raw_voltage - zero_point_;
|
||||
|
||||
if(inverted_){
|
||||
messured_voltage = messured_voltage * -1.0f;
|
||||
}
|
||||
|
||||
float milli_volt = messured_voltage * 1000.0f;
|
||||
|
||||
return milli_volt;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
78
external_components/analog_orp/analog_orp.h
Normal file
78
external_components/analog_orp/analog_orp.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#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 {
|
||||
|
||||
class FloatAverager{
|
||||
public:
|
||||
FloatAverager(size_t size);
|
||||
void add_value(float value);
|
||||
bool isFull();
|
||||
float create_average();
|
||||
bool has_filled_once();
|
||||
protected:
|
||||
size_t size_;
|
||||
std::vector<float> vector_;
|
||||
size_t index_;
|
||||
bool has_filled_once_ = false;
|
||||
};
|
||||
|
||||
class ChlorineSensor: public PollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void set_pin(InternalGPIOPin *pin);
|
||||
void set_zero_point(int zero_point);
|
||||
void set_print_raw(bool print_raw);
|
||||
void set_inverted(bool inverted);
|
||||
void add_read_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 init_averager(int mesurements, int send_average_every);
|
||||
bool has_averager();
|
||||
void setup() override;
|
||||
void update() override;
|
||||
float sample();
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const;
|
||||
protected:
|
||||
InternalGPIOPin *pin_;
|
||||
CallbackManager<void(float)> on_value_read_;
|
||||
CallbackManager<void(float)> on_average_changed_;
|
||||
CallbackManager<void(float)> new_value_callback_;
|
||||
bool inverted_;
|
||||
bool print_raw_;
|
||||
float zero_point_;
|
||||
int zero_point_int_;
|
||||
FloatAverager *averager_;
|
||||
bool use_averager_ = false;
|
||||
int send_averager_every_ = -1;
|
||||
int averager_send_counter_ = 0;
|
||||
};
|
||||
|
||||
class ChlorineValueReadTrigger : public Trigger<float> {
|
||||
public:
|
||||
explicit ChlorineValueReadTrigger(ChlorineSensor *parent) {
|
||||
parent->add_read_callback([this](int value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
class ChlorineAverageChangeTrigger : public Trigger<float> {
|
||||
public:
|
||||
explicit ChlorineAverageChangeTrigger(ChlorineSensor *parent) {
|
||||
parent->add_average_change_callback([this](int value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
class ChlorineNewValueTrigger : public Trigger<float> {
|
||||
public:
|
||||
explicit ChlorineNewValueTrigger(ChlorineSensor *parent) {
|
||||
parent->add_new_value_callback([this](int value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
99
external_components/analog_orp/sensor.py
Normal file
99
external_components/analog_orp/sensor.py
Normal file
@ -0,0 +1,99 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import sensor
|
||||
from esphome.components.adc import sensor as adc
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
CONF_TRIGGER_ID
|
||||
)
|
||||
|
||||
chlorine_sensor_ns = cg.esphome_ns.namespace('analog_orp')
|
||||
ChlorineSensor = chlorine_sensor_ns.class_('ChlorineSensor', cg.PollingComponent, sensor.Sensor)
|
||||
|
||||
CONF_SENSOR_PIN = "pin"
|
||||
CONF_INVERTED = "inverted"
|
||||
CONF_PRINT_RAW = "print_raw"
|
||||
CONF_ZERO_POINT = "zero_point"
|
||||
CONF_AVERAGE = "average"
|
||||
CONF_AVERAGE_MESUREMENTS="mesurements"
|
||||
CONF_SEND_STATE_EVERY="send_state_every"
|
||||
CONF_ON_READ_VALUE = "on_value_read"
|
||||
CONF_AVERAGE_ON_VALUE = "on_value"
|
||||
CONF_AVERAGE_NEW = "on_completely_new_value"
|
||||
|
||||
UNIT_MILLI_VOLT = "mV"
|
||||
|
||||
ICON_TEST_TUBE="mdi:test-tube"
|
||||
|
||||
ChlorineValueRead = chlorine_sensor_ns.class_(
|
||||
"ChlorineValueReadTrigger", automation.Trigger.template(cg.float_)
|
||||
)
|
||||
ChlorineAverageChange = chlorine_sensor_ns.class_(
|
||||
"ChlorineAverageChangeTrigger", automation.Trigger.template(cg.float_)
|
||||
)
|
||||
ChlorineNewValue = chlorine_sensor_ns.class_(
|
||||
"ChlorineNewValueTrigger", automation.Trigger.template(cg.float_)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(
|
||||
ChlorineSensor,
|
||||
unit_of_measurement=UNIT_MILLI_VOLT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
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),
|
||||
cv.Optional(CONF_INVERTED, False): cv.boolean,
|
||||
cv.Optional(CONF_PRINT_RAW, False): cv.boolean,
|
||||
cv.Optional(CONF_AVERAGE): cv.Schema({
|
||||
cv.Required(CONF_AVERAGE_MESUREMENTS): cv.int_range(min=1),
|
||||
cv.Optional(CONF_SEND_STATE_EVERY): cv.int_range(min=1),
|
||||
cv.Optional(CONF_ON_READ_VALUE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChlorineValueRead),
|
||||
}),
|
||||
cv.Optional(CONF_AVERAGE_ON_VALUE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChlorineAverageChange),
|
||||
}),
|
||||
cv.Optional(CONF_AVERAGE_NEW): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChlorineNewValue),
|
||||
}),
|
||||
}),
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s"))
|
||||
)
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
sensor_pin = await cg.gpio_pin_expression(config[CONF_SENSOR_PIN])
|
||||
cg.add(var.set_pin(sensor_pin))
|
||||
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_print_raw(config[CONF_PRINT_RAW]))
|
||||
cg.add(var.set_zero_point(config[CONF_ZERO_POINT]))
|
||||
|
||||
if averager_config := config.get(CONF_AVERAGE):
|
||||
update_each = -1
|
||||
if CONF_SEND_STATE_EVERY in averager_config:
|
||||
update_each = averager_config[CONF_SEND_STATE_EVERY]
|
||||
cg.add(var.init_averager(averager_config[CONF_AVERAGE_MESUREMENTS], update_each))
|
||||
|
||||
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, "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, "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, "x")], conf)
|
188
external_components/chlorine_pump/__init__.py
Normal file
188
external_components/chlorine_pump/__init__.py
Normal file
@ -0,0 +1,188 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import output, sensor
|
||||
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"
|
||||
CONF_TARGET_LAMBDA="get_target"
|
||||
CONF_PUMP_TIME_DIVIDER = "pump_time_divider"
|
||||
CONF_CYCLE_MODIFIERS = "cycle_modifiers"
|
||||
CONF_MIN_ON_TIME = "min_on_time"
|
||||
CONF_MAX_ON_TIME = "max_on_time"
|
||||
CONF_CYCLE_TIME_MULTIPLIER = "on_time_multiplier"
|
||||
CONF_CYCLE_TIME_OFFSET = "on_time_offset"
|
||||
CONF_CYCLE_TIME_IGNORE_MAX_X_BELOW_TARGET = "ignore_max_x_below_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, 300, 400)(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)
|
||||
ManualDoseAction = chlorine_pump_ns.class_("ChlorineDose", automation.Action)
|
||||
|
||||
def validate_config(config):
|
||||
if (config.get(CONF_TARGET_LAMBDA, None) is not None) is not (config.get(CONF_TARGET, None) not in config):
|
||||
raise cv.Invalid("Either a fixed target or an get_target lambda must be set to get a target")
|
||||
if CONF_CYCLE_MODIFIERS in config:
|
||||
cycle_mod = config.get(CONF_CYCLE_MODIFIERS)
|
||||
if cycle_mod[CONF_MIN_ON_TIME] >= config[CONF_PUMP_CYCLE_TIME]:
|
||||
raise cv.Invalid("min_on_time must be smaller than cycle_time(default = 360)")
|
||||
if cycle_mod.get(CONF_MAX_ON_TIME) in cycle_mod and config[CONF_PUMP_CYCLE_TIME] < cycle_mod[CONF_MAX_ON_TIME]:
|
||||
raise cv.Invalid("max_on_time must be smaller or equal cycle_time(default = 360)")
|
||||
if cycle_mod.get(CONF_MAX_ON_TIME) in cycle_mod and cycle_mod[CONF_MIN_ON_TIME] >= cycle_mod[CONF_MAX_ON_TIME]:
|
||||
raise cv.Invalid("min_on_time must be smaller than max_on_time")
|
||||
if cycle_mod.get(CONF_CYCLE_TIME_OFFSET) >= config[CONF_PUMP_CYCLE_TIME]:
|
||||
raise cv.Invalid("cycle_time_offset must be smaller than cycle_time(default = 360)")
|
||||
if cycle_mod.get(CONF_CYCLE_TIME_IGNORE_MAX_X_BELOW_TARGET) in cycle_mod and cycle_mod[CONF_CYCLE_TIME_IGNORE_MAX_X_BELOW_TARGET] <= config[CONF_PUMP_PROPORTIONAL_BAND]:
|
||||
raise cv.Invalid("ignore_max_x_below_target must larger than proportional_band(default = 200)")
|
||||
|
||||
return config
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
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(sensor.Sensor),
|
||||
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): cv.int_range(300, 1400),
|
||||
cv.Optional(CONF_PUMP_TIME_DIVIDER, 2): cv.float_range(1, 5),
|
||||
cv.Optional(CONF_TARGET_LAMBDA, 2): cv.templatable(cv.float_),
|
||||
cv.Optional(CONF_CYCLE_MODIFIERS): cv.All({
|
||||
cv.Optional(CONF_MIN_ON_TIME, 0): cv.int_range(0, 1400),
|
||||
cv.Optional(CONF_MAX_ON_TIME): cv.int_range(0, 1400),
|
||||
cv.Optional(CONF_CYCLE_TIME_MULTIPLIER, 1): cv.float_range(0.01, 5.0),
|
||||
cv.Optional(CONF_CYCLE_TIME_OFFSET, 0): cv.int_range(0, 1400),
|
||||
cv.Optional(CONF_CYCLE_TIME_IGNORE_MAX_X_BELOW_TARGET): cv.int_range(100 - 1500),
|
||||
}),
|
||||
|
||||
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),
|
||||
}
|
||||
),
|
||||
|
||||
}),
|
||||
validate_config
|
||||
)
|
||||
|
||||
|
||||
|
||||
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.manual_dose", 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))
|
||||
|
||||
if CONF_SENSOR in config:
|
||||
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]))
|
||||
|
||||
if CONF_CYCLE_MODIFIERS in config:
|
||||
conf = config.get(CONF_CYCLE_MODIFIERS)
|
||||
cg.add(var.set_min_on_time(conf[CONF_MIN_ON_TIME]))
|
||||
cg.add(var.set_time_multiplier(conf[CONF_CYCLE_TIME_MULTIPLIER]))
|
||||
cg.add(var.set_time_offset(conf[CONF_CYCLE_TIME_OFFSET]))
|
||||
if CONF_MAX_ON_TIME in conf:
|
||||
cg.add(var.set_max_on_time(conf[CONF_MAX_ON_TIME]))
|
||||
if CONF_CYCLE_TIME_IGNORE_MAX_X_BELOW_TARGET in conf:
|
||||
cg.add(var.set_ignore_max_x_below_target(conf[CONF_CYCLE_TIME_IGNORE_MAX_X_BELOW_TARGET]))
|
||||
|
||||
#cg.add(var.set_state(config[CONF_STATE]))
|
||||
if CONF_TARGET not in config:
|
||||
template_ = await cg.templatable(config[CONF_TARGET_LAMBDA], [], float)
|
||||
cg.add(var.set_target_lambda(template_))
|
||||
else:
|
||||
cg.add(var.set_target(config[CONF_TARGET]))
|
||||
|
||||
|
||||
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)
|
279
external_components/chlorine_pump/pump_cycle.h
Normal file
279
external_components/chlorine_pump/pump_cycle.h
Normal file
@ -0,0 +1,279 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
static const char *const TAG = "chlorine_pump";
|
||||
|
||||
namespace esphome {
|
||||
namespace chlorine_pump {
|
||||
|
||||
class ChlorinePump : public Component {
|
||||
protected:
|
||||
|
||||
sensor::Sensor *sensor_;
|
||||
output::BinaryOutput *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_ = -1;
|
||||
float tOn_divider_;
|
||||
std::function<float()> target_lambda_ = nullptr;
|
||||
|
||||
int min_on_time_ = 0;
|
||||
int max_on_time_ = -1;
|
||||
float on_time_multiplier_ = 1;
|
||||
int on_time_offset_ = 0;
|
||||
int ignore_max_x_below_target_ = -1;
|
||||
|
||||
public:
|
||||
|
||||
ChlorinePump(){
|
||||
last_action = 0;
|
||||
}
|
||||
|
||||
void disable_clock(bool disable_clock){
|
||||
this->disable_clock_ = disable_clock;
|
||||
}
|
||||
void set_sensor(sensor::Sensor *sensor){
|
||||
this->sensor_ = sensor;
|
||||
}
|
||||
void set_pump_time_divivder(float pump_time_divider){
|
||||
this->tOn_divider_ = pump_time_divider;
|
||||
}
|
||||
void set_pump_out(output::BinaryOutput *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(float target){
|
||||
this->target_ = target;
|
||||
}
|
||||
|
||||
// Cycle Time Modifiers
|
||||
void set_min_on_time(int min_on_time){
|
||||
this->min_on_time_ = min_on_time;
|
||||
}
|
||||
void set_max_on_time(int max_on_time){
|
||||
this->max_on_time_ = max_on_time;
|
||||
}
|
||||
void set_time_multiplier(float time_multi){
|
||||
this->on_time_multiplier_ = time_multi;
|
||||
}
|
||||
void set_time_offset(int offset){
|
||||
this->on_time_offset_ = offset;
|
||||
}
|
||||
void set_ignore_max_x_below_target(int set_below){
|
||||
this->ignore_max_x_below_target_ = set_below;
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
last_action = millis();
|
||||
if(disable_clock_ && sensor_ != nullptr){
|
||||
sensor_->add_on_state_callback([=](float val) -> void {
|
||||
this->tick_time(val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void set_target_lambda(std::function<float()> callback){
|
||||
target_lambda_ = std::move(callback);
|
||||
}
|
||||
|
||||
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){
|
||||
|
||||
float used_target = -1;
|
||||
if(target_ != -1){
|
||||
used_target = target_;
|
||||
}
|
||||
else{
|
||||
used_target = target_lambda_();
|
||||
}
|
||||
|
||||
if(last_mesurement >= used_target) return 0;
|
||||
|
||||
float currentVal = used_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;
|
||||
}
|
||||
|
||||
int calculate_changed_on_time(int tOn, float last_mesurement){
|
||||
|
||||
if(ignore_max_x_below_target_ != -1 && (float)(target_ - ignore_max_x_below_target_) >= last_mesurement){
|
||||
return cycle_time;
|
||||
}
|
||||
tOn = (int)((float) tOn * on_time_multiplier_);
|
||||
tOn += on_time_offset_;
|
||||
|
||||
if(tOn < min_on_time_) tOn = min_on_time_;
|
||||
if(max_on_time_ != -1 && tOn > max_on_time_) tOn = max_on_time_;
|
||||
|
||||
return tOn;
|
||||
}
|
||||
|
||||
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 = calculate_changed_on_time(seconds, last_mesurement);
|
||||
else
|
||||
tOn = 0;
|
||||
tOff = cycle_time - tOn;
|
||||
|
||||
if(seconds == 0){
|
||||
tOn = 0;
|
||||
tOff = 360;
|
||||
}
|
||||
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){
|
||||
|
||||
if(sensor_->has_state())
|
||||
tick_time(sensor_->get_state());
|
||||
|
||||
setMillisPrecies(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_config() override {
|
||||
ESP_LOGCONFIG(TAG, "Chlorine_pump controller");
|
||||
ESP_LOGCONFIG(TAG, " Ticker: %s", disable_clock_ ? "using on_value from sensor" : "using internal");
|
||||
//LOG_BINARY_OUTPUT(pump_out);
|
||||
ESP_LOGCONFIG(TAG, " Cycle Time: %ds", cycle_time);
|
||||
ESP_LOGCONFIG(TAG, " Proportional Band: %d", prop_band);
|
||||
if(target_ != -1) ESP_LOGCONFIG(TAG, " Target: %.0fmV", target_);
|
||||
else ESP_LOGCONFIG(TAG, " Target: Using get_target lambda");
|
||||
}
|
||||
};
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
1
external_components/ezo_orp_i2c/__init__.py
Normal file
1
external_components/ezo_orp_i2c/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@nbsgames"]
|
180
external_components/ezo_orp_i2c/ezo_orp.cpp
Normal file
180
external_components/ezo_orp_i2c/ezo_orp.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
#include "ezo_orp.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ezo_orp_i2c {
|
||||
|
||||
static const char *const TAG = "ezo_orp_i2c.sensor";
|
||||
|
||||
static const uint8_t COMMAND_READ[] = {0x52}; // R
|
||||
static const uint8_t COMMAND_PERFERM_CALIBRATION[] = {0x43, 0x61, 0x6C}; //Cal
|
||||
static const uint8_t COMMAND_IS_CALIBRATED[] = {0x43, 0x61, 0x6C, 0x2C, 0x3F}; //Cal
|
||||
static const uint8_t COMMAND_EXPORT_CALIBRATION[] = {0x45, 0x78, 0x70, 0x6F, 0x72, 0x74}; // Export
|
||||
static const uint8_t COMMAND_INFORMATION[] = {0x69}; // i (DON'T ASK ME WHY THAT IS ONLY A SMALL LETTER
|
||||
static const uint8_t COMMAND_STATUS[] = {0x53, 0x74, 0x61, 0x74, 0x75, 0x73}; // STATUS
|
||||
|
||||
static const uint8_t COMMAND_FACTORY_RESET[] = {0x46, 0x61, 0x63, 0x74, 0x6F, 0x72, 0x79}; // Factory
|
||||
|
||||
static const uint8_t COMMMAND_TO_ARGS_SEPERATOR = {0x2C};
|
||||
|
||||
static const int PROCESSING_DELAY_MS = 300; // FOR SOME OPERATIONS
|
||||
static const int READING_DELAY_MS = 900; // FOR BASICALLY EVERYTHING WHERE LARGE DATA IS READ/SENT
|
||||
|
||||
|
||||
void EzoOrpSensor::setup(){
|
||||
ESP_LOGCONFIG(TAG, "Setting up EzoOrpI2C '%s'...", this->get_name().c_str());
|
||||
ESP_LOGCONFIG(TAG, "EzoOrpI2C '%s' setup finished!", this->get_name().c_str());
|
||||
scheduleNextAction(SensorAction::IS_CALIBRATED, nullptr, 0, 450);
|
||||
}
|
||||
|
||||
void EzoOrpSensor::update() {
|
||||
scheduleNextAction(SensorAction::READ, nullptr, 0, 900);
|
||||
}
|
||||
|
||||
void EzoOrpSensor::read_response(int maxLength, SensorAction action) {
|
||||
uint8_t data[maxLength];
|
||||
|
||||
if(SensorAction::CALIBRATE == action){
|
||||
ESP_LOGD(TAG, "Device Calibrated");
|
||||
busy_ = false;
|
||||
runScheduledAction();
|
||||
return;
|
||||
}
|
||||
|
||||
if(i2c::ERROR_OK != this->read(data, maxLength)){
|
||||
ESP_LOGW(TAG, "Error Occured while reading respnse!");
|
||||
busy_ = false;
|
||||
runScheduledAction();
|
||||
return;
|
||||
}
|
||||
|
||||
if(data[0] != 1 && data[0] != 255){
|
||||
ESP_LOGW(TAG, "Read status code of %d, (Operation: %d)", data[0], action);
|
||||
busy_ = false;
|
||||
runScheduledAction();
|
||||
return;
|
||||
}
|
||||
|
||||
if(SensorAction::READ == action){
|
||||
float value;
|
||||
sscanf((const char*) &data[1], "%f", &value);
|
||||
this->publish_state(value);
|
||||
}
|
||||
else if(SensorAction::IS_CALIBRATED == action){
|
||||
ESP_LOGD(TAG, "%s", data[6] == 0x31 ? "Device has been calibrated" : "Device has NOT been calibrated");
|
||||
}
|
||||
else if(SensorAction::INFORMATION == action){
|
||||
ESP_LOGD(TAG, "Device Printed the following information: %s", &data[1]);
|
||||
}
|
||||
|
||||
busy_ = false;
|
||||
|
||||
runScheduledAction();
|
||||
}
|
||||
|
||||
void EzoOrpSensor::calibrate_to_target(int target){
|
||||
char targetArray[3];
|
||||
sprintf(targetArray, "%d", target);
|
||||
ESP_LOGD(TAG, "Calibrating to: %3s", targetArray);
|
||||
scheduleNextAction(SensorAction::CALIBRATE, (uint8_t*) &targetArray, 3, 4000);
|
||||
|
||||
}
|
||||
|
||||
float EzoOrpSensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void EzoOrpSensor::dump_config(){
|
||||
LOG_SENSOR("", "EzoOrpI2C Sensor", this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_I2C_DEVICE(this);
|
||||
}
|
||||
|
||||
bool EzoOrpSensor::is_busy(){
|
||||
return this->busy_;
|
||||
}
|
||||
|
||||
void EzoOrpSensor::runScheduledAction(){
|
||||
if(scheduledAction_ == SensorAction::NOTHING) return;
|
||||
scheduleNextAction(scheduledAction_, scheduledActionData_, scheduledActionDataSize_, scheduledActionTime_);
|
||||
scheduledAction_ = SensorAction::NOTHING;
|
||||
scheduledActionDataSize_ = 0;
|
||||
}
|
||||
|
||||
void EzoOrpSensor::scheduleNextAction(SensorAction action, uint8_t* data, int size, int expected_response_time){
|
||||
if(busy_){
|
||||
if(scheduledAction_ == action) return; // Queued Action cannot has no need of being repeated;
|
||||
if(scheduledAction_ != SensorAction::NOTHING) return; // Ehh... Felt better lol
|
||||
|
||||
scheduledAction_ = action;
|
||||
if(data != nullptr){
|
||||
for(int i = 0;i < size; ++i){
|
||||
scheduledActionData_[i] = data[i];
|
||||
}
|
||||
}
|
||||
scheduledActionDataSize_ = size;
|
||||
scheduledActionTime_ = expected_response_time;
|
||||
return;
|
||||
}
|
||||
|
||||
switch(action){
|
||||
case SensorAction::NOTHING:
|
||||
return;
|
||||
case SensorAction::READ:
|
||||
busy_ = true;
|
||||
send_command(COMMAND_READ, sizeof(COMMAND_READ));
|
||||
this->set_timeout(expected_response_time, [this](){ this->read_response(9, SensorAction::READ); });
|
||||
break;
|
||||
case SensorAction::CALIBRATE:
|
||||
busy_ = true;
|
||||
send_command(COMMAND_PERFERM_CALIBRATION, sizeof(COMMAND_PERFERM_CALIBRATION), scheduledActionData_, scheduledActionDataSize_);
|
||||
this->set_timeout(expected_response_time, [this](){ this->read_response(2, SensorAction::CALIBRATE); });
|
||||
break;
|
||||
case SensorAction::IS_CALIBRATED:
|
||||
busy_ = true;
|
||||
send_command(COMMAND_IS_CALIBRATED, sizeof(COMMAND_IS_CALIBRATED));
|
||||
this->set_timeout(expected_response_time, [this](){ this->read_response(8, SensorAction::IS_CALIBRATED); });
|
||||
break;
|
||||
case SensorAction::INFORMATION:
|
||||
busy_ = true;
|
||||
send_command(COMMAND_STATUS, sizeof(COMMAND_STATUS));
|
||||
this->set_timeout(expected_response_time, [this](){ this->read_response(18, SensorAction::INFORMATION); });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool EzoOrpSensor::send_command(const uint8_t* data, size_t dataSize){
|
||||
return send_command(data, dataSize, nullptr, 0);
|
||||
}
|
||||
bool EzoOrpSensor::send_command(const uint8_t* data, size_t dataSize, const uint8_t* parameterData, size_t parameterSize){
|
||||
|
||||
if(parameterData == nullptr){
|
||||
if(i2c::ERROR_OK != this->write(data, dataSize)){
|
||||
ESP_LOGW(TAG, "send_command failed writing data!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t completeData[dataSize + 1 + parameterSize];
|
||||
for(int i = 0;i < dataSize; ++i){
|
||||
completeData[i] = data[i];
|
||||
}
|
||||
completeData[dataSize++] = COMMMAND_TO_ARGS_SEPERATOR;
|
||||
for(int i = 0; i < parameterSize; ++i){
|
||||
completeData[dataSize + i] = parameterData[i];
|
||||
}
|
||||
|
||||
if(i2c::ERROR_OK != this->write(completeData, sizeof(completeData))){
|
||||
ESP_LOGW(TAG, "send_command failed writing data!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EzoOrpSensor::print_info(){
|
||||
scheduleNextAction(SensorAction::INFORMATION, nullptr, 0, 300);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
73
external_components/ezo_orp_i2c/ezo_orp.h
Normal file
73
external_components/ezo_orp_i2c/ezo_orp.h
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ezo_orp_i2c {
|
||||
|
||||
enum SensorAction: int{
|
||||
NOTHING = 0,
|
||||
READ = 10,
|
||||
CALIBRATE = 20,
|
||||
IS_CALIBRATED = 21,
|
||||
EXPORT_CALIBRATION = 30,
|
||||
FACTORY_RESET = 40,
|
||||
INFORMATION = 50,
|
||||
};
|
||||
|
||||
class EzoOrpSensor: public PollingComponent, public sensor::Sensor, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const;
|
||||
void calibrate_to_target(int target);
|
||||
void print_info();
|
||||
bool is_busy();
|
||||
protected:
|
||||
SensorAction scheduledAction_ = SensorAction::NOTHING; // THIS ACTS AS A SORT OF QUEUE
|
||||
uint8_t scheduledActionData_[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
int scheduledActionDataSize_ = 0;
|
||||
int scheduledActionTime_ = 0;
|
||||
bool busy_ = false;
|
||||
void read_response(int maxLength, SensorAction action);
|
||||
void runScheduledAction();
|
||||
void scheduleNextAction(SensorAction action, uint8_t* data, int size, int expected_response_time);
|
||||
bool send_command(const uint8_t* data, size_t dataSize);
|
||||
bool send_command(const uint8_t* data, size_t dataSize, const uint8_t* parameterData, size_t parameterSize);
|
||||
//bool read_data_(u_int8_t[] * read_data);
|
||||
};
|
||||
|
||||
|
||||
template<typename... Ts> class EzoCalibrateAction : public Action<Ts...> {
|
||||
public:
|
||||
EzoCalibrateAction(EzoOrpSensor *sensor) : sensor_(sensor) {}
|
||||
TEMPLATABLE_VALUE(float, value)
|
||||
|
||||
void play(Ts... x) override {
|
||||
this->sensor_->calibrate_to_target((int) this->value_.value(x...));
|
||||
}
|
||||
|
||||
protected:
|
||||
EzoOrpSensor *sensor_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DeviceInfoAction : public Action<Ts...> {
|
||||
public:
|
||||
DeviceInfoAction(EzoOrpSensor *sensor) : sensor_(sensor) {}
|
||||
TEMPLATABLE_VALUE(float, value)
|
||||
|
||||
void play(Ts... x) override {
|
||||
this->sensor_->print_info();
|
||||
}
|
||||
|
||||
protected:
|
||||
EzoOrpSensor *sensor_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
62
external_components/ezo_orp_i2c/sensor.py
Normal file
62
external_components/ezo_orp_i2c/sensor.py
Normal file
@ -0,0 +1,62 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import sensor, i2c
|
||||
from esphome.components.adc import sensor as adc
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
chlorine_sensor_ns = cg.esphome_ns.namespace('ezo_orp_i2c')
|
||||
ChlorineSensor = chlorine_sensor_ns.class_('EzoOrpSensor', cg.PollingComponent, sensor.Sensor, i2c.I2CDevice)
|
||||
|
||||
UNIT_MILLI_VOLT = "mV"
|
||||
|
||||
ICON_TEST_TUBE="mdi:test-tube"
|
||||
|
||||
ACTION_CONF_CALIBRATE_TARGET = "calibrate_target"
|
||||
|
||||
CalibrateAction = chlorine_sensor_ns.class_("EzoCalibrateAction", automation.Action)
|
||||
DeviceInfoAction = chlorine_sensor_ns.class_("DeviceInfoAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(
|
||||
ChlorineSensor,
|
||||
unit_of_measurement=UNIT_MILLI_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
icon=ICON_TEST_TUBE
|
||||
)
|
||||
.extend(i2c.i2c_device_schema(0x62))
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
)
|
||||
|
||||
ACTION_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.use_id(ChlorineSensor)
|
||||
})
|
||||
|
||||
@automation.register_action("sensor.ezo_orp_i2c.calibrate", CalibrateAction, ACTION_SCHEMA.extend({
|
||||
cv.Required(ACTION_CONF_CALIBRATE_TARGET): cv.int_range(100, 999),
|
||||
}))
|
||||
async def calibrate_action_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[ACTION_CONF_CALIBRATE_TARGET], args, float)
|
||||
cg.add(var.set_value(template_))
|
||||
return var
|
||||
|
||||
@automation.register_action("sensor.ezo_orp_i2c.print_device_info", DeviceInfoAction, ACTION_SCHEMA)
|
||||
async def info_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)
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
@ -15,4 +15,6 @@ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield cover.register_cover(var, config)
|
||||
yield cover.register_cover(var, config)
|
||||
|
||||
cg.add_library("plerup/EspSoftwareSerial", "8.2.0")
|
@ -26,7 +26,7 @@ public:
|
||||
cover::CoverTraits get_traits() override {
|
||||
auto traits = cover::CoverTraits();
|
||||
traits.set_is_assumed_state(false);
|
||||
traits.set_supports_position(false);
|
||||
traits.set_supports_position(true);
|
||||
traits.set_supports_tilt(false);
|
||||
traits.set_supports_stop(true);
|
||||
traits.set_supports_toggle(true);
|
||||
|
@ -19,4 +19,6 @@ def to_code(config):
|
||||
yield output.register_output(var, config)
|
||||
|
||||
callback = yield cg.get_variable(config[CONF_STATE_CALLBACK])
|
||||
cg.add(var.set_state_callback(callback))
|
||||
cg.add(var.set_state_callback(callback))
|
||||
|
||||
cg.add_library("plerup/EspSoftwareSerial", "8.2.0")
|
41
external_components/tablesoccer_btn_helper/__init__.py
Normal file
41
external_components/tablesoccer_btn_helper/__init__.py
Normal file
@ -0,0 +1,41 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import mqtt, gpio
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
DEPENDENCIES=["mqtt"]
|
||||
|
||||
tablesoccer_helper_ns = cg.esphome_ns.namespace('tablesoccer_helper')
|
||||
BtnHelperClass = tablesoccer_helper_ns.class_('BtnHelperClass', mqtt.MQTTComponent, cg.Component)
|
||||
#CONF_WS2812_PIN = "pin"
|
||||
#CONF_WS2812_RGB_MODE = "rgb_mode"
|
||||
|
||||
CONF_SENSOR_RED = "sensor_blue"
|
||||
CONF_SENSOR_BLUE = "sender_red"
|
||||
CONF_BTN_RESET = "reset_pin"
|
||||
CONF_BTN_UNDO = "undo_pin"
|
||||
CONF_KEEP_ALIVE_OUTPUT = "keep_alive"
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(BtnHelperClass),
|
||||
cv.Required(CONF_BTN_RESET): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_BTN_UNDO): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_KEEP_ALIVE_OUTPUT): cv.use_id(gpio.output.GPIOBinaryOutput),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
#yield cg.register_component(var, config)
|
||||
yield mqtt.register_mqtt_component(var, config)
|
||||
|
||||
btnReset = yield cg.gpio_pin_expression(config[CONF_BTN_RESET])
|
||||
cg.add(var.set_reset_pin(btnReset))
|
||||
|
||||
btnUndo = yield cg.gpio_pin_expression(config[CONF_BTN_UNDO])
|
||||
cg.add(var.set_undo_pin(btnUndo))
|
||||
|
||||
keep_alive = yield cg.get_variable(config[CONF_KEEP_ALIVE_OUTPUT])
|
||||
cg.add(var.set_keep_alive_output(keep_alive))
|
@ -0,0 +1,73 @@
|
||||
#include "esphome.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tablesoccer_helper {
|
||||
|
||||
class BtnHelperClass : public Component, public mqtt::CustomMQTTDevice {
|
||||
protected:
|
||||
|
||||
InternalGPIOPin *resetBtn;
|
||||
InternalGPIOPin *undoBtn;
|
||||
gpio::GPIOBinaryOutput *keep_alive;
|
||||
unsigned long lastThingDone = 0;
|
||||
unsigned long enableFrom = 0;
|
||||
|
||||
public:
|
||||
|
||||
void set_reset_pin(InternalGPIOPin *resetBtn){
|
||||
this->resetBtn = resetBtn;
|
||||
}
|
||||
void set_undo_pin(InternalGPIOPin *undoBtn){
|
||||
this->undoBtn = undoBtn;
|
||||
}
|
||||
void set_keep_alive_output(gpio::GPIOBinaryOutput *keep_alive){
|
||||
this->keep_alive = keep_alive;
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
lastThingDone = millis();
|
||||
enableFrom = millis();
|
||||
|
||||
ESP_LOGD("Tablesoccer", "Ready");
|
||||
}
|
||||
|
||||
void reset_last_thing_done(){
|
||||
lastThingDone = millis();
|
||||
}
|
||||
|
||||
void send_score(std::string side){
|
||||
publish("tabletenniscounter/control/score/count", side, 2, false);
|
||||
lastThingDone = millis();
|
||||
enableFrom = lastThingDone + 200;
|
||||
}
|
||||
|
||||
void loop() override{
|
||||
|
||||
if(millis() < enableFrom) return;
|
||||
|
||||
if(resetBtn->digital_read() == LOW && undoBtn->digital_read() == LOW){
|
||||
ESP_LOGD("Tablesoccer", "User requested shutdown, Shutting Down.");
|
||||
keep_alive->set_state(false);
|
||||
}
|
||||
if(resetBtn->digital_read() == LOW){
|
||||
|
||||
publish("tabletenniscounter/control/score/reset", "", 2, false);
|
||||
|
||||
lastThingDone = millis();
|
||||
enableFrom = lastThingDone + 500;
|
||||
}
|
||||
else if(undoBtn->digital_read() == LOW){
|
||||
publish("tabletenniscounter/control/score/undo", "", 2, false);
|
||||
lastThingDone = millis();
|
||||
enableFrom = lastThingDone + 500;
|
||||
}
|
||||
else if((millis() - lastThingDone) >= 300000){
|
||||
ESP_LOGD("Tablesoccer", "Table soccer table has't been used in over 15 minutes, Shutting Down.");
|
||||
keep_alive->set_state(false);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -6,11 +6,10 @@ esphome:
|
||||
platform: ESP32
|
||||
board: esp32dev
|
||||
|
||||
libraries:
|
||||
- plerup/EspSoftwareSerial
|
||||
|
||||
ota:
|
||||
password: !secret gd_passwd
|
||||
- platform: esphome
|
||||
password: !secret gd_passwd
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret gd_key
|
||||
@ -18,6 +17,7 @@ api:
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
power_save_mode: none
|
||||
fast_connect: true
|
||||
logger:
|
||||
#level: VERY_VERBOSE
|
||||
|
@ -12,10 +12,11 @@ api:
|
||||
key: !secret gong_key
|
||||
|
||||
ota:
|
||||
password: !secret gong_password
|
||||
- platform: esphome
|
||||
password: !secret gong_password
|
||||
|
||||
wifi:
|
||||
ssid: !secet wifi_ssid
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
|
||||
|
@ -1,16 +1,33 @@
|
||||
substitutions:
|
||||
collection_name: mic_holder #mic_holder, stairs, test2
|
||||
|
||||
|
||||
device_name_mic_holder: ledstrip-mic-holder
|
||||
entity_prefix_mic_holder: NBS Mic
|
||||
led_count_mic_holder: "32"
|
||||
|
||||
device_name_stairs: ledstrip-stairs
|
||||
entity_prefix_stairs: Stairs
|
||||
led_count_stairs: "191"
|
||||
|
||||
device_name_test2: teststrip2
|
||||
entity_prefix_test2: Test2
|
||||
led_count_test2: "56"
|
||||
|
||||
esphome:
|
||||
name: teststrip
|
||||
platform: ESP8266
|
||||
name: ${device_name_${collection_name}}
|
||||
|
||||
esp8266:
|
||||
board: nodemcuv2
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
|
||||
ota:
|
||||
password: !secret ledsimplepassword
|
||||
- platform: esphome
|
||||
password: !secret ledsimplepassword
|
||||
|
||||
api:
|
||||
password: !secret ledsimplepassword
|
||||
encryption:
|
||||
key: !secret ledsimplekey
|
||||
|
||||
@ -18,21 +35,14 @@ wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
# use_address: teststrip2.local
|
||||
|
||||
mqtt:
|
||||
broker: !secret mqtt_broker_1
|
||||
client_id: "teststrip"
|
||||
username: !secret mqtt_broker_1_username
|
||||
password: !secret mqtt_broker_1_password
|
||||
|
||||
light:
|
||||
- platform: neopixelbus
|
||||
type: GRB
|
||||
variant: WS2811
|
||||
pin: D5
|
||||
num_leds: 56
|
||||
name: ledstrip
|
||||
num_leds: ${led_count_${collection_name}} # 56, 191
|
||||
name: ${entity_prefix_${collection_name}} Leds
|
||||
effects:
|
||||
- addressable_rainbow:
|
||||
- addressable_rainbow:
|
||||
|
57
light_switch.yaml
Normal file
57
light_switch.yaml
Normal file
@ -0,0 +1,57 @@
|
||||
esphome:
|
||||
name: nbs-mainlight
|
||||
|
||||
esp8266:
|
||||
board: esp01_1m
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
ap:
|
||||
ssid: nbs_light_rescue
|
||||
password: !secret rescue_ap_password
|
||||
|
||||
captive_portal:
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret ml_key
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ml_password
|
||||
|
||||
logger:
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: GPIO0
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
inverted: true
|
||||
id: button_1
|
||||
on_press:
|
||||
then:
|
||||
- light.toggle: light_1
|
||||
|
||||
- platform: status
|
||||
name: "T1 Status"
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin: GPIO12
|
||||
id: relay_1
|
||||
|
||||
light:
|
||||
- platform: binary
|
||||
name: Main Light
|
||||
id: light_1
|
||||
output: relay_1
|
||||
|
||||
status_led:
|
||||
pin:
|
||||
number: GPIO13
|
||||
inverted: no
|
@ -2,6 +2,10 @@ esphome:
|
||||
name: pingfuss1
|
||||
platform: ESP8266
|
||||
board: nodemcuv2
|
||||
on_boot:
|
||||
priority: 300
|
||||
then:
|
||||
- output.turn_on: st_led
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
@ -14,7 +18,8 @@ logger:
|
||||
# key: !secret ttf_key
|
||||
|
||||
ota:
|
||||
password: !secret ttf_password
|
||||
- platform: esphome
|
||||
password: !secret ttf_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
@ -27,6 +32,20 @@ mqtt:
|
||||
broker: !secret mqtt_broker_1
|
||||
username: !secret mqtt_broker_1_username
|
||||
password: !secret mqtt_broker_1_password
|
||||
on_connect:
|
||||
then:
|
||||
- output.turn_off: st_led
|
||||
on_disconnect:
|
||||
then:
|
||||
- output.turn_on: st_led
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: D0
|
||||
mode: output
|
||||
inverted: true
|
||||
id: st_led
|
||||
|
||||
# Example configuration entry
|
||||
binary_sensor:
|
||||
|
@ -15,7 +15,7 @@ a3poolpasswd: # A3Pool OTA and native API password
|
||||
a3poolkey: # Native API encryption KEY
|
||||
|
||||
# Table Tennis Footswitches (No ttf encryption but may be added)
|
||||
ttfpassword: # TableTennisFootswitch OTA password
|
||||
ttf_password: # TableTennisFootswitch OTA password
|
||||
# ttfkey: # Native API encryption KEY
|
||||
|
||||
# Table Tennis ledstrip display
|
||||
@ -28,4 +28,28 @@ ss1key: # Native API encryption KEY
|
||||
|
||||
# Ledstrip Simple
|
||||
ledsimplepassword: # Ledstrip Simple OTA and native API password
|
||||
ledsimplekey: # Native API encryption KEY
|
||||
ledsimplekey: # Native API encryption KEY
|
||||
|
||||
# Bluetooth Low Energy Global Vars test
|
||||
ble_password: <INSERT>
|
||||
ble_key: <INSERT>
|
||||
|
||||
# streamsign
|
||||
streamsign_password: <INSERT>
|
||||
streamsign_key: <INSERT>
|
||||
|
||||
# Clor Pump
|
||||
cp_key: <INSERT>
|
||||
cp_password: <INSERT>
|
||||
|
||||
# Gong
|
||||
gong_key: <INSERT>
|
||||
gong_password: <INSERT>
|
||||
|
||||
# Garage Doors
|
||||
gd_key: <INSERT>
|
||||
gd_passwd: <INSERT>
|
||||
|
||||
# T1 Sonoff touch switch
|
||||
ml_key: <INSERT>
|
||||
ml_password: <INSERT>
|
||||
|
@ -1,6 +1,7 @@
|
||||
esphome:
|
||||
name: sonoffstecker1
|
||||
platform: ESP8266
|
||||
|
||||
esp8266:
|
||||
board: sonoff_basic
|
||||
|
||||
# Enable logging
|
||||
@ -15,12 +16,19 @@ api:
|
||||
|
||||
|
||||
ota:
|
||||
password: !secret ss1password
|
||||
- platform: esphome
|
||||
password: !secret ss1password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
ap:
|
||||
ssid: sonoff_stecker_1_rescue
|
||||
password: !secret rescue_ap_password
|
||||
|
||||
captive_portal:
|
||||
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
|
27
streamsign.yaml
Normal file
27
streamsign.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
esphome:
|
||||
name: streamsign
|
||||
|
||||
esp8266:
|
||||
board: d1_mini_lite
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
logger:
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret streamsign_password
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret streamsign_key
|
||||
|
||||
light:
|
||||
- platform: neopixelbus
|
||||
variant: WS2812
|
||||
pin: D4
|
||||
num_leds: 5
|
||||
type: GRB
|
||||
name: "Streaming Sign"
|
@ -13,7 +13,8 @@ external_components:
|
||||
logger:
|
||||
|
||||
ota:
|
||||
password: !secret ttdpassword
|
||||
- platform: esphome
|
||||
password: !secret ttdpassword
|
||||
|
||||
api:
|
||||
encryption:
|
||||
|
101
tablesoccer.yaml
Normal file
101
tablesoccer.yaml
Normal file
@ -0,0 +1,101 @@
|
||||
esphome:
|
||||
name: tablesoccer
|
||||
platform: ESP8266
|
||||
board: nodemcuv2
|
||||
on_boot:
|
||||
priority: 600
|
||||
then:
|
||||
- output.turn_on: power_control
|
||||
- output.turn_on: st_led
|
||||
|
||||
mqtt:
|
||||
broker: !secret mqtt_broker_1
|
||||
username: !secret mqtt_broker_1_username
|
||||
password: !secret mqtt_broker_1_password
|
||||
on_connect:
|
||||
then:
|
||||
- output.turn_off: st_led
|
||||
on_disconnect:
|
||||
then:
|
||||
- output.turn_on: st_led
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
logger:
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: external_components/
|
||||
components: [ tablesoccer_btn_helper ]
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret streamsign_password
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin: 14
|
||||
id: power_control
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: D0
|
||||
mode: output
|
||||
inverted: true
|
||||
id: st_led
|
||||
|
||||
tablesoccer_btn_helper:
|
||||
keep_alive: power_control
|
||||
reset_pin:
|
||||
number: 12
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
undo_pin:
|
||||
number: 13
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
id: btn_helper
|
||||
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
id: sensor_goal_blue
|
||||
pin:
|
||||
number: D1
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
filters:
|
||||
- delayed_on: 10ms
|
||||
on_press:
|
||||
then:
|
||||
- lambda: |-
|
||||
id(btn_helper).send_score("0");
|
||||
- platform: gpio
|
||||
id: sensor_goal_red
|
||||
pin:
|
||||
number: D2
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
filters:
|
||||
- delayed_on: 10ms
|
||||
on_press:
|
||||
then:
|
||||
- lambda: |-
|
||||
id(btn_helper).send_score("1");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pinMode(5, INPUT_PULLUP);
|
||||
#pinMode(4, INPUT_PULLUP);
|
||||
|
||||
#pinMode(14, OUTPUT);
|
||||
#pinMode(12, INPUT_PULLUP);
|
||||
#pinMode(13, INPUT_PULLUP);
|
Reference in New Issue
Block a user