Sprinkler controller component (#2249)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Keith Burzinski 2022-08-08 19:19:42 -05:00 committed by GitHub
parent ced423748e
commit e2cddf1005
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 2687 additions and 0 deletions

View File

@ -193,6 +193,7 @@ esphome/components/smt100/* @piechade
esphome/components/socket/* @esphome/core
esphome/components/sonoff_d1/* @anatoly-savchenkov
esphome/components/spi/* @esphome/core
esphome/components/sprinkler/* @kbx81
esphome/components/sps30/* @martgras
esphome/components/ssd1322_base/* @kbx81
esphome/components/ssd1322_spi/* @kbx81

View File

@ -0,0 +1,599 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.components import switch
from esphome.const import (
CONF_ID,
CONF_NAME,
CONF_REPEAT,
CONF_RUN_DURATION,
)
AUTO_LOAD = ["switch"]
CODEOWNERS = ["@kbx81"]
CONF_AUTO_ADVANCE_SWITCH = "auto_advance_switch"
CONF_ENABLE_SWITCH = "enable_switch"
CONF_MAIN_SWITCH = "main_switch"
CONF_MANUAL_SELECTION_DELAY = "manual_selection_delay"
CONF_MULTIPLIER = "multiplier"
CONF_PUMP_OFF_SWITCH_ID = "pump_off_switch_id"
CONF_PUMP_ON_SWITCH_ID = "pump_on_switch_id"
CONF_PUMP_PULSE_DURATION = "pump_pulse_duration"
CONF_PUMP_START_PUMP_DELAY = "pump_start_pump_delay"
CONF_PUMP_START_VALVE_DELAY = "pump_start_valve_delay"
CONF_PUMP_STOP_PUMP_DELAY = "pump_stop_pump_delay"
CONF_PUMP_STOP_VALVE_DELAY = "pump_stop_valve_delay"
CONF_PUMP_SWITCH = "pump_switch"
CONF_PUMP_SWITCH_ID = "pump_switch_id"
CONF_PUMP_SWITCH_OFF_DURING_VALVE_OPEN_DELAY = "pump_switch_off_during_valve_open_delay"
CONF_QUEUE_ENABLE_SWITCH = "queue_enable_switch"
CONF_REVERSE_SWITCH = "reverse_switch"
CONF_VALVE_NUMBER = "valve_number"
CONF_VALVE_OPEN_DELAY = "valve_open_delay"
CONF_VALVE_OVERLAP = "valve_overlap"
CONF_VALVE_PULSE_DURATION = "valve_pulse_duration"
CONF_VALVE_OFF_SWITCH_ID = "valve_off_switch_id"
CONF_VALVE_ON_SWITCH_ID = "valve_on_switch_id"
CONF_VALVE_SWITCH = "valve_switch"
CONF_VALVE_SWITCH_ID = "valve_switch_id"
CONF_VALVES = "valves"
sprinkler_ns = cg.esphome_ns.namespace("sprinkler")
Sprinkler = sprinkler_ns.class_("Sprinkler", cg.Component)
SprinklerControllerSwitch = sprinkler_ns.class_(
"SprinklerControllerSwitch", switch.Switch, cg.Component
)
SetMultiplierAction = sprinkler_ns.class_("SetMultiplierAction", automation.Action)
QueueValveAction = sprinkler_ns.class_("QueueValveAction", automation.Action)
ClearQueuedValvesAction = sprinkler_ns.class_(
"ClearQueuedValvesAction", automation.Action
)
SetRepeatAction = sprinkler_ns.class_("SetRepeatAction", automation.Action)
SetRunDurationAction = sprinkler_ns.class_("SetRunDurationAction", automation.Action)
StartFromQueueAction = sprinkler_ns.class_("StartFromQueueAction", automation.Action)
StartFullCycleAction = sprinkler_ns.class_("StartFullCycleAction", automation.Action)
StartSingleValveAction = sprinkler_ns.class_(
"StartSingleValveAction", automation.Action
)
ShutdownAction = sprinkler_ns.class_("ShutdownAction", automation.Action)
NextValveAction = sprinkler_ns.class_("NextValveAction", automation.Action)
PreviousValveAction = sprinkler_ns.class_("PreviousValveAction", automation.Action)
PauseAction = sprinkler_ns.class_("PauseAction", automation.Action)
ResumeAction = sprinkler_ns.class_("ResumeAction", automation.Action)
ResumeOrStartAction = sprinkler_ns.class_("ResumeOrStartAction", automation.Action)
def validate_sprinkler(config):
for sprinkler_controller_index, sprinkler_controller in enumerate(config):
if len(sprinkler_controller[CONF_VALVES]) <= 1:
exclusions = [
CONF_VALVE_OPEN_DELAY,
CONF_VALVE_OVERLAP,
CONF_AUTO_ADVANCE_SWITCH,
CONF_MAIN_SWITCH,
CONF_REVERSE_SWITCH,
]
for config_item in exclusions:
if config_item in sprinkler_controller:
raise cv.Invalid(f"Do not define {config_item} with only one valve")
if CONF_ENABLE_SWITCH in sprinkler_controller[CONF_VALVES][0]:
raise cv.Invalid(
f"Do not define {CONF_ENABLE_SWITCH} with only one valve"
)
else:
requirements = [
CONF_AUTO_ADVANCE_SWITCH,
CONF_MAIN_SWITCH,
]
for config_item in requirements:
if config_item not in sprinkler_controller:
raise cv.Invalid(
f"{config_item} is a required option for {sprinkler_controller_index}"
)
if (
CONF_PUMP_SWITCH_OFF_DURING_VALVE_OPEN_DELAY in sprinkler_controller
and CONF_VALVE_OPEN_DELAY not in sprinkler_controller
):
if sprinkler_controller[CONF_PUMP_SWITCH_OFF_DURING_VALVE_OPEN_DELAY]:
raise cv.Invalid(
f"{CONF_VALVE_OPEN_DELAY} must be defined when {CONF_PUMP_SWITCH_OFF_DURING_VALVE_OPEN_DELAY} is enabled"
)
for valve in sprinkler_controller[CONF_VALVES]:
if (
CONF_VALVE_OVERLAP in sprinkler_controller
and valve[CONF_RUN_DURATION] <= sprinkler_controller[CONF_VALVE_OVERLAP]
):
raise cv.Invalid(
f"{CONF_RUN_DURATION} must be greater than {CONF_VALVE_OVERLAP}"
)
if (
CONF_VALVE_OPEN_DELAY in sprinkler_controller
and valve[CONF_RUN_DURATION]
<= sprinkler_controller[CONF_VALVE_OPEN_DELAY]
):
raise cv.Invalid(
f"{CONF_RUN_DURATION} must be greater than {CONF_VALVE_OPEN_DELAY}"
)
if (
CONF_PUMP_OFF_SWITCH_ID in valve and CONF_PUMP_ON_SWITCH_ID not in valve
) or (
CONF_PUMP_ON_SWITCH_ID in valve and CONF_PUMP_OFF_SWITCH_ID not in valve
):
raise cv.Invalid(
f"Both {CONF_PUMP_OFF_SWITCH_ID} and {CONF_PUMP_ON_SWITCH_ID} must be specified for latching pump configuration"
)
if CONF_PUMP_SWITCH_ID in valve and (
CONF_PUMP_OFF_SWITCH_ID in valve or CONF_PUMP_ON_SWITCH_ID in valve
):
raise cv.Invalid(
f"Do not specify {CONF_PUMP_OFF_SWITCH_ID} or {CONF_PUMP_ON_SWITCH_ID} when using {CONF_PUMP_SWITCH_ID}"
)
if CONF_PUMP_PULSE_DURATION not in sprinkler_controller and (
CONF_PUMP_OFF_SWITCH_ID in valve or CONF_PUMP_ON_SWITCH_ID in valve
):
raise cv.Invalid(
f"{CONF_PUMP_PULSE_DURATION} must be specified when using {CONF_PUMP_OFF_SWITCH_ID} and {CONF_PUMP_ON_SWITCH_ID}"
)
if (
CONF_VALVE_OFF_SWITCH_ID in valve
and CONF_VALVE_ON_SWITCH_ID not in valve
) or (
CONF_VALVE_ON_SWITCH_ID in valve
and CONF_VALVE_OFF_SWITCH_ID not in valve
):
raise cv.Invalid(
f"Both {CONF_VALVE_OFF_SWITCH_ID} and {CONF_VALVE_ON_SWITCH_ID} must be specified for latching valve configuration"
)
if CONF_VALVE_SWITCH_ID in valve and (
CONF_VALVE_OFF_SWITCH_ID in valve or CONF_VALVE_ON_SWITCH_ID in valve
):
raise cv.Invalid(
f"Do not specify {CONF_VALVE_OFF_SWITCH_ID} or {CONF_VALVE_ON_SWITCH_ID} when using {CONF_VALVE_SWITCH_ID}"
)
if CONF_VALVE_PULSE_DURATION not in sprinkler_controller and (
CONF_VALVE_OFF_SWITCH_ID in valve or CONF_VALVE_ON_SWITCH_ID in valve
):
raise cv.Invalid(
f"{CONF_VALVE_PULSE_DURATION} must be specified when using {CONF_VALVE_OFF_SWITCH_ID} and {CONF_VALVE_ON_SWITCH_ID}"
)
if (
CONF_VALVE_SWITCH_ID not in valve
and CONF_VALVE_OFF_SWITCH_ID not in valve
and CONF_VALVE_ON_SWITCH_ID not in valve
):
raise cv.Invalid(
f"Either {CONF_VALVE_SWITCH_ID} or {CONF_VALVE_OFF_SWITCH_ID} and {CONF_VALVE_ON_SWITCH_ID} must be specified in valve configuration"
)
return config
SPRINKLER_ACTION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(Sprinkler),
}
)
SPRINKLER_ACTION_REPEAT_SCHEMA = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(Sprinkler),
cv.Required(CONF_REPEAT): cv.templatable(cv.positive_int),
},
key=CONF_REPEAT,
)
SPRINKLER_ACTION_SINGLE_VALVE_SCHEMA = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(Sprinkler),
cv.Required(CONF_VALVE_NUMBER): cv.templatable(cv.positive_int),
},
key=CONF_VALVE_NUMBER,
)
SPRINKLER_ACTION_SET_MULTIPLIER_SCHEMA = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(Sprinkler),
cv.Required(CONF_MULTIPLIER): cv.templatable(cv.positive_float),
},
key=CONF_MULTIPLIER,
)
SPRINKLER_ACTION_SET_RUN_DURATION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Sprinkler),
cv.Required(CONF_RUN_DURATION): cv.templatable(cv.positive_time_period_seconds),
cv.Required(CONF_VALVE_NUMBER): cv.templatable(cv.positive_int),
}
)
SPRINKLER_ACTION_QUEUE_VALVE_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Sprinkler),
cv.Optional(CONF_RUN_DURATION, default=0): cv.templatable(
cv.positive_time_period_seconds
),
cv.Required(CONF_VALVE_NUMBER): cv.templatable(cv.positive_int),
}
)
SPRINKLER_VALVE_SCHEMA = cv.Schema(
{
cv.Optional(CONF_ENABLE_SWITCH): cv.maybe_simple_value(
switch.SWITCH_SCHEMA.extend(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch),
}
)
),
key=CONF_NAME,
),
cv.Optional(CONF_PUMP_OFF_SWITCH_ID): cv.use_id(switch.Switch),
cv.Optional(CONF_PUMP_ON_SWITCH_ID): cv.use_id(switch.Switch),
cv.Optional(CONF_PUMP_SWITCH_ID): cv.use_id(switch.Switch),
cv.Required(CONF_RUN_DURATION): cv.positive_time_period_seconds,
cv.Required(CONF_VALVE_SWITCH): cv.maybe_simple_value(
switch.SWITCH_SCHEMA.extend(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch),
}
)
),
key=CONF_NAME,
),
cv.Optional(CONF_VALVE_OFF_SWITCH_ID): cv.use_id(switch.Switch),
cv.Optional(CONF_VALVE_ON_SWITCH_ID): cv.use_id(switch.Switch),
cv.Optional(CONF_VALVE_SWITCH_ID): cv.use_id(switch.Switch),
}
)
SPRINKLER_CONTROLLER_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(Sprinkler),
cv.Optional(CONF_AUTO_ADVANCE_SWITCH): cv.maybe_simple_value(
switch.SWITCH_SCHEMA.extend(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch),
}
)
),
key=CONF_NAME,
),
cv.Optional(CONF_MAIN_SWITCH): cv.maybe_simple_value(
switch.SWITCH_SCHEMA.extend(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch),
}
)
),
key=CONF_NAME,
),
cv.Optional(CONF_QUEUE_ENABLE_SWITCH): cv.maybe_simple_value(
switch.SWITCH_SCHEMA.extend(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch),
}
)
),
key=CONF_NAME,
),
cv.Optional(CONF_REVERSE_SWITCH): cv.maybe_simple_value(
switch.SWITCH_SCHEMA.extend(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch),
}
)
),
key=CONF_NAME,
),
cv.Optional(CONF_MANUAL_SELECTION_DELAY): cv.positive_time_period_seconds,
cv.Optional(CONF_REPEAT): cv.positive_int,
cv.Optional(CONF_PUMP_PULSE_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_VALVE_PULSE_DURATION): cv.positive_time_period_milliseconds,
cv.Exclusive(
CONF_PUMP_START_PUMP_DELAY, "pump_start_xxxx_delay"
): cv.positive_time_period_seconds,
cv.Exclusive(
CONF_PUMP_STOP_PUMP_DELAY, "pump_stop_xxxx_delay"
): cv.positive_time_period_seconds,
cv.Optional(CONF_PUMP_SWITCH_OFF_DURING_VALVE_OPEN_DELAY): cv.boolean,
cv.Exclusive(
CONF_PUMP_START_VALVE_DELAY, "pump_start_xxxx_delay"
): cv.positive_time_period_seconds,
cv.Exclusive(
CONF_PUMP_STOP_VALVE_DELAY, "pump_stop_xxxx_delay"
): cv.positive_time_period_seconds,
cv.Exclusive(
CONF_VALVE_OVERLAP, "open_delay/overlap"
): cv.positive_time_period_seconds,
cv.Exclusive(
CONF_VALVE_OPEN_DELAY, "open_delay/overlap"
): cv.positive_time_period_seconds,
cv.Required(CONF_VALVES): cv.ensure_list(SPRINKLER_VALVE_SCHEMA),
}
).extend(cv.ENTITY_BASE_SCHEMA)
CONFIG_SCHEMA = cv.All(
cv.ensure_list(SPRINKLER_CONTROLLER_SCHEMA),
validate_sprinkler,
)
@automation.register_action(
"sprinkler.set_multiplier",
SetMultiplierAction,
SPRINKLER_ACTION_SET_MULTIPLIER_SCHEMA,
)
async def sprinkler_set_multiplier_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_MULTIPLIER], args, cg.float_)
cg.add(var.set_multiplier(template_))
return var
@automation.register_action(
"sprinkler.queue_valve",
QueueValveAction,
SPRINKLER_ACTION_QUEUE_VALVE_SCHEMA,
)
async def sprinkler_set_queued_valve_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_VALVE_NUMBER], args, cg.uint8)
cg.add(var.set_valve_number(template_))
template_ = await cg.templatable(config[CONF_RUN_DURATION], args, cg.uint32)
cg.add(var.set_valve_run_duration(template_))
return var
@automation.register_action(
"sprinkler.set_repeat",
SetRepeatAction,
SPRINKLER_ACTION_REPEAT_SCHEMA,
)
async def sprinkler_set_repeat_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_REPEAT], args, cg.float_)
cg.add(var.set_repeat(template_))
return var
@automation.register_action(
"sprinkler.set_valve_run_duration",
SetRunDurationAction,
SPRINKLER_ACTION_SET_RUN_DURATION_SCHEMA,
)
async def sprinkler_set_valve_run_duration_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_VALVE_NUMBER], args, cg.uint8)
cg.add(var.set_valve_number(template_))
template_ = await cg.templatable(config[CONF_RUN_DURATION], args, cg.uint32)
cg.add(var.set_valve_run_duration(template_))
return var
@automation.register_action(
"sprinkler.start_from_queue", StartFromQueueAction, SPRINKLER_ACTION_SCHEMA
)
async def sprinkler_start_from_queue_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(
"sprinkler.start_full_cycle", StartFullCycleAction, SPRINKLER_ACTION_SCHEMA
)
async def sprinkler_start_full_cycle_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(
"sprinkler.start_single_valve",
StartSingleValveAction,
SPRINKLER_ACTION_SINGLE_VALVE_SCHEMA,
)
async def sprinkler_start_single_valve_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_VALVE_NUMBER], args, cg.uint8)
cg.add(var.set_valve_to_start(template_))
return var
@automation.register_action(
"sprinkler.clear_queued_valves", ClearQueuedValvesAction, SPRINKLER_ACTION_SCHEMA
)
@automation.register_action(
"sprinkler.next_valve", NextValveAction, SPRINKLER_ACTION_SCHEMA
)
@automation.register_action(
"sprinkler.previous_valve", PreviousValveAction, SPRINKLER_ACTION_SCHEMA
)
@automation.register_action("sprinkler.pause", PauseAction, SPRINKLER_ACTION_SCHEMA)
@automation.register_action("sprinkler.resume", ResumeAction, SPRINKLER_ACTION_SCHEMA)
@automation.register_action(
"sprinkler.resume_or_start_full_cycle", ResumeOrStartAction, SPRINKLER_ACTION_SCHEMA
)
@automation.register_action(
"sprinkler.shutdown", ShutdownAction, SPRINKLER_ACTION_SCHEMA
)
async def sprinkler_simple_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):
for sprinkler_controller in config:
if len(sprinkler_controller[CONF_VALVES]) > 1:
var = cg.new_Pvariable(
sprinkler_controller[CONF_ID],
sprinkler_controller[CONF_MAIN_SWITCH][CONF_NAME],
)
else:
var = cg.new_Pvariable(
sprinkler_controller[CONF_ID],
sprinkler_controller[CONF_VALVES][0][CONF_VALVE_SWITCH][CONF_NAME],
)
await cg.register_component(var, sprinkler_controller)
if len(sprinkler_controller[CONF_VALVES]) > 1:
sw_var = await switch.new_switch(sprinkler_controller[CONF_MAIN_SWITCH])
await cg.register_component(sw_var, sprinkler_controller[CONF_MAIN_SWITCH])
cg.add(var.set_controller_main_switch(sw_var))
sw_aa_var = await switch.new_switch(
sprinkler_controller[CONF_AUTO_ADVANCE_SWITCH]
)
await cg.register_component(
sw_aa_var, sprinkler_controller[CONF_AUTO_ADVANCE_SWITCH]
)
cg.add(var.set_controller_auto_adv_switch(sw_aa_var))
if CONF_QUEUE_ENABLE_SWITCH in sprinkler_controller:
sw_qen_var = await switch.new_switch(
sprinkler_controller[CONF_QUEUE_ENABLE_SWITCH]
)
await cg.register_component(
sw_qen_var, sprinkler_controller[CONF_QUEUE_ENABLE_SWITCH]
)
cg.add(var.set_controller_queue_enable_switch(sw_qen_var))
if CONF_REVERSE_SWITCH in sprinkler_controller:
sw_rev_var = await switch.new_switch(
sprinkler_controller[CONF_REVERSE_SWITCH]
)
await cg.register_component(
sw_rev_var, sprinkler_controller[CONF_REVERSE_SWITCH]
)
cg.add(var.set_controller_reverse_switch(sw_rev_var))
for valve in sprinkler_controller[CONF_VALVES]:
sw_valve_var = await switch.new_switch(valve[CONF_VALVE_SWITCH])
await cg.register_component(sw_valve_var, valve[CONF_VALVE_SWITCH])
if (
CONF_ENABLE_SWITCH in valve
and len(sprinkler_controller[CONF_VALVES]) > 1
):
sw_en_var = await switch.new_switch(valve[CONF_ENABLE_SWITCH])
await cg.register_component(sw_en_var, valve[CONF_ENABLE_SWITCH])
cg.add(var.add_valve(sw_valve_var, sw_en_var))
else:
cg.add(var.add_valve(sw_valve_var))
if CONF_MANUAL_SELECTION_DELAY in sprinkler_controller:
cg.add(
var.set_manual_selection_delay(
sprinkler_controller[CONF_MANUAL_SELECTION_DELAY]
)
)
if CONF_REPEAT in sprinkler_controller:
cg.add(var.set_repeat(sprinkler_controller[CONF_REPEAT]))
if CONF_VALVE_OVERLAP in sprinkler_controller:
cg.add(var.set_valve_overlap(sprinkler_controller[CONF_VALVE_OVERLAP]))
if CONF_VALVE_OPEN_DELAY in sprinkler_controller:
cg.add(
var.set_valve_open_delay(sprinkler_controller[CONF_VALVE_OPEN_DELAY])
)
if CONF_PUMP_START_PUMP_DELAY in sprinkler_controller:
cg.add(
var.set_pump_start_delay(
sprinkler_controller[CONF_PUMP_START_PUMP_DELAY]
)
)
if CONF_PUMP_STOP_PUMP_DELAY in sprinkler_controller:
cg.add(
var.set_pump_stop_delay(sprinkler_controller[CONF_PUMP_STOP_PUMP_DELAY])
)
if CONF_PUMP_START_VALVE_DELAY in sprinkler_controller:
cg.add(
var.set_valve_start_delay(
sprinkler_controller[CONF_PUMP_START_VALVE_DELAY]
)
)
if CONF_PUMP_STOP_VALVE_DELAY in sprinkler_controller:
cg.add(
var.set_valve_stop_delay(
sprinkler_controller[CONF_PUMP_STOP_VALVE_DELAY]
)
)
if CONF_PUMP_SWITCH_OFF_DURING_VALVE_OPEN_DELAY in sprinkler_controller:
cg.add(
var.set_pump_switch_off_during_valve_open_delay(
sprinkler_controller[CONF_PUMP_SWITCH_OFF_DURING_VALVE_OPEN_DELAY]
)
)
for sprinkler_controller in config:
var = await cg.get_variable(sprinkler_controller[CONF_ID])
for valve_index, valve in enumerate(sprinkler_controller[CONF_VALVES]):
if CONF_VALVE_SWITCH_ID in valve:
valve_switch = await cg.get_variable(valve[CONF_VALVE_SWITCH_ID])
cg.add(
var.configure_valve_switch(
valve_index, valve_switch, valve[CONF_RUN_DURATION]
)
)
elif CONF_VALVE_OFF_SWITCH_ID in valve and CONF_VALVE_ON_SWITCH_ID in valve:
valve_switch_off = await cg.get_variable(
valve[CONF_VALVE_OFF_SWITCH_ID]
)
valve_switch_on = await cg.get_variable(valve[CONF_VALVE_ON_SWITCH_ID])
cg.add(
var.configure_valve_switch_pulsed(
valve_index,
valve_switch_off,
valve_switch_on,
sprinkler_controller[CONF_VALVE_PULSE_DURATION],
valve[CONF_RUN_DURATION],
)
)
if CONF_PUMP_SWITCH_ID in valve:
pump = await cg.get_variable(valve[CONF_PUMP_SWITCH_ID])
cg.add(var.configure_valve_pump_switch(valve_index, pump))
elif CONF_PUMP_OFF_SWITCH_ID in valve and CONF_PUMP_ON_SWITCH_ID in valve:
pump_off = await cg.get_variable(valve[CONF_PUMP_OFF_SWITCH_ID])
pump_on = await cg.get_variable(valve[CONF_PUMP_ON_SWITCH_ID])
cg.add(
var.configure_valve_pump_switch_pulsed(
valve_index,
pump_off,
pump_on,
sprinkler_controller[CONF_PUMP_PULSE_DURATION],
)
)
for sprinkler_controller in config:
var = await cg.get_variable(sprinkler_controller[CONF_ID])
for controller_to_add in config:
if sprinkler_controller[CONF_ID] != controller_to_add[CONF_ID]:
cg.add(
var.add_controller(
await cg.get_variable(controller_to_add[CONF_ID])
)
)

View File

@ -0,0 +1,169 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/components/sprinkler/sprinkler.h"
namespace esphome {
namespace sprinkler {
template<typename... Ts> class SetMultiplierAction : public Action<Ts...> {
public:
explicit SetMultiplierAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
TEMPLATABLE_VALUE(float, multiplier)
void play(Ts... x) override { this->sprinkler_->set_multiplier(this->multiplier_.optional_value(x...)); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class QueueValveAction : public Action<Ts...> {
public:
explicit QueueValveAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
TEMPLATABLE_VALUE(size_t, valve_number)
TEMPLATABLE_VALUE(uint32_t, valve_run_duration)
void play(Ts... x) override {
this->sprinkler_->queue_valve(this->valve_number_.optional_value(x...),
this->valve_run_duration_.optional_value(x...));
}
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class ClearQueuedValvesAction : public Action<Ts...> {
public:
explicit ClearQueuedValvesAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->clear_queued_valves(); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class SetRepeatAction : public Action<Ts...> {
public:
explicit SetRepeatAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
TEMPLATABLE_VALUE(uint32_t, repeat)
void play(Ts... x) override { this->sprinkler_->set_repeat(this->repeat_.optional_value(x...)); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class SetRunDurationAction : public Action<Ts...> {
public:
explicit SetRunDurationAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
TEMPLATABLE_VALUE(size_t, valve_number)
TEMPLATABLE_VALUE(uint32_t, valve_run_duration)
void play(Ts... x) override {
this->sprinkler_->set_valve_run_duration(this->valve_number_.optional_value(x...),
this->valve_run_duration_.optional_value(x...));
}
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class StartFromQueueAction : public Action<Ts...> {
public:
explicit StartFromQueueAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->start_from_queue(); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class StartFullCycleAction : public Action<Ts...> {
public:
explicit StartFullCycleAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->start_full_cycle(); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class StartSingleValveAction : public Action<Ts...> {
public:
explicit StartSingleValveAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
TEMPLATABLE_VALUE(size_t, valve_to_start)
void play(Ts... x) override { this->sprinkler_->start_single_valve(this->valve_to_start_.optional_value(x...)); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class ShutdownAction : public Action<Ts...> {
public:
explicit ShutdownAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->shutdown(); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class NextValveAction : public Action<Ts...> {
public:
explicit NextValveAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->next_valve(); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class PreviousValveAction : public Action<Ts...> {
public:
explicit PreviousValveAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->previous_valve(); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class PauseAction : public Action<Ts...> {
public:
explicit PauseAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->pause(); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class ResumeAction : public Action<Ts...> {
public:
explicit ResumeAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->resume(); }
protected:
Sprinkler *sprinkler_;
};
template<typename... Ts> class ResumeOrStartAction : public Action<Ts...> {
public:
explicit ResumeOrStartAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {}
void play(Ts... x) override { this->sprinkler_->resume_or_start_full_cycle(); }
protected:
Sprinkler *sprinkler_;
};
} // namespace sprinkler
} // namespace esphome

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,528 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/switch/switch.h"
namespace esphome {
namespace sprinkler {
enum SprinklerState : uint8_t {
// NOTE: these states are used by both SprinklerValveOperator and Sprinkler (the controller)!
IDLE, // system/valve is off
STARTING, // system/valve is starting/"half open" -- either pump or valve is on, but the remaining pump/valve is not
ACTIVE, // system/valve is running its cycle
STOPPING, // system/valve is stopping/"half open" -- either pump or valve is on, but the remaining pump/valve is not
BYPASS // used by SprinklerValveOperator to ignore the instance checking pump status
};
enum SprinklerTimerIndex : uint8_t {
TIMER_SM = 0,
TIMER_VALVE_SELECTION = 1,
};
class Sprinkler; // this component
class SprinklerControllerSwitch; // switches that appear in the front end; based on switch core
class SprinklerSwitch; // switches representing any valve or pump; provides abstraction for latching valves
class SprinklerValveOperator; // manages all switching on/off of valves and associated pumps
class SprinklerValveRunRequest; // tells the sprinkler controller what valve to run and for how long as well as what
// SprinklerValveOperator is handling it
template<typename... Ts> class StartSingleValveAction;
template<typename... Ts> class ShutdownAction;
template<typename... Ts> class ResumeOrStartAction;
class SprinklerSwitch {
public:
SprinklerSwitch();
SprinklerSwitch(switch_::Switch *sprinkler_switch);
SprinklerSwitch(switch_::Switch *off_switch, switch_::Switch *on_switch, uint32_t pulse_duration);
bool is_latching_valve(); // returns true if configured as a latching valve
void loop(); // called as a part of loop(), used for latching valve pulses
uint32_t pulse_duration() { return this->pulse_duration_; }
bool state(); // returns the switch's current state
void set_off_switch(switch_::Switch *off_switch) { this->off_switch_ = off_switch; }
void set_on_switch(switch_::Switch *on_switch) { this->on_switch_ = on_switch; }
void set_pulse_duration(uint32_t pulse_duration) { this->pulse_duration_ = pulse_duration; }
void sync_valve_state(
bool latch_state); // syncs internal state to switch; if latching valve, sets state to latch_state
void turn_off(); // sets internal flag and actuates the switch
void turn_on(); // sets internal flag and actuates the switch
switch_::Switch *off_switch() { return this->off_switch_; }
switch_::Switch *on_switch() { return this->on_switch_; }
protected:
bool state_{false};
uint32_t pulse_duration_{0};
uint64_t pinned_millis_{0};
switch_::Switch *off_switch_{nullptr}; // only used for latching valves
switch_::Switch *on_switch_{nullptr}; // used for both latching and non-latching valves
};
struct SprinklerQueueItem {
size_t valve_number;
uint32_t run_duration;
};
struct SprinklerTimer {
const std::string name;
bool active;
uint32_t time;
uint32_t start_time;
std::function<void()> func;
};
struct SprinklerValve {
SprinklerControllerSwitch *controller_switch;
SprinklerControllerSwitch *enable_switch;
SprinklerSwitch valve_switch;
uint32_t run_duration;
optional<size_t> pump_switch_index;
bool valve_cycle_complete;
std::unique_ptr<ShutdownAction<>> valve_shutdown_action;
std::unique_ptr<StartSingleValveAction<>> valve_resumeorstart_action;
std::unique_ptr<Automation<>> valve_turn_off_automation;
std::unique_ptr<Automation<>> valve_turn_on_automation;
};
class SprinklerControllerSwitch : public switch_::Switch, public Component {
public:
SprinklerControllerSwitch();
void setup() override;
void dump_config() override;
void set_state_lambda(std::function<optional<bool>()> &&f);
void set_restore_state(bool restore_state);
Trigger<> *get_turn_on_trigger() const;
Trigger<> *get_turn_off_trigger() const;
void set_optimistic(bool optimistic);
void set_assumed_state(bool assumed_state);
void loop() override;
float get_setup_priority() const override;
protected:
bool assumed_state() override;
void write_state(bool state) override;
optional<std::function<optional<bool>()>> f_;
bool optimistic_{false};
bool assumed_state_{false};
Trigger<> *turn_on_trigger_;
Trigger<> *turn_off_trigger_;
Trigger<> *prev_trigger_{nullptr};
bool restore_state_{false};
};
class SprinklerValveOperator {
public:
SprinklerValveOperator();
SprinklerValveOperator(SprinklerValve *valve, Sprinkler *controller);
void loop();
void set_controller(Sprinkler *controller);
void set_valve(SprinklerValve *valve);
void set_run_duration(uint32_t run_duration); // set the desired run duration in seconds
void set_start_delay(uint32_t start_delay, bool start_delay_is_valve_delay);
void set_stop_delay(uint32_t stop_delay, bool stop_delay_is_valve_delay);
void start();
void stop();
uint32_t run_duration(); // returns the desired run duration in seconds
uint32_t time_remaining(); // returns seconds remaining (does not include stop_delay_)
SprinklerState state(); // returns the valve's state/status
SprinklerSwitch *pump_switch(); // returns this SprinklerValveOperator's pump's SprinklerSwitch
protected:
void pump_off_();
void pump_on_();
void valve_off_();
void valve_on_();
void kill_();
void run_();
bool start_delay_is_valve_delay_{false};
bool stop_delay_is_valve_delay_{false};
uint32_t start_delay_{0};
uint32_t stop_delay_{0};
uint32_t run_duration_{0};
uint64_t pinned_millis_{0};
Sprinkler *controller_{nullptr};
SprinklerValve *valve_{nullptr};
SprinklerState state_{IDLE};
};
class SprinklerValveRunRequest {
public:
SprinklerValveRunRequest();
SprinklerValveRunRequest(size_t valve_number, uint32_t run_duration, SprinklerValveOperator *valve_op);
bool has_request();
bool has_valve_operator();
void set_run_duration(uint32_t run_duration);
void set_valve(size_t valve_number);
void set_valve_operator(SprinklerValveOperator *valve_op);
void reset();
uint32_t run_duration();
size_t valve();
optional<size_t> valve_as_opt();
SprinklerValveOperator *valve_operator();
protected:
bool has_valve_{false};
size_t valve_number_{0};
uint32_t run_duration_{0};
SprinklerValveOperator *valve_op_{nullptr};
};
class Sprinkler : public Component, public EntityBase {
public:
Sprinkler();
Sprinkler(const std::string &name);
void setup() override;
void loop() override;
void dump_config() override;
/// add a valve to the controller
void add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw = nullptr);
/// add another controller to the controller so it can check if pumps/main valves are in use
void add_controller(Sprinkler *other_controller);
/// configure important controller switches
void set_controller_main_switch(SprinklerControllerSwitch *controller_switch);
void set_controller_auto_adv_switch(SprinklerControllerSwitch *auto_adv_switch);
void set_controller_queue_enable_switch(SprinklerControllerSwitch *queue_enable_switch);
void set_controller_reverse_switch(SprinklerControllerSwitch *reverse_switch);
/// configure a valve's switch object and run duration. run_duration is time in seconds.
void configure_valve_switch(size_t valve_number, switch_::Switch *valve_switch, uint32_t run_duration);
void configure_valve_switch_pulsed(size_t valve_number, switch_::Switch *valve_switch_off,
switch_::Switch *valve_switch_on, uint32_t pulse_duration, uint32_t run_duration);
/// configure a valve's associated pump switch object
void configure_valve_pump_switch(size_t valve_number, switch_::Switch *pump_switch);
void configure_valve_pump_switch_pulsed(size_t valve_number, switch_::Switch *pump_switch_off,
switch_::Switch *pump_switch_on, uint32_t pulse_duration);
/// value multiplied by configured run times -- used to extend or shorten the cycle
void set_multiplier(optional<float> multiplier);
/// set how long the pump should start after the valve (when the pump is starting)
void set_pump_start_delay(uint32_t start_delay);
/// set how long the pump should stop after the valve (when the pump is starting)
void set_pump_stop_delay(uint32_t stop_delay);
/// set how long the valve should start after the pump (when the pump is stopping)
void set_valve_start_delay(uint32_t start_delay);
/// set how long the valve should stop after the pump (when the pump is stopping)
void set_valve_stop_delay(uint32_t stop_delay);
/// if pump_switch_off_during_valve_open_delay is true, the controller will switch off the pump during the
/// valve_open_delay interval
void set_pump_switch_off_during_valve_open_delay(bool pump_switch_off_during_valve_open_delay);
/// set how long the controller should wait to open/switch on the valve after it becomes active
void set_valve_open_delay(uint32_t valve_open_delay);
/// set how long the controller should wait after opening a valve before closing the previous valve
void set_valve_overlap(uint32_t valve_overlap);
/// set how long the controller should wait to activate a valve after next_valve() or previous_valve() is called
void set_manual_selection_delay(uint32_t manual_selection_delay);
/// set how long the valve should remain on/open. run_duration is time in seconds
void set_valve_run_duration(optional<size_t> valve_number, optional<uint32_t> run_duration);
/// if auto_advance is true, controller will iterate through all enabled valves
void set_auto_advance(bool auto_advance);
/// set the number of times to repeat a full cycle
void set_repeat(optional<uint32_t> repeat);
/// if queue_enable is true, controller will iterate through valves in the queue
void set_queue_enable(bool queue_enable);
/// if reverse is true, controller will iterate through all enabled valves in reverse (descending) order
void set_reverse(bool reverse);
/// returns valve_number's run duration in seconds
uint32_t valve_run_duration(size_t valve_number);
/// returns valve_number's run duration (in seconds) adjusted by multiplier_
uint32_t valve_run_duration_adjusted(size_t valve_number);
/// returns true if auto_advance is enabled
bool auto_advance();
/// returns the current value of the multiplier
float multiplier();
/// returns the number of times the controller is set to repeat cycles, if at all. check with 'has_value()'
optional<uint32_t> repeat();
/// if a cycle is active, returns the number of times the controller has repeated the cycle. check with 'has_value()'
optional<uint32_t> repeat_count();
/// returns true if the queue is enabled to run
bool queue_enabled();
/// returns true if reverse is enabled
bool reverse();
/// starts the controller from the first valve in the queue and disables auto_advance.
/// if the queue is empty, does nothing.
void start_from_queue();
/// starts a full cycle of all enabled valves and enables auto_advance.
/// if no valves are enabled, all valves will be enabled.
void start_full_cycle();
/// activates a single valve and disables auto_advance.
void start_single_valve(optional<size_t> valve_number);
/// adds a valve into the queue. queued valves have priority over valves to be run as a part of a full cycle.
/// NOTE: queued valves will always run, regardless of auto-advance and/or valve enable switches.
void queue_valve(optional<size_t> valve_number, optional<uint32_t> run_duration);
/// clears/removes all valves from the queue
void clear_queued_valves();
/// advances to the next valve (numerically)
void next_valve();
/// advances to the previous valve (numerically)
void previous_valve();
/// turns off all valves, effectively shutting down the system.
void shutdown(bool clear_queue = false);
/// same as shutdown(), but also stores active_valve() and time_remaining() allowing resume() to continue the cycle
void pause();
/// resumes a cycle that was suspended using pause()
void resume();
/// if a cycle was suspended using pause(), resumes it. otherwise calls start_full_cycle()
void resume_or_start_full_cycle();
/// returns a pointer to a valve's name string object; returns nullptr if valve_number is invalid
const char *valve_name(size_t valve_number);
/// returns the number of the valve that is currently active, if any. check with 'has_value()'
optional<size_t> active_valve();
/// returns the number of the valve that is paused, if any. check with 'has_value()'
optional<size_t> paused_valve();
/// returns the number of the next valve in the queue, if any. check with 'has_value()'
optional<size_t> queued_valve();
/// returns the number of the valve that is manually selected, if any. check with 'has_value()'
/// this is set by next_valve() and previous_valve() when manual_selection_delay_ > 0
optional<size_t> manual_valve();
/// returns the number of valves the controller is configured with
size_t number_of_valves();
/// returns true if valve number is valid
bool is_a_valid_valve(size_t valve_number);
/// returns true if the pump the pointer points to is in use
bool pump_in_use(SprinklerSwitch *pump_switch);
/// switches on/off a pump "safely" by checking that the new state will not conflict with another controller
void set_pump_state(SprinklerSwitch *pump_switch, bool state);
/// returns the amount of time remaining in seconds for the active valve, if any. check with 'has_value()'
optional<uint32_t> time_remaining();
/// returns a pointer to a valve's control switch object
SprinklerControllerSwitch *control_switch(size_t valve_number);
/// returns a pointer to a valve's enable switch object
SprinklerControllerSwitch *enable_switch(size_t valve_number);
/// returns a pointer to a valve's switch object
SprinklerSwitch *valve_switch(size_t valve_number);
/// returns a pointer to a valve's pump switch object
SprinklerSwitch *valve_pump_switch(size_t valve_number);
/// returns a pointer to a valve's pump switch object
SprinklerSwitch *valve_pump_switch_by_pump_index(size_t pump_index);
protected:
uint32_t hash_base() override;
/// returns true if valve number is enabled
bool valve_is_enabled_(size_t valve_number);
/// marks a valve's cycle as complete
void mark_valve_cycle_complete_(size_t valve_number);
/// returns true if valve's cycle is flagged as complete
bool valve_cycle_complete_(size_t valve_number);
/// returns the number of the next/previous valve in the vector
size_t next_valve_number_(size_t first_valve);
size_t previous_valve_number_(size_t first_valve);
/// returns the number of the next valve that should be activated in a full cycle.
/// if no valve is next (cycle is complete), returns no value (check with 'has_value()')
optional<size_t> next_valve_number_in_cycle_(optional<size_t> first_valve = nullopt);
/// loads next_req_ with the next valve that should be activated, including its run duration.
/// if next_req_ already contains a request, nothing is done. after next_req_,
/// queued valves have priority, followed by enabled valves if auto-advance is enabled.
/// if no valve is next (for example, a full cycle is complete), next_req_ is reset via reset().
void load_next_valve_run_request_(optional<size_t> first_valve = nullopt);
/// returns the number of the next/previous valve that should be activated.
/// if no valve is next (cycle is complete), returns no value (check with 'has_value()')
optional<size_t> next_enabled_incomplete_valve_number_(optional<size_t> first_valve);
optional<size_t> previous_enabled_incomplete_valve_number_(optional<size_t> first_valve);
/// returns true if any valve is enabled
bool any_valve_is_enabled_();
/// loads an available SprinklerValveOperator (valve_op_) based on req and starts it (switches it on).
/// NOTE: if run_duration is zero, the valve's run_duration will be set based on the valve's configuration.
void start_valve_(SprinklerValveRunRequest *req);
/// turns off/closes all valves, including pump if include_pump is true
void all_valves_off_(bool include_pump = false);
/// prepares for a full cycle by verifying auto-advance is on as well as one or more valve enable switches.
void prep_full_cycle_();
/// resets the cycle state for all valves
void reset_cycle_states_();
/// resets resume state
void reset_resume_();
/// make a request of the state machine
void fsm_request_(size_t requested_valve, uint32_t requested_run_duration = 0);
/// kicks the state machine to advance, starting it if it is not already active
void fsm_kick_();
/// advance controller state, advancing to target_valve if provided
void fsm_transition_();
/// starts up the system from IDLE state
void fsm_transition_from_shutdown_();
/// transitions from ACTIVE state to ACTIVE (as in, next valve) or to a SHUTDOWN or IDLE state
void fsm_transition_from_valve_run_();
/// starts up the system from IDLE state
void fsm_transition_to_shutdown_();
/// return the current FSM state as a string
std::string state_as_str_(SprinklerState state);
/// Start/cancel/get status of valve timers
void start_timer_(SprinklerTimerIndex timer_index);
bool cancel_timer_(SprinklerTimerIndex timer_index);
/// returns true if the specified timer is active/running
bool timer_active_(SprinklerTimerIndex timer_index);
/// time is converted to milliseconds (ms) for set_timeout()
void set_timer_duration_(SprinklerTimerIndex timer_index, uint32_t time);
/// returns time in milliseconds (ms)
uint32_t timer_duration_(SprinklerTimerIndex timer_index);
std::function<void()> timer_cbf_(SprinklerTimerIndex timer_index);
/// callback functions for timers
void valve_selection_callback_();
void sm_timer_callback_();
void pump_stop_delay_callback_();
/// Maximum allowed queue size
const uint8_t max_queue_size_{100};
/// Pump should be off during valve_open_delay interval
bool pump_switch_off_during_valve_open_delay_{false};
/// Sprinkler valve cycle should overlap
bool valve_overlap_{false};
/// Pump start/stop delay interval types
bool start_delay_is_valve_delay_{false};
bool stop_delay_is_valve_delay_{false};
/// Pump start/stop delay intervals
uint32_t start_delay_{0};
uint32_t stop_delay_{0};
/// Sprinkler controller state
SprinklerState state_{IDLE};
/// The valve run request that is currently active
SprinklerValveRunRequest active_req_;
/// The number of the manually selected valve currently selected
optional<size_t> manual_valve_;
/// The number of the valve to resume from (if paused)
optional<size_t> paused_valve_;
/// The next run request for the controller to consume after active_req_ is complete
SprinklerValveRunRequest next_req_;
/// Set the number of times to repeat a full cycle
optional<uint32_t> target_repeats_;
/// Set from time_remaining() when paused
optional<uint32_t> resume_duration_;
/// Manual switching delay
optional<uint32_t> manual_selection_delay_;
/// Valve switching delay
optional<uint32_t> switching_delay_;
/// Number of times the full cycle has been repeated
uint32_t repeat_count_{0};
/// Sprinkler valve run time multiplier value
float multiplier_{1.0};
/// Queue of valves to activate next, regardless of auto-advance
std::vector<SprinklerQueueItem> queued_valves_;
/// Sprinkler valve pump objects
std::vector<SprinklerSwitch> pump_;
/// Sprinkler valve objects
std::vector<SprinklerValve> valve_;
/// Sprinkler valve operator objects
std::vector<SprinklerValveOperator> valve_op_{2};
/// Valve control timers
std::vector<SprinklerTimer> timer_{
{this->name_ + "sm", false, 0, 0, std::bind(&Sprinkler::sm_timer_callback_, this)},
{this->name_ + "vs", false, 0, 0, std::bind(&Sprinkler::valve_selection_callback_, this)}};
/// Other Sprinkler instances we should be aware of (used to check if pumps are in use)
std::vector<Sprinkler *> other_controllers_;
/// Switches we'll present to the front end
SprinklerControllerSwitch *auto_adv_sw_{nullptr};
SprinklerControllerSwitch *controller_sw_{nullptr};
SprinklerControllerSwitch *queue_enable_sw_{nullptr};
SprinklerControllerSwitch *reverse_sw_{nullptr};
std::unique_ptr<ShutdownAction<>> sprinkler_shutdown_action_;
std::unique_ptr<ResumeOrStartAction<>> sprinkler_resumeorstart_action_;
std::unique_ptr<Automation<>> sprinkler_turn_off_automation_;
std::unique_ptr<Automation<>> sprinkler_turn_on_automation_;
};
} // namespace sprinkler
} // namespace esphome

View File

@ -1128,6 +1128,49 @@ climate:
ki: 0.0
kd: 0.0
sprinkler:
- id: yard_sprinkler_ctrlr
main_switch: "Yard Sprinklers"
auto_advance_switch: "Yard Sprinklers Auto Advance"
reverse_switch: "Yard Sprinklers Reverse"
pump_start_pump_delay: 2s
pump_stop_valve_delay: 4s
pump_switch_off_during_valve_open_delay: true
valve_open_delay: 5s
valves:
- valve_switch: "Yard Valve 0"
enable_switch: "Enable Yard Valve 0"
pump_switch_id: gpio_switch1
run_duration: 10s
valve_switch_id: gpio_switch2
- valve_switch: "Yard Valve 1"
enable_switch: "Enable Yard Valve 1"
pump_switch_id: gpio_switch1
run_duration: 10s
valve_switch_id: gpio_switch2
- valve_switch: "Yard Valve 2"
enable_switch: "Enable Yard Valve 2"
pump_switch_id: gpio_switch1
run_duration: 10s
valve_switch_id: gpio_switch2
- id: garden_sprinkler_ctrlr
main_switch: "Garden Sprinklers"
auto_advance_switch: "Garden Sprinklers Auto Advance"
reverse_switch: "Garden Sprinklers Reverse"
valve_overlap: 5s
valves:
- valve_switch: "Garden Valve 0"
enable_switch: "Enable Garden Valve 0"
pump_switch_id: gpio_switch1
run_duration: 10s
valve_switch_id: gpio_switch2
- valve_switch: "Garden Valve 1"
enable_switch: "Enable Garden Valve 1"
pump_switch_id: gpio_switch1
run_duration: 10s
valve_switch_id: gpio_switch2
cover:
- platform: endstop
name: Endstop Cover