mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 00:18:11 +01:00
add key collector component (#4242)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
This commit is contained in:
parent
efc6a8df35
commit
1bb90f304c
6 changed files with 272 additions and 2 deletions
|
@ -113,6 +113,7 @@ esphome/components/integration/* @OttoWinter
|
|||
esphome/components/interval/* @esphome/core
|
||||
esphome/components/json/* @OttoWinter
|
||||
esphome/components/kalman_combinator/* @Cat-Ion
|
||||
esphome/components/key_collector/* @ssieb
|
||||
esphome/components/key_provider/* @ssieb
|
||||
esphome/components/lcd_menu/* @numo68
|
||||
esphome/components/ledc/* @OttoWinter
|
||||
|
|
95
esphome/components/key_collector/__init__.py
Normal file
95
esphome/components/key_collector/__init__.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import key_provider
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MAX_LENGTH,
|
||||
CONF_MIN_LENGTH,
|
||||
CONF_ON_TIMEOUT,
|
||||
CONF_SOURCE_ID,
|
||||
CONF_TIMEOUT,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@ssieb"]
|
||||
|
||||
MULTI_CONF = True
|
||||
|
||||
AUTO_LOAD = ["key_provider"]
|
||||
|
||||
CONF_START_KEYS = "start_keys"
|
||||
CONF_END_KEYS = "end_keys"
|
||||
CONF_END_KEY_REQUIRED = "end_key_required"
|
||||
CONF_BACK_KEYS = "back_keys"
|
||||
CONF_CLEAR_KEYS = "clear_keys"
|
||||
CONF_ALLOWED_KEYS = "allowed_keys"
|
||||
CONF_ON_PROGRESS = "on_progress"
|
||||
CONF_ON_RESULT = "on_result"
|
||||
|
||||
key_collector_ns = cg.esphome_ns.namespace("key_collector")
|
||||
KeyCollector = key_collector_ns.class_("KeyCollector", cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(KeyCollector),
|
||||
cv.GenerateID(CONF_SOURCE_ID): cv.use_id(key_provider.KeyProvider),
|
||||
cv.Optional(CONF_MIN_LENGTH): cv.int_,
|
||||
cv.Optional(CONF_MAX_LENGTH): cv.int_,
|
||||
cv.Optional(CONF_START_KEYS): cv.string,
|
||||
cv.Optional(CONF_END_KEYS): cv.string,
|
||||
cv.Optional(CONF_END_KEY_REQUIRED): cv.boolean,
|
||||
cv.Optional(CONF_BACK_KEYS): cv.string,
|
||||
cv.Optional(CONF_CLEAR_KEYS): cv.string,
|
||||
cv.Optional(CONF_ALLOWED_KEYS): cv.string,
|
||||
cv.Optional(CONF_ON_PROGRESS): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_ON_RESULT): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_ON_TIMEOUT): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_TIMEOUT): cv.positive_time_period_milliseconds,
|
||||
}
|
||||
),
|
||||
cv.has_at_least_one_key(CONF_END_KEYS, CONF_MAX_LENGTH),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_provider(source))
|
||||
if CONF_MIN_LENGTH in config:
|
||||
cg.add(var.set_min_length(config[CONF_MIN_LENGTH]))
|
||||
if CONF_MAX_LENGTH in config:
|
||||
cg.add(var.set_max_length(config[CONF_MAX_LENGTH]))
|
||||
if CONF_START_KEYS in config:
|
||||
cg.add(var.set_start_keys(config[CONF_START_KEYS]))
|
||||
if CONF_END_KEYS in config:
|
||||
cg.add(var.set_end_keys(config[CONF_END_KEYS]))
|
||||
if CONF_END_KEY_REQUIRED in config:
|
||||
cg.add(var.set_end_key_required(config[CONF_END_KEY_REQUIRED]))
|
||||
if CONF_BACK_KEYS in config:
|
||||
cg.add(var.set_back_keys(config[CONF_BACK_KEYS]))
|
||||
if CONF_CLEAR_KEYS in config:
|
||||
cg.add(var.set_clear_keys(config[CONF_CLEAR_KEYS]))
|
||||
if CONF_ALLOWED_KEYS in config:
|
||||
cg.add(var.set_allowed_keys(config[CONF_ALLOWED_KEYS]))
|
||||
if CONF_ON_PROGRESS in config:
|
||||
await automation.build_automation(
|
||||
var.get_progress_trigger(),
|
||||
[(cg.std_string, "x"), (cg.uint8, "start")],
|
||||
config[CONF_ON_PROGRESS],
|
||||
)
|
||||
if CONF_ON_RESULT in config:
|
||||
await automation.build_automation(
|
||||
var.get_result_trigger(),
|
||||
[(cg.std_string, "x"), (cg.uint8, "start"), (cg.uint8, "end")],
|
||||
config[CONF_ON_RESULT],
|
||||
)
|
||||
if CONF_ON_TIMEOUT in config:
|
||||
await automation.build_automation(
|
||||
var.get_timeout_trigger(),
|
||||
[(cg.std_string, "x"), (cg.uint8, "start")],
|
||||
config[CONF_ON_TIMEOUT],
|
||||
)
|
||||
if CONF_TIMEOUT in config:
|
||||
cg.add(var.set_timeout(config[CONF_TIMEOUT]))
|
95
esphome/components/key_collector/key_collector.cpp
Normal file
95
esphome/components/key_collector/key_collector.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
#include "key_collector.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace key_collector {
|
||||
|
||||
static const char *const TAG = "key_collector";
|
||||
|
||||
KeyCollector::KeyCollector()
|
||||
: progress_trigger_(new Trigger<std::string, uint8_t>()),
|
||||
result_trigger_(new Trigger<std::string, uint8_t, uint8_t>()),
|
||||
timeout_trigger_(new Trigger<std::string, uint8_t>()) {}
|
||||
|
||||
void KeyCollector::loop() {
|
||||
if ((this->timeout_ == 0) || this->result_.empty() || (millis() - this->last_key_time_ < this->timeout_))
|
||||
return;
|
||||
this->timeout_trigger_->trigger(this->result_, this->start_key_);
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void KeyCollector::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Key Collector:");
|
||||
if (this->min_length_ > 0)
|
||||
ESP_LOGCONFIG(TAG, " min length: %d", this->min_length_);
|
||||
if (this->max_length_ > 0)
|
||||
ESP_LOGCONFIG(TAG, " max length: %d", this->max_length_);
|
||||
if (!this->back_keys_.empty())
|
||||
ESP_LOGCONFIG(TAG, " erase keys '%s'", this->back_keys_.c_str());
|
||||
if (!this->clear_keys_.empty())
|
||||
ESP_LOGCONFIG(TAG, " clear keys '%s'", this->clear_keys_.c_str());
|
||||
if (!this->start_keys_.empty())
|
||||
ESP_LOGCONFIG(TAG, " start keys '%s'", this->start_keys_.c_str());
|
||||
if (!this->end_keys_.empty()) {
|
||||
ESP_LOGCONFIG(TAG, " end keys '%s'", this->end_keys_.c_str());
|
||||
ESP_LOGCONFIG(TAG, " end key is required: %s", ONOFF(this->end_key_required_));
|
||||
}
|
||||
if (!this->allowed_keys_.empty())
|
||||
ESP_LOGCONFIG(TAG, " allowed keys '%s'", this->allowed_keys_.c_str());
|
||||
if (this->timeout_ > 0)
|
||||
ESP_LOGCONFIG(TAG, " entry timeout: %0.1f", this->timeout_ / 1000.0);
|
||||
}
|
||||
|
||||
void KeyCollector::set_provider(key_provider::KeyProvider *provider) {
|
||||
provider->add_on_key_callback([this](uint8_t key) { this->key_pressed_(key); });
|
||||
}
|
||||
|
||||
void KeyCollector::clear(bool progress_update) {
|
||||
this->result_.clear();
|
||||
this->start_key_ = 0;
|
||||
if (progress_update)
|
||||
this->progress_trigger_->trigger(this->result_, 0);
|
||||
}
|
||||
|
||||
void KeyCollector::key_pressed_(uint8_t key) {
|
||||
this->last_key_time_ = millis();
|
||||
if (!this->start_keys_.empty() && !this->start_key_) {
|
||||
if (this->start_keys_.find(key) != std::string::npos) {
|
||||
this->start_key_ = key;
|
||||
this->progress_trigger_->trigger(this->result_, this->start_key_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this->back_keys_.find(key) != std::string::npos) {
|
||||
if (!this->result_.empty()) {
|
||||
this->result_.pop_back();
|
||||
this->progress_trigger_->trigger(this->result_, this->start_key_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this->clear_keys_.find(key) != std::string::npos) {
|
||||
if (!this->result_.empty())
|
||||
this->clear();
|
||||
return;
|
||||
}
|
||||
if (this->end_keys_.find(key) != std::string::npos) {
|
||||
if ((this->min_length_ == 0) || (this->result_.size() >= this->min_length_)) {
|
||||
this->result_trigger_->trigger(this->result_, this->start_key_, key);
|
||||
this->clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!this->allowed_keys_.empty() && (this->allowed_keys_.find(key) == std::string::npos))
|
||||
return;
|
||||
if ((this->max_length_ == 0) || (this->result_.size() < this->max_length_))
|
||||
this->result_.push_back(key);
|
||||
if ((this->max_length_ > 0) && (this->result_.size() == this->max_length_) && (!this->end_key_required_)) {
|
||||
this->result_trigger_->trigger(this->result_, this->start_key_, 0);
|
||||
this->clear(false);
|
||||
}
|
||||
this->progress_trigger_->trigger(this->result_, this->start_key_);
|
||||
}
|
||||
|
||||
} // namespace key_collector
|
||||
} // namespace esphome
|
52
esphome/components/key_collector/key_collector.h
Normal file
52
esphome/components/key_collector/key_collector.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include "esphome/components/key_provider/key_provider.h"
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace key_collector {
|
||||
|
||||
class KeyCollector : public Component {
|
||||
public:
|
||||
KeyCollector();
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
void set_provider(key_provider::KeyProvider *provider);
|
||||
void set_min_length(int min_length) { this->min_length_ = min_length; };
|
||||
void set_max_length(int max_length) { this->max_length_ = max_length; };
|
||||
void set_start_keys(std::string start_keys) { this->start_keys_ = std::move(start_keys); };
|
||||
void set_end_keys(std::string end_keys) { this->end_keys_ = std::move(end_keys); };
|
||||
void set_end_key_required(bool end_key_required) { this->end_key_required_ = end_key_required; };
|
||||
void set_back_keys(std::string back_keys) { this->back_keys_ = std::move(back_keys); };
|
||||
void set_clear_keys(std::string clear_keys) { this->clear_keys_ = std::move(clear_keys); };
|
||||
void set_allowed_keys(std::string allowed_keys) { this->allowed_keys_ = std::move(allowed_keys); };
|
||||
Trigger<std::string, uint8_t> *get_progress_trigger() const { return this->progress_trigger_; };
|
||||
Trigger<std::string, uint8_t, uint8_t> *get_result_trigger() const { return this->result_trigger_; };
|
||||
Trigger<std::string, uint8_t> *get_timeout_trigger() const { return this->timeout_trigger_; };
|
||||
void set_timeout(int timeout) { this->timeout_ = timeout; };
|
||||
|
||||
void clear(bool progress_update = true);
|
||||
|
||||
protected:
|
||||
void key_pressed_(uint8_t key);
|
||||
|
||||
int min_length_{0};
|
||||
int max_length_{0};
|
||||
std::string start_keys_;
|
||||
std::string end_keys_;
|
||||
bool end_key_required_{false};
|
||||
std::string back_keys_;
|
||||
std::string clear_keys_;
|
||||
std::string allowed_keys_;
|
||||
std::string result_;
|
||||
uint8_t start_key_{0};
|
||||
Trigger<std::string, uint8_t> *progress_trigger_;
|
||||
Trigger<std::string, uint8_t, uint8_t> *result_trigger_;
|
||||
Trigger<std::string, uint8_t> *timeout_trigger_;
|
||||
uint32_t last_key_time_;
|
||||
uint32_t timeout_{0};
|
||||
};
|
||||
|
||||
} // namespace key_collector
|
||||
} // namespace esphome
|
|
@ -465,6 +465,7 @@ CONF_ON_TAG = "on_tag"
|
|||
CONF_ON_TAG_REMOVED = "on_tag_removed"
|
||||
CONF_ON_TIME = "on_time"
|
||||
CONF_ON_TIME_SYNC = "on_time_sync"
|
||||
CONF_ON_TIMEOUT = "on_timeout"
|
||||
CONF_ON_TOUCH = "on_touch"
|
||||
CONF_ON_TURN_OFF = "on_turn_off"
|
||||
CONF_ON_TURN_ON = "on_turn_on"
|
||||
|
|
|
@ -168,14 +168,22 @@ binary_sensor:
|
|||
sn74hc165: sn74hc165_hub
|
||||
number: 0
|
||||
|
||||
|
||||
|
||||
- platform: ezo_pmp
|
||||
pump_state:
|
||||
name: "Pump State"
|
||||
is_paused:
|
||||
name: "Is Paused"
|
||||
|
||||
- platform: matrix_keypad
|
||||
keypad_id: keypad
|
||||
id: key4
|
||||
row: 1
|
||||
col: 1
|
||||
- platform: matrix_keypad
|
||||
id: key1
|
||||
key: 1
|
||||
|
||||
|
||||
tlc5947:
|
||||
data_pin: GPIO12
|
||||
clock_pin: GPIO14
|
||||
|
@ -558,3 +566,21 @@ sn74hc165:
|
|||
load_pin: GPIO27
|
||||
clock_inhibit_pin: GPIO26
|
||||
sr_count: 4
|
||||
|
||||
|
||||
matrix_keypad:
|
||||
id: keypad
|
||||
rows:
|
||||
- pin: 21
|
||||
- pin: 19
|
||||
columns:
|
||||
- pin: 17
|
||||
- pin: 16
|
||||
keys: "1234"
|
||||
|
||||
key_collector:
|
||||
- id: reader
|
||||
source_id: keypad
|
||||
min_length: 4
|
||||
max_length: 4
|
||||
|
||||
|
|
Loading…
Reference in a new issue