2024-05-12 21:02:52 +00:00
|
|
|
import esphome.codegen as cg
|
|
|
|
import esphome.config_validation as cv
|
|
|
|
from esphome import automation
|
2024-05-24 14:00:34 +00:00
|
|
|
from esphome.components import output, sensor
|
2024-05-12 21:02:52 +00:00
|
|
|
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"
|
2024-05-27 12:43:10 +00:00
|
|
|
CONF_TARGET_LAMBDA="get_target"
|
2024-05-24 14:00:34 +00:00
|
|
|
CONF_PUMP_TIME_DIVIDER = "pump_time_divider"
|
2024-06-04 06:42:37 +00:00
|
|
|
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_modifier"
|
|
|
|
CONF_CYCLE_TIME_OFFSET = "on_time_offset"
|
|
|
|
CONF_CYCLE_TIME_IGNORE_MAX_X_BELOW_TARGET = "ignore_max_x_below_target"
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
def to_proportional_band(value):
|
|
|
|
try:
|
|
|
|
value = int(value)
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
# pylint: disable=raise-missing-from
|
|
|
|
raise cv.Invalid(f"")
|
2024-05-24 14:00:34 +00:00
|
|
|
return cv.one_of(20, 50, 100, 150, 200, 300, 400)(value)
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
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)
|
2024-05-24 14:00:34 +00:00
|
|
|
ManualDoseAction = chlorine_pump_ns.class_("ChlorineDose", automation.Action)
|
2024-05-12 21:02:52 +00:00
|
|
|
|
2024-05-27 12:43:10 +00:00
|
|
|
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")
|
2024-06-04 06:42:37 +00:00
|
|
|
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)")
|
|
|
|
|
2024-05-27 12:43:10 +00:00
|
|
|
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_),
|
2024-06-04 06:42:37 +00:00
|
|
|
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),
|
|
|
|
}),
|
|
|
|
|
2024-05-27 12:43:10 +00:00
|
|
|
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),
|
|
|
|
}
|
|
|
|
),
|
2024-06-04 06:42:37 +00:00
|
|
|
|
2024-05-27 12:43:10 +00:00
|
|
|
}),
|
|
|
|
validate_config
|
|
|
|
)
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2024-05-24 14:00:34 +00:00
|
|
|
@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)
|
|
|
|
|
2024-05-12 21:02:52 +00:00
|
|
|
@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))
|
|
|
|
|
2024-05-24 14:00:34 +00:00
|
|
|
if CONF_SENSOR in config:
|
|
|
|
sensor = await cg.get_variable(config[CONF_SENSOR])
|
|
|
|
cg.add(var.set_sensor(sensor))
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
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]))
|
|
|
|
|
2024-06-04 06:42:37 +00:00
|
|
|
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]))
|
|
|
|
|
2024-05-12 21:02:52 +00:00
|
|
|
#cg.add(var.set_state(config[CONF_STATE]))
|
2024-05-27 12:43:10 +00:00
|
|
|
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]))
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
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)
|