Compare commits

..

39 Commits

Author SHA1 Message Date
8198d8f62e Forcing Raspi and NBS repos to be back like how they should be. MAYBE 2025-05-13 16:13:51 +02:00
3d06338782 Fixed pingpongfuss1.yaml for further use 2024-11-22 20:36:28 +01:00
3b2414c3c4 Combined patches to 2024.11.0 2024-11-18 14:08:12 +01:00
db2cb2a556 Updated Light switch for esphome 2024.8.0 2024-08-22 18:37:04 +02:00
051c6f32ce Fixed a tiny bug when attempting to use device stairs since device_name_stairs was spelled wrong 2024-06-11 21:10:36 +02:00
47335abc89 Fixed typo on Raspberri 2024-06-11 19:24:26 +02:00
6fd235c2f9 Removed Project data because it might cause trouble 2024-06-11 11:29:49 +02:00
8c9c9f403c Dammit Forgot something 2024-06-11 10:49:29 +02:00
a9bc9d15cd I seem to have forgotten what I changed in the Pi 2024-06-11 10:48:02 +02:00
a28af51304 Some tiny changes in ledstrip. Now we are perfect! 2024-06-11 10:45:05 +02:00
e1a7d166e5 Ledstrip simple got the biggest update in centuries. Now with 100% less confusion. 2024-06-11 10:38:05 +02:00
1d0c3fd07b ledstrip_simple now has to have an entity_prefix set for reasons 2024-06-10 12:23:12 +02:00
8b11af147f Changed chlorPump on_time_multiplier to 0.5 2024-06-05 08:42:10 +02:00
f3d7f4183a Added a on_time_multiplier to be 0.8
This should help to a better scaling of certain numbers
2024-06-04 10:14:56 +02:00
30db0802a0 Fixed:
Slight bug fixes for ezo_orp_i2c component

Added: A new time_modifier for chlorine_pump,
Used to set maximums and stuff for chlorine pump pump
2024-06-04 08:42:37 +02:00
f0be663618 Added Captive Portal to a3pool and sonoffstecker1 2024-05-29 10:49:54 +02:00
82d3a7458a Added fast connect to the nbs_mainlight 2024-05-29 10:40:26 +02:00
53831f23f3 Added Light_switch.yaml 2024-05-29 10:14:22 +02:00
cfd7c86235 Added ESP_LOGD chlorine-pump 2024-05-27 17:06:53 +02:00
d21229fa73 Fixed a bug for chlorPump.yaml where the target would not properly load on start 2024-05-27 14:43:10 +02:00
1bd3b7eb90 Changed chlorine-pump to use the Ezo Orp module by AtlasScientific using I2C.
This module is by miles more accurate than any analog crap we could have come up with...

Not that the last idea was anything to scoff at I guess
2024-05-25 20:01:05 +02:00
4b7195a037 Added Door Status again 2024-05-24 16:02:00 +02:00
fd6d54697c Added all the changes necessary to run chlorine pump again 2024-05-24 16:00:34 +02:00
cb6a250d29 Just commited my GONG.yaml 2024-05-24 13:11:45 +02:00
d38b9b38d7 Fixed bug that A3pool wouldn't turn off 2024-05-13 22:20:52 +02:00
1e35bff570 Removed the second check on a3pool again.yaml 2024-05-13 11:02:49 +02:00
12f9dea01b Changed one tiny things about a3pool 2024-05-13 10:51:20 +02:00
01548813dc Updated a3pool to use the new datetime template picker in HA 2024-05-13 10:44:42 +02:00
3d7e80154a All the code changes to the chlorine pump... More will follow 2024-05-12 23:02:52 +02:00
a96531dda9 Added Averager 2024-05-11 10:54:14 +02:00
d6c8d35385 Stream sign updated. Nothing more 2024-05-10 15:38:55 +02:00
056990076e Updated chlorine Pump 2024-05-10 12:37:19 +02:00
9fac30e714 Added:
OnBoard LED to turn on while ESP is not connected to MQTT for pingpongfuss and tablesoccer

Changed:
  Garage to no use NO power saving while on wifi
2024-05-04 15:00:31 +02:00
cdcd297d68 Added stream sign. Haha 2024-05-02 12:25:39 +02:00
42f9224c05 Removed the api password. Scream all you like, my gods can't hear you 2024-05-02 12:20:25 +02:00
264bcb66f6 Added TableSoccer as esphome... It's finally no longer an ill-configure Homie 2024-05-02 12:19:08 +02:00
8ed0efd32c Moved SoftwareSeriel to the .py files 2024-05-02 12:17:43 +02:00
5e89b866b4 That is the new nbs mainlight config which replaced the old tasmota config of my light switch 2024-05-01 12:35:11 +02:00
bdb3ec110f Updated secrets.yaml.template 2024-05-01 12:34:45 +02:00
28 changed files with 1735 additions and 116 deletions

View File

@ -1,9 +1,11 @@
esphome:
name: a3pool
esp8266:
board: nodemcuv2
platform: ESP8266
ota:
- platform: esphome
password: !secret a3poolpasswd
api:
@ -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
View 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

View File

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

View File

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

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

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

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

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

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

View File

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

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

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

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

View File

@ -16,3 +16,5 @@ def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield cover.register_cover(var, config)
cg.add_library("plerup/EspSoftwareSerial", "8.2.0")

View File

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

View File

@ -20,3 +20,5 @@ def to_code(config):
callback = yield cg.get_variable(config[CONF_STATE_CALLBACK])
cg.add(var.set_state_callback(callback))
cg.add_library("plerup/EspSoftwareSerial", "8.2.0")

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

View File

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

View File

@ -6,11 +6,10 @@ esphome:
platform: ESP32
board: esp32dev
libraries:
- plerup/EspSoftwareSerial
ota:
- 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

View File

@ -12,10 +12,11 @@ api:
key: !secret gong_key
ota:
- platform: esphome
password: !secret gong_password
wifi:
ssid: !secet wifi_ssid
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true

View File

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

View File

@ -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,6 +18,7 @@ logger:
# key: !secret ttf_key
ota:
- platform: esphome
password: !secret ttf_password
wifi:
@ -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:

View File

@ -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
@ -29,3 +29,27 @@ ss1key: # Native API encryption KEY
# Ledstrip Simple
ledsimplepassword: # Ledstrip Simple OTA and native API password
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>

View File

@ -1,6 +1,7 @@
esphome:
name: sonoffstecker1
platform: ESP8266
esp8266:
board: sonoff_basic
# Enable logging
@ -15,12 +16,19 @@ api:
ota:
- 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
View 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"

View File

@ -13,6 +13,7 @@ external_components:
logger:
ota:
- platform: esphome
password: !secret ttdpassword
api:

101
tablesoccer.yaml Normal file
View 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);