From 4d7c1ae143200b5ba21329505dcd8b17514fec14 Mon Sep 17 00:00:00 2001 From: Barry Loong Date: Thu, 29 Apr 2021 06:08:27 +0800 Subject: [PATCH] Add Grow Fingerprint Reader (#1356) --- CODEOWNERS | 1 + .../components/fingerprint_grow/__init__.py | 293 ++++++++++++ .../fingerprint_grow/binary_sensor.py | 20 + .../fingerprint_grow/fingerprint_grow.cpp | 434 ++++++++++++++++++ .../fingerprint_grow/fingerprint_grow.h | 276 +++++++++++ esphome/components/fingerprint_grow/sensor.py | 64 +++ esphome/const.py | 23 + tests/test3.yaml | 65 +++ 8 files changed, 1176 insertions(+) create mode 100644 esphome/components/fingerprint_grow/__init__.py create mode 100644 esphome/components/fingerprint_grow/binary_sensor.py create mode 100644 esphome/components/fingerprint_grow/fingerprint_grow.cpp create mode 100644 esphome/components/fingerprint_grow/fingerprint_grow.h create mode 100644 esphome/components/fingerprint_grow/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 08189a13b3..1a6a54a00c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/esphome/components/fingerprint_grow/__init__.py b/esphome/components/fingerprint_grow/__init__.py new file mode 100644 index 0000000000..6fbaa4e6c9 --- /dev/null +++ b/esphome/components/fingerprint_grow/__init__.py @@ -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 diff --git a/esphome/components/fingerprint_grow/binary_sensor.py b/esphome/components/fingerprint_grow/binary_sensor.py new file mode 100644 index 0000000000..4f49841f15 --- /dev/null +++ b/esphome/components/fingerprint_grow/binary_sensor.py @@ -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)) diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp new file mode 100644 index 0000000000..77ddf8ec37 --- /dev/null +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -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 diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h new file mode 100644 index 0000000000..e7d734777a --- /dev/null +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -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 callback) { + this->finger_scan_matched_callback_.add(std::move(callback)); + } + void add_on_finger_scan_unmatched_callback(std::function callback) { + this->finger_scan_unmatched_callback_.add(std::move(callback)); + } + void add_on_enrollment_scan_callback(std::function callback) { + this->enrollment_scan_callback_.add(std::move(callback)); + } + void add_on_enrollment_done_callback(std::function callback) { + this->enrollment_done_callback_.add(std::move(callback)); + } + + void add_on_enrollment_failed_callback(std::function 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 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 finger_scan_matched_callback_; + CallbackManager finger_scan_unmatched_callback_; + CallbackManager enrollment_scan_callback_; + CallbackManager enrollment_done_callback_; + CallbackManager enrollment_failed_callback_; +}; + +class FingerScanMatchedTrigger : public Trigger { + 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 { + 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 { + public: + explicit EnrollmentDoneTrigger(FingerprintGrowComponent *parent) { + parent->add_on_enrollment_done_callback([this](uint16_t finger_id) { this->trigger(finger_id); }); + } +}; + +class EnrollmentFailedTrigger : public Trigger { + public: + explicit EnrollmentFailedTrigger(FingerprintGrowComponent *parent) { + parent->add_on_enrollment_failed_callback([this](uint16_t finger_id) { this->trigger(finger_id); }); + } +}; + +template class EnrollmentAction : public Action, public Parented { + 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 +class CancelEnrollmentAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->finish_enrollment(1); } +}; + +template class DeleteAction : public Action, public Parented { + 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 class DeleteAllAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->delete_all_fingerprints(); } +}; + +template class LEDControlAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(bool, state) + + void play(Ts... x) override { + auto state = this->state_.value(x...); + this->parent_->led_control(state); + } +}; + +template class AuraLEDControlAction : public Action, public Parented { + 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 diff --git a/esphome/components/fingerprint_grow/sensor.py b/esphome/components/fingerprint_grow/sensor.py new file mode 100644 index 0000000000..c76c898727 --- /dev/null +++ b/esphome/components/fingerprint_grow/sensor.py @@ -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)) diff --git a/esphome/const.py b/esphome/const.py index 618c9fd922..eef9252a5c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -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" diff --git a/tests/test3.yaml b/tests/test3.yaml index ee95e9d35f..165a36c015 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -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;'