Add Grow Fingerprint Reader (#1356)

This commit is contained in:
Barry Loong 2021-04-29 06:08:27 +08:00 committed by GitHub
parent cc6d1e85cc
commit 4d7c1ae143
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 1176 additions and 0 deletions

View file

@ -36,6 +36,7 @@ esphome/components/ds1307/* @badbadc0ffee
esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb
esphome/components/fastled_base/* @OttoWinter
esphome/components/fingerprint_grow/* @OnFreund @loongyh
esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle

View file

@ -0,0 +1,293 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome import pins
from esphome.components import uart
from esphome.const import (
CONF_COLOR,
CONF_COUNT,
CONF_FINGER_ID,
CONF_ID,
CONF_NEW_PASSWORD,
CONF_NUM_SCANS,
CONF_ON_ENROLLMENT_DONE,
CONF_ON_ENROLLMENT_FAILED,
CONF_ON_ENROLLMENT_SCAN,
CONF_ON_FINGER_SCAN_MATCHED,
CONF_ON_FINGER_SCAN_UNMATCHED,
CONF_PASSWORD,
CONF_SENSING_PIN,
CONF_SPEED,
CONF_STATE,
CONF_TRIGGER_ID,
)
CODEOWNERS = ["@OnFreund", "@loongyh"]
DEPENDENCIES = ["uart"]
AUTO_LOAD = ["binary_sensor", "sensor"]
MULTI_CONF = True
CONF_FINGERPRINT_GROW_ID = "fingerprint_grow_id"
fingerprint_grow_ns = cg.esphome_ns.namespace("fingerprint_grow")
FingerprintGrowComponent = fingerprint_grow_ns.class_(
"FingerprintGrowComponent", cg.PollingComponent, uart.UARTDevice
)
FingerScanMatchedTrigger = fingerprint_grow_ns.class_(
"FingerScanMatchedTrigger", automation.Trigger.template(cg.uint16, cg.uint16)
)
FingerScanUnmatchedTrigger = fingerprint_grow_ns.class_(
"FingerScanUnmatchedTrigger", automation.Trigger.template()
)
EnrollmentScanTrigger = fingerprint_grow_ns.class_(
"EnrollmentScanTrigger", automation.Trigger.template(cg.uint8, cg.uint16)
)
EnrollmentDoneTrigger = fingerprint_grow_ns.class_(
"EnrollmentDoneTrigger", automation.Trigger.template(cg.uint16)
)
EnrollmentFailedTrigger = fingerprint_grow_ns.class_(
"EnrollmentFailedTrigger", automation.Trigger.template(cg.uint16)
)
EnrollmentAction = fingerprint_grow_ns.class_("EnrollmentAction", automation.Action)
CancelEnrollmentAction = fingerprint_grow_ns.class_(
"CancelEnrollmentAction", automation.Action
)
DeleteAction = fingerprint_grow_ns.class_("DeleteAction", automation.Action)
DeleteAllAction = fingerprint_grow_ns.class_("DeleteAllAction", automation.Action)
LEDControlAction = fingerprint_grow_ns.class_("LEDControlAction", automation.Action)
AuraLEDControlAction = fingerprint_grow_ns.class_(
"AuraLEDControlAction", automation.Action
)
AuraLEDState = fingerprint_grow_ns.enum("GrowAuraLEDState", True)
AURA_LED_STATES = {
"BREATHING": AuraLEDState.BREATHING,
"FLASHING": AuraLEDState.FLASHING,
"ALWAYS_ON": AuraLEDState.ALWAYS_ON,
"ALWAYS_OFF": AuraLEDState.ALWAYS_OFF,
"GRADUAL_ON": AuraLEDState.GRADUAL_ON,
"GRADUAL_OFF": AuraLEDState.GRADUAL_OFF,
}
validate_aura_led_states = cv.enum(AURA_LED_STATES, upper=True)
AuraLEDColor = fingerprint_grow_ns.enum("GrowAuraLEDColor", True)
AURA_LED_COLORS = {
"RED": AuraLEDColor.RED,
"BLUE": AuraLEDColor.BLUE,
"PURPLE": AuraLEDColor.PURPLE,
}
validate_aura_led_colors = cv.enum(AURA_LED_COLORS, upper=True)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(FingerprintGrowComponent),
cv.Optional(CONF_SENSING_PIN): pins.gpio_input_pin_schema,
cv.Optional(CONF_PASSWORD): cv.uint32_t,
cv.Optional(CONF_NEW_PASSWORD): cv.uint32_t,
cv.Optional(CONF_ON_FINGER_SCAN_MATCHED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
FingerScanMatchedTrigger
),
}
),
cv.Optional(CONF_ON_FINGER_SCAN_UNMATCHED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
FingerScanUnmatchedTrigger
),
}
),
cv.Optional(CONF_ON_ENROLLMENT_SCAN): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
EnrollmentScanTrigger
),
}
),
cv.Optional(CONF_ON_ENROLLMENT_DONE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
EnrollmentDoneTrigger
),
}
),
cv.Optional(CONF_ON_ENROLLMENT_FAILED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
EnrollmentFailedTrigger
),
}
),
}
)
.extend(cv.polling_component_schema("500ms"))
.extend(uart.UART_DEVICE_SCHEMA)
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
if CONF_PASSWORD in config:
password = config[CONF_PASSWORD]
cg.add(var.set_password(password))
yield uart.register_uart_device(var, config)
if CONF_NEW_PASSWORD in config:
new_password = config[CONF_NEW_PASSWORD]
cg.add(var.set_new_password(new_password))
if CONF_SENSING_PIN in config:
sensing_pin = yield cg.gpio_pin_expression(config[CONF_SENSING_PIN])
cg.add(var.set_sensing_pin(sensing_pin))
for conf in config.get(CONF_ON_FINGER_SCAN_MATCHED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(
trigger, [(cg.uint16, "finger_id"), (cg.uint16, "confidence")], conf
)
for conf in config.get(CONF_ON_FINGER_SCAN_UNMATCHED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_ENROLLMENT_SCAN, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(
trigger, [(cg.uint8, "scan_num"), (cg.uint16, "finger_id")], conf
)
for conf in config.get(CONF_ON_ENROLLMENT_DONE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [(cg.uint16, "finger_id")], conf)
for conf in config.get(CONF_ON_ENROLLMENT_FAILED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [(cg.uint16, "finger_id")], conf)
@automation.register_action(
"fingerprint_grow.enroll",
EnrollmentAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
cv.Required(CONF_FINGER_ID): cv.templatable(cv.uint16_t),
cv.Optional(CONF_NUM_SCANS): cv.templatable(cv.uint8_t),
},
key=CONF_FINGER_ID,
),
)
def fingerprint_grow_enroll_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
template_ = yield cg.templatable(config[CONF_FINGER_ID], args, cg.uint16)
cg.add(var.set_finger_id(template_))
if CONF_NUM_SCANS in config:
template_ = yield cg.templatable(config[CONF_NUM_SCANS], args, cg.uint8)
cg.add(var.set_num_scans(template_))
yield var
@automation.register_action(
"fingerprint_grow.cancel_enroll",
CancelEnrollmentAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
}
),
)
def fingerprint_grow_cancel_enroll_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action(
"fingerprint_grow.delete",
DeleteAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
cv.Required(CONF_FINGER_ID): cv.templatable(cv.uint16_t),
},
key=CONF_FINGER_ID,
),
)
def fingerprint_grow_delete_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
template_ = yield cg.templatable(config[CONF_FINGER_ID], args, cg.uint16)
cg.add(var.set_finger_id(template_))
yield var
@automation.register_action(
"fingerprint_grow.delete_all",
DeleteAllAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
}
),
)
def fingerprint_grow_delete_all_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
FINGERPRINT_GROW_LED_CONTROL_ACTION_SCHEMA = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
cv.Required(CONF_STATE): cv.templatable(cv.boolean),
},
key=CONF_STATE,
)
@automation.register_action(
"fingerprint_grow.led_control",
LEDControlAction,
FINGERPRINT_GROW_LED_CONTROL_ACTION_SCHEMA,
)
def fingerprint_grow_led_control_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
template_ = yield cg.templatable(config[CONF_STATE], args, cg.bool_)
cg.add(var.set_state(template_))
yield var
@automation.register_action(
"fingerprint_grow.aura_led_control",
AuraLEDControlAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
cv.Required(CONF_STATE): cv.templatable(validate_aura_led_states),
cv.Required(CONF_SPEED): cv.templatable(cv.uint8_t),
cv.Required(CONF_COLOR): cv.templatable(validate_aura_led_colors),
cv.Required(CONF_COUNT): cv.templatable(cv.uint8_t),
}
),
)
def fingerprint_grow_aura_led_control_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
for key in [CONF_STATE, CONF_SPEED, CONF_COLOR, CONF_COUNT]:
template_ = yield cg.templatable(config[key], args, cg.uint8)
cg.add(getattr(var, f"set_{key}")(template_))
yield var

View file

@ -0,0 +1,20 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_ICON, ICON_KEY_PLUS
from . import CONF_FINGERPRINT_GROW_ID, FingerprintGrowComponent
DEPENDENCIES = ["fingerprint_grow"]
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(CONF_FINGERPRINT_GROW_ID): cv.use_id(FingerprintGrowComponent),
cv.Optional(CONF_ICON, default=ICON_KEY_PLUS): cv.icon,
}
)
def to_code(config):
hub = yield cg.get_variable(config[CONF_FINGERPRINT_GROW_ID])
var = yield binary_sensor.new_binary_sensor(config)
cg.add(hub.set_enrolling_binary_sensor(var))

View file

@ -0,0 +1,434 @@
#include "fingerprint_grow.h"
#include "esphome/core/log.h"
namespace esphome {
namespace fingerprint_grow {
static const char* TAG = "fingerprint_grow";
// Based on Adafruit's library: https://github.com/adafruit/Adafruit-Fingerprint-Sensor-Library
void FingerprintGrowComponent::update() {
if (this->enrollment_image_ > this->enrollment_buffers_) {
this->finish_enrollment(this->save_fingerprint_());
return;
}
if (this->sensing_pin_ != nullptr) {
if (this->sensing_pin_->digital_read() == HIGH) {
ESP_LOGV(TAG, "No touch sensing");
this->waiting_removal_ = false;
return;
}
}
if (this->waiting_removal_) {
if (this->scan_image_(1) == NO_FINGER) {
ESP_LOGD(TAG, "Finger removed");
this->waiting_removal_ = false;
}
return;
}
if (this->enrollment_image_ == 0) {
this->scan_and_match_();
return;
}
uint8_t result = this->scan_image_(this->enrollment_image_);
if (result == NO_FINGER) {
return;
}
this->waiting_removal_ = true;
if (result != OK) {
this->finish_enrollment(result);
return;
}
this->enrollment_scan_callback_.call(this->enrollment_image_, this->enrollment_slot_);
++this->enrollment_image_;
}
void FingerprintGrowComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader...");
if (this->check_password_()) {
if (this->new_password_ != nullptr) {
if (this->set_password_())
return;
} else {
if (this->get_parameters_())
return;
}
}
this->mark_failed();
}
void FingerprintGrowComponent::enroll_fingerprint(uint16_t finger_id, uint8_t num_buffers) {
ESP_LOGI(TAG, "Starting enrollment in slot %d", finger_id);
if (this->enrolling_binary_sensor_ != nullptr) {
this->enrolling_binary_sensor_->publish_state(true);
}
this->enrollment_slot_ = finger_id;
this->enrollment_buffers_ = num_buffers;
this->enrollment_image_ = 1;
}
void FingerprintGrowComponent::finish_enrollment(uint8_t result) {
if (result == OK) {
this->enrollment_done_callback_.call(this->enrollment_slot_);
} else {
this->enrollment_failed_callback_.call(this->enrollment_slot_);
}
this->enrollment_image_ = 0;
this->enrollment_slot_ = 0;
if (this->enrolling_binary_sensor_ != nullptr) {
this->enrolling_binary_sensor_->publish_state(false);
}
ESP_LOGI(TAG, "Finished enrollment");
}
void FingerprintGrowComponent::scan_and_match_() {
if (this->sensing_pin_ != nullptr) {
ESP_LOGD(TAG, "Scan and match");
} else {
ESP_LOGV(TAG, "Scan and match");
}
if (this->scan_image_(1) == OK) {
this->waiting_removal_ = true;
this->data_ = {SEARCH, 0x01, 0x00, 0x00, (uint8_t)(this->capacity_ >> 8), (uint8_t)(this->capacity_ & 0xFF)};
switch (this->send_command_()) {
case OK: {
ESP_LOGD(TAG, "Fingerprint matched");
uint16_t finger_id = ((uint16_t) this->data_[1] << 8) | this->data_[2];
uint16_t confidence = ((uint16_t) this->data_[3] << 8) | this->data_[4];
if (this->last_finger_id_sensor_ != nullptr) {
this->last_finger_id_sensor_->publish_state(finger_id);
}
if (this->last_confidence_sensor_ != nullptr) {
this->last_confidence_sensor_->publish_state(confidence);
}
this->finger_scan_matched_callback_.call(finger_id, confidence);
break;
}
case NOT_FOUND:
ESP_LOGD(TAG, "Fingerprint not matched to any saved slots");
this->finger_scan_unmatched_callback_.call();
break;
}
}
}
uint8_t FingerprintGrowComponent::scan_image_(uint8_t buffer) {
if (this->sensing_pin_ != nullptr) {
ESP_LOGD(TAG, "Getting image %d", buffer);
} else {
ESP_LOGV(TAG, "Getting image %d", buffer);
}
this->data_ = {GET_IMAGE};
switch (this->send_command_()) {
case OK:
break;
case NO_FINGER:
if (this->sensing_pin_ != nullptr) {
ESP_LOGD(TAG, "No finger");
} else {
ESP_LOGV(TAG, "No finger");
}
return this->data_[0];
case IMAGE_FAIL:
ESP_LOGE(TAG, "Imaging error");
default:
return this->data_[0];
}
ESP_LOGD(TAG, "Processing image %d", buffer);
this->data_ = {IMAGE_2_TZ, buffer};
switch (this->send_command_()) {
case OK:
ESP_LOGI(TAG, "Processed image %d", buffer);
break;
case IMAGE_MESS:
ESP_LOGE(TAG, "Image too messy");
break;
case FEATURE_FAIL:
case INVALID_IMAGE:
ESP_LOGE(TAG, "Could not find fingerprint features");
break;
}
return this->data_[0];
}
uint8_t FingerprintGrowComponent::save_fingerprint_() {
ESP_LOGI(TAG, "Creating model");
this->data_ = {REG_MODEL};
switch (this->send_command_()) {
case OK:
break;
case ENROLL_MISMATCH:
ESP_LOGE(TAG, "Scans do not match");
default:
return this->data_[0];
}
ESP_LOGI(TAG, "Storing model");
this->data_ = {STORE, 0x01, (uint8_t)(this->enrollment_slot_ >> 8), (uint8_t)(this->enrollment_slot_ & 0xFF)};
switch (this->send_command_()) {
case OK:
ESP_LOGI(TAG, "Stored model");
break;
case BAD_LOCATION:
ESP_LOGE(TAG, "Invalid slot");
break;
case FLASH_ERR:
ESP_LOGE(TAG, "Error writing to flash");
break;
}
return this->data_[0];
}
bool FingerprintGrowComponent::check_password_() {
ESP_LOGD(TAG, "Checking password");
this->data_ = {VERIFY_PASSWORD, (uint8_t)(this->password_ >> 24), (uint8_t)(this->password_ >> 16),
(uint8_t)(this->password_ >> 8), (uint8_t)(this->password_ & 0xFF)};
switch (this->send_command_()) {
case OK:
ESP_LOGD(TAG, "Password verified");
return true;
case PASSWORD_FAIL:
ESP_LOGE(TAG, "Wrong password");
break;
}
return false;
}
bool FingerprintGrowComponent::set_password_() {
ESP_LOGI(TAG, "Setting new password: %d", *this->new_password_);
this->data_ = {SET_PASSWORD, (uint8_t)(*this->new_password_ >> 24), (uint8_t)(*this->new_password_ >> 16),
(uint8_t)(*this->new_password_ >> 8), (uint8_t)(*this->new_password_ & 0xFF)};
if (this->send_command_() == OK) {
ESP_LOGI(TAG, "New password successfully set");
ESP_LOGI(TAG, "Define the new password in your configuration and reflash now");
ESP_LOGW(TAG, "!!!Forgetting the password will render your device unusable!!!");
return true;
}
return false;
}
bool FingerprintGrowComponent::get_parameters_() {
ESP_LOGD(TAG, "Getting parameters");
this->data_ = {READ_SYS_PARAM};
if (this->send_command_() == OK) {
ESP_LOGD(TAG, "Got parameters");
if (this->status_sensor_ != nullptr) {
this->status_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
}
this->capacity_ = ((uint16_t) this->data_[5] << 8) | this->data_[6];
if (this->capacity_sensor_ != nullptr) {
this->capacity_sensor_->publish_state(this->capacity_);
}
if (this->security_level_sensor_ != nullptr) {
this->security_level_sensor_->publish_state(((uint16_t) this->data_[7] << 8) | this->data_[8]);
}
if (this->enrolling_binary_sensor_ != nullptr) {
this->enrolling_binary_sensor_->publish_state(false);
}
this->get_fingerprint_count_();
return true;
}
return false;
}
void FingerprintGrowComponent::get_fingerprint_count_() {
ESP_LOGD(TAG, "Getting fingerprint count");
this->data_ = {TEMPLATE_COUNT};
if (this->send_command_() == OK) {
ESP_LOGD(TAG, "Got fingerprint count");
if (this->fingerprint_count_sensor_ != nullptr)
this->fingerprint_count_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
}
}
void FingerprintGrowComponent::delete_fingerprint(uint16_t finger_id) {
ESP_LOGI(TAG, "Deleting fingerprint in slot %d", finger_id);
this->data_ = {DELETE, (uint8_t)(finger_id >> 8), (uint8_t)(finger_id & 0xFF), 0x00, 0x01};
switch (this->send_command_()) {
case OK:
ESP_LOGI(TAG, "Deleted fingerprint");
this->get_fingerprint_count_();
break;
case DELETE_FAIL:
ESP_LOGE(TAG, "Reader failed to delete fingerprint");
break;
}
}
void FingerprintGrowComponent::delete_all_fingerprints() {
ESP_LOGI(TAG, "Deleting all stored fingerprints");
this->data_ = {EMPTY};
switch (this->send_command_()) {
case OK:
ESP_LOGI(TAG, "Deleted all fingerprints");
this->get_fingerprint_count_();
break;
case DB_CLEAR_FAIL:
ESP_LOGE(TAG, "Reader failed to clear fingerprint library");
break;
}
}
void FingerprintGrowComponent::led_control(bool state) {
ESP_LOGD(TAG, "Setting LED");
if (state)
this->data_ = {LED_ON};
else
this->data_ = {LED_OFF};
switch (this->send_command_()) {
case OK:
ESP_LOGD(TAG, "LED set");
break;
case PACKET_RCV_ERR:
case TIMEOUT:
break;
default:
ESP_LOGE(TAG, "Try aura_led_control instead");
break;
}
}
void FingerprintGrowComponent::aura_led_control(uint8_t state, uint8_t speed, uint8_t color, uint8_t count) {
const uint32_t now = millis();
const uint32_t elapsed = now - this->last_aura_led_control_;
if (elapsed < this->last_aura_led_duration_) {
delay(this->last_aura_led_duration_ - elapsed);
}
ESP_LOGD(TAG, "Setting Aura LED");
this->data_ = {AURA_CONFIG, state, speed, color, count};
switch (this->send_command_()) {
case OK:
ESP_LOGD(TAG, "Aura LED set");
this->last_aura_led_control_ = millis();
this->last_aura_led_duration_ = 10 * speed * count;
break;
case PACKET_RCV_ERR:
case TIMEOUT:
break;
default:
ESP_LOGE(TAG, "Try led_control instead");
break;
}
}
uint8_t FingerprintGrowComponent::send_command_() {
this->write((uint8_t)(START_CODE >> 8));
this->write((uint8_t)(START_CODE & 0xFF));
this->write(this->address_[0]);
this->write(this->address_[1]);
this->write(this->address_[2]);
this->write(this->address_[3]);
this->write(COMMAND);
uint16_t wire_length = this->data_.size() + 2;
this->write((uint8_t)(wire_length >> 8));
this->write((uint8_t)(wire_length & 0xFF));
uint16_t sum = ((wire_length) >> 8) + ((wire_length) &0xFF) + COMMAND;
for (auto data : this->data_) {
this->write(data);
sum += data;
}
this->write((uint8_t)(sum >> 8));
this->write((uint8_t)(sum & 0xFF));
this->data_.clear();
uint8_t byte;
uint16_t idx = 0, length = 0;
for (uint16_t timer = 0; timer < 1000; timer++) {
if (this->available() == 0) {
delay(1);
continue;
}
byte = this->read();
switch (idx) {
case 0:
if (byte != (uint8_t)(START_CODE >> 8))
continue;
break;
case 1:
if (byte != (uint8_t)(START_CODE & 0xFF)) {
idx = 0;
continue;
}
break;
case 2:
case 3:
case 4:
case 5:
if (byte != this->address_[idx - 2]) {
idx = 0;
continue;
}
break;
case 6:
if (byte != ACK) {
idx = 0;
continue;
}
break;
case 7:
length = (uint16_t) byte << 8;
break;
case 8:
length |= byte;
break;
default:
this->data_.push_back(byte);
if ((idx - 8) == length) {
switch (this->data_[0]) {
case OK:
case NO_FINGER:
case IMAGE_FAIL:
case IMAGE_MESS:
case FEATURE_FAIL:
case NO_MATCH:
case NOT_FOUND:
case ENROLL_MISMATCH:
case BAD_LOCATION:
case DELETE_FAIL:
case DB_CLEAR_FAIL:
case PASSWORD_FAIL:
case INVALID_IMAGE:
case FLASH_ERR:
break;
case PACKET_RCV_ERR:
ESP_LOGE(TAG, "Reader failed to process request");
break;
default:
ESP_LOGE(TAG, "Unknown response received from reader: %d", this->data_[0]);
break;
}
return this->data_[0];
}
break;
}
idx++;
}
ESP_LOGE(TAG, "No response received from reader");
this->data_[0] = TIMEOUT;
return TIMEOUT;
}
void FingerprintGrowComponent::dump_config() {
ESP_LOGCONFIG(TAG, "GROW_FINGERPRINT_READER:");
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_);
LOG_SENSOR(" ", "Status", this->status_sensor_);
LOG_SENSOR(" ", "Capacity", this->capacity_sensor_);
LOG_SENSOR(" ", "Security Level", this->security_level_sensor_);
LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_);
LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_);
}
} // namespace fingerprint_grow
} // namespace esphome

View file

@ -0,0 +1,276 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace fingerprint_grow {
static const uint16_t START_CODE = 0xEF01;
enum GrowPacketType {
COMMAND = 0x01,
DATA = 0x02,
ACK = 0x07,
END_DATA = 0x08,
};
enum GrowCommand {
GET_IMAGE = 0x01,
IMAGE_2_TZ = 0x02,
SEARCH = 0x04,
REG_MODEL = 0x05,
STORE = 0x06,
LOAD = 0x07,
UPLOAD = 0x08,
DELETE = 0x0C,
EMPTY = 0x0D,
READ_SYS_PARAM = 0x0F,
SET_PASSWORD = 0x12,
VERIFY_PASSWORD = 0x13,
HI_SPEED_SEARCH = 0x1B,
TEMPLATE_COUNT = 0x1D,
AURA_CONFIG = 0x35,
LED_ON = 0x50,
LED_OFF = 0x51,
};
enum GrowResponse {
OK = 0x00,
PACKET_RCV_ERR = 0x01,
NO_FINGER = 0x02,
IMAGE_FAIL = 0x03,
IMAGE_MESS = 0x06,
FEATURE_FAIL = 0x07,
NO_MATCH = 0x08,
NOT_FOUND = 0x09,
ENROLL_MISMATCH = 0x0A,
BAD_LOCATION = 0x0B,
DB_RANGE_FAIL = 0x0C,
UPLOAD_FEATURE_FAIL = 0x0D,
PACKET_RESPONSE_FAIL = 0x0E,
UPLOAD_FAIL = 0x0F,
DELETE_FAIL = 0x10,
DB_CLEAR_FAIL = 0x11,
PASSWORD_FAIL = 0x13,
INVALID_IMAGE = 0x15,
FLASH_ERR = 0x18,
INVALID_REG = 0x1A,
BAD_PACKET = 0xFE,
TIMEOUT = 0xFF,
};
enum GrowAuraLEDState {
BREATHING = 0x01,
FLASHING = 0x02,
ALWAYS_ON = 0x03,
ALWAYS_OFF = 0x04,
GRADUAL_ON = 0x05,
GRADUAL_OFF = 0x06,
};
enum GrowAuraLEDColor {
RED = 0x01,
BLUE = 0x02,
PURPLE = 0x03,
};
class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevice {
public:
void update() override;
void setup() override;
void dump_config() override;
void set_address(uint32_t address) {
this->address_[0] = (uint8_t)(address >> 24);
this->address_[1] = (uint8_t)(address >> 16);
this->address_[2] = (uint8_t)(address >> 8);
this->address_[3] = (uint8_t)(address & 0xFF);
}
void set_sensing_pin(GPIOPin *sensing_pin) { this->sensing_pin_ = sensing_pin; }
void set_password(uint32_t password) { this->password_ = password; }
void set_new_password(uint32_t new_password) { this->new_password_ = &new_password; }
void set_fingerprint_count_sensor(sensor::Sensor *fingerprint_count_sensor) {
this->fingerprint_count_sensor_ = fingerprint_count_sensor;
}
void set_status_sensor(sensor::Sensor *status_sensor) { this->status_sensor_ = status_sensor; }
void set_capacity_sensor(sensor::Sensor *capacity_sensor) { this->capacity_sensor_ = capacity_sensor; }
void set_security_level_sensor(sensor::Sensor *security_level_sensor) {
this->security_level_sensor_ = security_level_sensor;
}
void set_last_finger_id_sensor(sensor::Sensor *last_finger_id_sensor) {
this->last_finger_id_sensor_ = last_finger_id_sensor;
}
void set_last_confidence_sensor(sensor::Sensor *last_confidence_sensor) {
this->last_confidence_sensor_ = last_confidence_sensor;
}
void set_enrolling_binary_sensor(binary_sensor::BinarySensor *enrolling_binary_sensor) {
this->enrolling_binary_sensor_ = enrolling_binary_sensor;
}
void add_on_finger_scan_matched_callback(std::function<void(uint16_t, uint16_t)> callback) {
this->finger_scan_matched_callback_.add(std::move(callback));
}
void add_on_finger_scan_unmatched_callback(std::function<void()> callback) {
this->finger_scan_unmatched_callback_.add(std::move(callback));
}
void add_on_enrollment_scan_callback(std::function<void(uint8_t, uint16_t)> callback) {
this->enrollment_scan_callback_.add(std::move(callback));
}
void add_on_enrollment_done_callback(std::function<void(uint16_t)> callback) {
this->enrollment_done_callback_.add(std::move(callback));
}
void add_on_enrollment_failed_callback(std::function<void(uint16_t)> callback) {
this->enrollment_failed_callback_.add(std::move(callback));
}
void enroll_fingerprint(uint16_t finger_id, uint8_t num_buffers);
void finish_enrollment(uint8_t result);
void delete_fingerprint(uint16_t finger_id);
void delete_all_fingerprints();
void led_control(bool state);
void aura_led_control(uint8_t state, uint8_t speed, uint8_t color, uint8_t count);
protected:
void scan_and_match_();
uint8_t scan_image_(uint8_t buffer);
uint8_t save_fingerprint_();
bool check_password_();
bool set_password_();
bool get_parameters_();
void get_fingerprint_count_();
uint8_t send_command_();
std::vector<uint8_t> data_ = {};
uint8_t address_[4] = {0xFF, 0xFF, 0xFF, 0xFF};
uint16_t capacity_ = 64;
uint32_t password_ = 0x0;
uint32_t *new_password_{nullptr};
GPIOPin *sensing_pin_{nullptr};
uint8_t enrollment_image_ = 0;
uint16_t enrollment_slot_ = 0;
uint8_t enrollment_buffers_ = 5;
bool waiting_removal_ = false;
uint32_t last_aura_led_control_ = 0;
uint16_t last_aura_led_duration_ = 0;
sensor::Sensor *fingerprint_count_sensor_{nullptr};
sensor::Sensor *status_sensor_{nullptr};
sensor::Sensor *capacity_sensor_{nullptr};
sensor::Sensor *security_level_sensor_{nullptr};
sensor::Sensor *last_finger_id_sensor_{nullptr};
sensor::Sensor *last_confidence_sensor_{nullptr};
binary_sensor::BinarySensor *enrolling_binary_sensor_{nullptr};
CallbackManager<void(uint16_t, uint16_t)> finger_scan_matched_callback_;
CallbackManager<void()> finger_scan_unmatched_callback_;
CallbackManager<void(uint8_t, uint16_t)> enrollment_scan_callback_;
CallbackManager<void(uint16_t)> enrollment_done_callback_;
CallbackManager<void(uint16_t)> enrollment_failed_callback_;
};
class FingerScanMatchedTrigger : public Trigger<uint16_t, uint16_t> {
public:
explicit FingerScanMatchedTrigger(FingerprintGrowComponent *parent) {
parent->add_on_finger_scan_matched_callback(
[this](uint16_t finger_id, uint16_t confidence) { this->trigger(finger_id, confidence); });
}
};
class FingerScanUnmatchedTrigger : public Trigger<> {
public:
explicit FingerScanUnmatchedTrigger(FingerprintGrowComponent *parent) {
parent->add_on_finger_scan_unmatched_callback([this]() { this->trigger(); });
}
};
class EnrollmentScanTrigger : public Trigger<uint8_t, uint16_t> {
public:
explicit EnrollmentScanTrigger(FingerprintGrowComponent *parent) {
parent->add_on_enrollment_scan_callback(
[this](uint8_t scan_num, uint16_t finger_id) { this->trigger(scan_num, finger_id); });
}
};
class EnrollmentDoneTrigger : public Trigger<uint16_t> {
public:
explicit EnrollmentDoneTrigger(FingerprintGrowComponent *parent) {
parent->add_on_enrollment_done_callback([this](uint16_t finger_id) { this->trigger(finger_id); });
}
};
class EnrollmentFailedTrigger : public Trigger<uint16_t> {
public:
explicit EnrollmentFailedTrigger(FingerprintGrowComponent *parent) {
parent->add_on_enrollment_failed_callback([this](uint16_t finger_id) { this->trigger(finger_id); });
}
};
template<typename... Ts> class EnrollmentAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
public:
TEMPLATABLE_VALUE(uint16_t, finger_id)
TEMPLATABLE_VALUE(uint8_t, num_scans)
void play(Ts... x) override {
auto finger_id = this->finger_id_.value(x...);
auto num_scans = this->num_scans_.value(x...);
if (num_scans) {
this->parent_->enroll_fingerprint(finger_id, num_scans);
} else {
this->parent_->enroll_fingerprint(finger_id, 2);
}
}
};
template<typename... Ts>
class CancelEnrollmentAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
public:
void play(Ts... x) override { this->parent_->finish_enrollment(1); }
};
template<typename... Ts> class DeleteAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
public:
TEMPLATABLE_VALUE(uint16_t, finger_id)
void play(Ts... x) override {
auto finger_id = this->finger_id_.value(x...);
this->parent_->delete_fingerprint(finger_id);
}
};
template<typename... Ts> class DeleteAllAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
public:
void play(Ts... x) override { this->parent_->delete_all_fingerprints(); }
};
template<typename... Ts> class LEDControlAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
public:
TEMPLATABLE_VALUE(bool, state)
void play(Ts... x) override {
auto state = this->state_.value(x...);
this->parent_->led_control(state);
}
};
template<typename... Ts> class AuraLEDControlAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
public:
TEMPLATABLE_VALUE(uint8_t, state)
TEMPLATABLE_VALUE(uint8_t, speed)
TEMPLATABLE_VALUE(uint8_t, color)
TEMPLATABLE_VALUE(uint8_t, count)
void play(Ts... x) override {
auto state = this->state_.value(x...);
auto speed = this->speed_.value(x...);
auto color = this->color_.value(x...);
auto count = this->count_.value(x...);
this->parent_->aura_led_control(state, speed, color, count);
}
};
} // namespace fingerprint_grow
} // namespace esphome

View file

@ -0,0 +1,64 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_CAPACITY,
CONF_FINGERPRINT_COUNT,
CONF_LAST_CONFIDENCE,
CONF_LAST_FINGER_ID,
CONF_SECURITY_LEVEL,
CONF_STATUS,
DEVICE_CLASS_EMPTY,
ICON_ACCOUNT,
ICON_ACCOUNT_CHECK,
ICON_DATABASE,
ICON_EMPTY,
ICON_FINGERPRINT,
ICON_SECURITY,
UNIT_EMPTY,
)
from . import CONF_FINGERPRINT_GROW_ID, FingerprintGrowComponent
DEPENDENCIES = ["fingerprint_grow"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_FINGERPRINT_GROW_ID): cv.use_id(FingerprintGrowComponent),
cv.Optional(CONF_FINGERPRINT_COUNT): sensor.sensor_schema(
UNIT_EMPTY, ICON_FINGERPRINT, 0, DEVICE_CLASS_EMPTY
),
cv.Optional(CONF_STATUS): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY
),
cv.Optional(CONF_CAPACITY): sensor.sensor_schema(
UNIT_EMPTY, ICON_DATABASE, 0, DEVICE_CLASS_EMPTY
),
cv.Optional(CONF_SECURITY_LEVEL): sensor.sensor_schema(
UNIT_EMPTY, ICON_SECURITY, 0, DEVICE_CLASS_EMPTY
),
cv.Optional(CONF_LAST_FINGER_ID): sensor.sensor_schema(
UNIT_EMPTY, ICON_ACCOUNT, 0, DEVICE_CLASS_EMPTY
),
cv.Optional(CONF_LAST_CONFIDENCE): sensor.sensor_schema(
UNIT_EMPTY, ICON_ACCOUNT_CHECK, 0, DEVICE_CLASS_EMPTY
),
}
)
def to_code(config):
hub = yield cg.get_variable(config[CONF_FINGERPRINT_GROW_ID])
for key in [
CONF_FINGERPRINT_COUNT,
CONF_STATUS,
CONF_CAPACITY,
CONF_SECURITY_LEVEL,
CONF_LAST_FINGER_ID,
CONF_LAST_CONFIDENCE,
]:
if key not in config:
continue
conf = config[key]
sens = yield sensor.new_sensor(conf)
cg.add(getattr(hub, f"set_{key}_sensor")(sens))

View file

@ -97,6 +97,7 @@ CONF_BUSY_PIN = "busy_pin"
CONF_CALIBRATE_LINEAR = "calibrate_linear"
CONF_CALIBRATION = "calibration"
CONF_CAPACITANCE = "capacitance"
CONF_CAPACITY = "capacity"
CONF_CARRIER_DUTY_PERCENT = "carrier_duty_percent"
CONF_CARRIER_FREQUENCY = "carrier_frequency"
CONF_CERTIFICATE = "certificate"
@ -115,6 +116,7 @@ CONF_CO2 = "co2"
CONF_CODE = "code"
CONF_COLD_WHITE = "cold_white"
CONF_COLD_WHITE_COLOR_TEMPERATURE = "cold_white_color_temperature"
CONF_COLOR = "color"
CONF_COLOR_CORRECT = "color_correct"
CONF_COLOR_TEMPERATURE = "color_temperature"
CONF_COLORS = "colors"
@ -130,6 +132,7 @@ CONF_CONDUCTIVITY = "conductivity"
CONF_CONTRAST = "contrast"
CONF_COOL_ACTION = "cool_action"
CONF_COOL_MODE = "cool_mode"
CONF_COUNT = "count"
CONF_COUNT_MODE = "count_mode"
CONF_COURSE = "course"
CONF_CRON = "cron"
@ -208,6 +211,8 @@ CONF_FILE = "file"
CONF_FILTER = "filter"
CONF_FILTER_OUT = "filter_out"
CONF_FILTERS = "filters"
CONF_FINGER_ID = "finger_id"
CONF_FINGERPRINT_COUNT = "fingerprint_count"
CONF_FLASH_LENGTH = "flash_length"
CONF_FOR = "for"
CONF_FORCE_UPDATE = "force_update"
@ -275,6 +280,8 @@ CONF_KEEP_ON_TIME = "keep_on_time"
CONF_KEEPALIVE = "keepalive"
CONF_KEY = "key"
CONF_LAMBDA = "lambda"
CONF_LAST_CONFIDENCE = "last_confidence"
CONF_LAST_FINGER_ID = "last_finger_id"
CONF_LATITUDE = "latitude"
CONF_LENGTH = "length"
CONF_LEVEL = "level"
@ -335,11 +342,13 @@ CONF_NAME = "name"
CONF_NBITS = "nbits"
CONF_NEC = "nec"
CONF_NETWORKS = "networks"
CONF_NEW_PASSWORD = "new_password"
CONF_NOISE_LEVEL = "noise_level"
CONF_NUM_ATTEMPTS = "num_attempts"
CONF_NUM_CHANNELS = "num_channels"
CONF_NUM_CHIPS = "num_chips"
CONF_NUM_LEDS = "num_leds"
CONF_NUM_SCANS = "num_scans"
CONF_NUMBER = "number"
CONF_OFF_MODE = "off_mode"
CONF_OFFSET = "offset"
@ -350,6 +359,11 @@ CONF_ON_BLE_SERVICE_DATA_ADVERTISE = "on_ble_service_data_advertise"
CONF_ON_BOOT = "on_boot"
CONF_ON_CLICK = "on_click"
CONF_ON_DOUBLE_CLICK = "on_double_click"
CONF_ON_ENROLLMENT_DONE = "on_enrollment_done"
CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed"
CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan"
CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched"
CONF_ON_FINGER_SCAN_UNMATCHED = "on_finger_scan_unmatched"
CONF_ON_JSON_MESSAGE = "on_json_message"
CONF_ON_LOOP = "on_loop"
CONF_ON_MESSAGE = "on_message"
@ -472,10 +486,12 @@ CONF_SDA = "sda"
CONF_SDO_PIN = "sdo_pin"
CONF_SECOND = "second"
CONF_SECONDS = "seconds"
CONF_SECURITY_LEVEL = "security_level"
CONF_SEGMENTS = "segments"
CONF_SEL_PIN = "sel_pin"
CONF_SEND_EVERY = "send_every"
CONF_SEND_FIRST_AT = "send_first_at"
CONF_SENSING_PIN = "sensing_pin"
CONF_SENSOR = "sensor"
CONF_SENSOR_ID = "sensor_id"
CONF_SENSORS = "sensors"
@ -505,6 +521,7 @@ CONF_SSL_FINGERPRINTS = "ssl_fingerprints"
CONF_STATE = "state"
CONF_STATE_TOPIC = "state_topic"
CONF_STATIC_IP = "static_ip"
CONF_STATUS = "status"
CONF_STEP_MODE = "step_mode"
CONF_STEP_PIN = "step_pin"
CONF_STOP = "stop"
@ -599,6 +616,8 @@ ICON_ACCELERATION = "mdi:axis-arrow"
ICON_ACCELERATION_X = "mdi:axis-x-arrow"
ICON_ACCELERATION_Y = "mdi:axis-y-arrow"
ICON_ACCELERATION_Z = "mdi:axis-z-arrow"
ICON_ACCOUNT = "mdi:account"
ICON_ACCOUNT_CHECK = "mdi:account-check"
ICON_ARROW_EXPAND_VERTICAL = "mdi:arrow-expand-vertical"
ICON_BATTERY = "mdi:battery"
ICON_BRIEFCASE_DOWNLOAD = "mdi:briefcase-download"
@ -608,7 +627,9 @@ ICON_CHECK_CIRCLE_OUTLINE = "mdi:check-circle-outline"
ICON_CHEMICAL_WEAPON = "mdi:chemical-weapon"
ICON_COUNTER = "mdi:counter"
ICON_CURRENT_AC = "mdi:current-ac"
ICON_DATABASE = "mdi:database"
ICON_EMPTY = ""
ICON_FINGERPRINT = "mdi:fingerprint"
ICON_FLASH = "mdi:flash"
ICON_FLASK = "mdi:flask"
ICON_FLASK_OUTLINE = "mdi:flask-outline"
@ -616,6 +637,7 @@ ICON_FLOWER = "mdi:flower"
ICON_GAS_CYLINDER = "mdi:gas-cylinder"
ICON_GAUGE = "mdi:gauge"
ICON_GRAIN = "mdi:grain"
ICON_KEY_PLUS = "mdi:key-plus"
ICON_LIGHTBULB = "mdi:lightbulb"
ICON_MAGNET = "mdi:magnet"
ICON_MOLECULE_CO2 = "mdi:molecule-co2"
@ -632,6 +654,7 @@ ICON_RULER = "mdi:ruler"
ICON_SCALE = "mdi:scale"
ICON_SCALE_BATHROOM = "mdi:scale-bathroom"
ICON_SCREEN_ROTATION = "mdi:screen-rotation"
ICON_SECURITY = "mdi:security"
ICON_SIGN_DIRECTION = "mdi:sign-direction"
ICON_SIGNAL = "mdi:signal-distance-variant"
ICON_SIGNAL_DISTANCE_VARIANT = "mdi:signal"

View file

@ -177,6 +177,26 @@ api:
kp: 1.0
kd: 1.0
ki: 1.0
- service: fingerprint_grow_enroll
variables:
finger_id: int
num_scans: int
then:
- fingerprint_grow.enroll:
finger_id: !lambda 'return finger_id;'
num_scans: !lambda 'return num_scans;'
- service: fingerprint_grow_cancel_enroll
then:
- fingerprint_grow.cancel_enroll:
- service: fingerprint_grow_delete
variables:
finger_id: int
then:
- fingerprint_grow.delete:
finger_id: !lambda 'return finger_id;'
- service: fingerprint_grow_delete_all
then:
- fingerprint_grow.delete_all:
wifi:
ssid: 'MySSID'
@ -424,6 +444,19 @@ sensor:
id: ph_ezo
address: 99
unit_of_measurement: 'pH'
- platform: fingerprint_grow
fingerprint_count:
name: "Fingerprint Count"
status:
name: "Fingerprint Status"
capacity:
name: "Fingerprint Capacity"
security_level:
name: "Fingerprint Security Level"
last_finger_id:
name: "Fingerprint Last Finger ID"
last_confidence:
name: "Fingerprint Last Confidence"
time:
- platform: homeassistant
@ -486,6 +519,8 @@ binary_sensor:
- platform: ttp229_bsf
channel: 1
name: TTP229 BSF Test
- platform: fingerprint_grow
name: "Fingerprint Enrolling"
- platform: custom
lambda: |-
auto s = new CustomBinarySensor();
@ -919,3 +954,33 @@ display:
http_request:
useragent: esphome/device
timeout: 10s
fingerprint_grow:
sensing_pin: 4
password: 0x12FE37DC
new_password: 0xA65B9840
on_finger_scan_matched:
- homeassistant.event:
event: esphome.${devicename}_fingerprint_grow_finger_scan_matched
data:
finger_id: !lambda 'return finger_id;'
confidence: !lambda 'return confidence;'
on_finger_scan_unmatched:
- homeassistant.event:
event: esphome.${devicename}_fingerprint_grow_finger_scan_unmatched
on_enrollment_scan:
- homeassistant.event:
event: esphome.${devicename}_fingerprint_grow_enrollment_scan
data:
finger_id: !lambda 'return finger_id;'
scan_num: !lambda 'return scan_num;'
on_enrollment_done:
- homeassistant.event:
event: esphome.${devicename}_fingerprint_grow_node_enrollment_done
data:
finger_id: !lambda 'return finger_id;'
on_enrollment_failed:
- homeassistant.event:
event: esphome.${devicename}_fingerprint_grow_enrollment_failed
data:
finger_id: !lambda 'return finger_id;'