1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-29 22:24:26 +00:00

Add new Lock core component (#2958)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Keilin Bickar
2022-02-03 13:24:31 -05:00
committed by GitHub
parent 62b366a5ec
commit 21803607e7
41 changed files with 1558 additions and 3 deletions

View File

@@ -0,0 +1,103 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import lock
from esphome.const import (
CONF_ASSUMED_STATE,
CONF_ID,
CONF_LAMBDA,
CONF_LOCK_ACTION,
CONF_OPEN_ACTION,
CONF_OPTIMISTIC,
CONF_STATE,
CONF_UNLOCK_ACTION,
)
from .. import template_ns
TemplateLock = template_ns.class_("TemplateLock", lock.Lock, cg.Component)
LockState = lock.lock_ns.enum("LockState")
LOCK_STATES = {
"LOCKED": LockState.LOCK_STATE_LOCKED,
"UNLOCKED": LockState.LOCK_STATE_UNLOCKED,
"JAMMED": LockState.LOCK_STATE_JAMMED,
"LOCKING": LockState.LOCK_STATE_LOCKING,
"UNLOCKING": LockState.LOCK_STATE_UNLOCKING,
}
validate_lock_state = cv.enum(LOCK_STATES, upper=True)
def validate(config):
if not config[CONF_OPTIMISTIC] and (
CONF_LOCK_ACTION not in config or CONF_UNLOCK_ACTION not in config
):
raise cv.Invalid(
"Either optimistic mode must be enabled, or lock_action and unlock_action must be set, "
"to handle the lock being changed."
)
return config
CONFIG_SCHEMA = cv.All(
lock.LOCK_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TemplateLock),
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
cv.Optional(CONF_UNLOCK_ACTION): automation.validate_automation(
single=True
),
cv.Optional(CONF_LOCK_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
}
).extend(cv.COMPONENT_SCHEMA),
validate,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await lock.register_lock(var, config)
if CONF_LAMBDA in config:
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.optional.template(LockState)
)
cg.add(var.set_state_lambda(template_))
if CONF_UNLOCK_ACTION in config:
await automation.build_automation(
var.get_unlock_trigger(), [], config[CONF_UNLOCK_ACTION]
)
if CONF_LOCK_ACTION in config:
await automation.build_automation(
var.get_lock_trigger(), [], config[CONF_LOCK_ACTION]
)
if CONF_OPEN_ACTION in config:
await automation.build_automation(
var.get_open_trigger(), [], config[CONF_OPEN_ACTION]
)
cg.add(var.traits.set_supports_open(CONF_OPEN_ACTION in config))
cg.add(var.traits.set_assumed_state(config[CONF_ASSUMED_STATE]))
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
@automation.register_action(
"lock.template.publish",
lock.LockPublishAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(lock.Lock),
cv.Required(CONF_STATE): cv.templatable(validate_lock_state),
}
),
)
async def lock_template_publish_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_STATE], args, LockState)
cg.add(var.set_state(template_))
return var

View File

@@ -0,0 +1,59 @@
#include "template_lock.h"
#include "esphome/core/log.h"
namespace esphome {
namespace template_ {
using namespace esphome::lock;
static const char *const TAG = "template.lock";
TemplateLock::TemplateLock()
: lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {}
void TemplateLock::loop() {
if (!this->f_.has_value())
return;
auto val = (*this->f_)();
if (!val.has_value())
return;
this->publish_state(*val);
}
void TemplateLock::control(const lock::LockCall &call) {
if (this->prev_trigger_ != nullptr) {
this->prev_trigger_->stop_action();
}
auto state = *call.get_state();
if (state == LOCK_STATE_LOCKED) {
this->prev_trigger_ = this->lock_trigger_;
this->lock_trigger_->trigger();
} else if (state == LOCK_STATE_UNLOCKED) {
this->prev_trigger_ = this->unlock_trigger_;
this->unlock_trigger_->trigger();
}
if (this->optimistic_)
this->publish_state(state);
}
void TemplateLock::open_latch() {
if (this->prev_trigger_ != nullptr) {
this->prev_trigger_->stop_action();
}
this->prev_trigger_ = this->open_trigger_;
this->open_trigger_->trigger();
}
void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
void TemplateLock::set_state_lambda(std::function<optional<lock::LockState>()> &&f) { this->f_ = f; }
float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; }
Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; }
Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; }
Trigger<> *TemplateLock::get_open_trigger() const { return this->open_trigger_; }
void TemplateLock::dump_config() {
LOG_LOCK("", "Template Lock", this);
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
}
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,38 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/lock/lock.h"
namespace esphome {
namespace template_ {
class TemplateLock : public lock::Lock, public Component {
public:
TemplateLock();
void dump_config() override;
void set_state_lambda(std::function<optional<lock::LockState>()> &&f);
Trigger<> *get_lock_trigger() const;
Trigger<> *get_unlock_trigger() const;
Trigger<> *get_open_trigger() const;
void set_optimistic(bool optimistic);
void loop() override;
float get_setup_priority() const override;
protected:
void control(const lock::LockCall &call) override;
void open_latch() override;
optional<std::function<optional<lock::LockState>()>> f_;
bool optimistic_{false};
Trigger<> *lock_trigger_;
Trigger<> *unlock_trigger_;
Trigger<> *open_trigger_;
Trigger<> *prev_trigger_{nullptr};
};
} // namespace template_
} // namespace esphome