Sync time to the lock automatically
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
import esphome.const as c
|
||||
from esphome.components import lock, binary_sensor, text_sensor, sensor, button
|
||||
from esphome.components import lock, binary_sensor, text_sensor, sensor, button, time
|
||||
|
||||
AUTO_LOAD = ["binary_sensor", "text_sensor", "sensor", "button"]
|
||||
|
||||
@@ -32,6 +32,7 @@ CONF_REQUEST_BATTERY_REPORTS = "request_battery_reports"
|
||||
CONF_RESTART_AFTER_BEACON_LATENCY = "restart_after_beacon_latency"
|
||||
CONF_REQUEST_STATE = "request_state"
|
||||
CONF_UNPAIR = "unpair"
|
||||
CONF_TIME_SOURCE = "time_source"
|
||||
|
||||
nuki_lock_ns = cg.esphome_ns.namespace("nuki_lock")
|
||||
NukiLockComponent = nuki_lock_ns.class_("NukiLockComponent", lock.Lock, cg.PollingComponent)
|
||||
@@ -184,6 +185,7 @@ CONFIG_SCHEMA = lock.LOCK_SCHEMA.extend({
|
||||
unit_of_measurement=c.UNIT_AMPERE,
|
||||
device_class=c.DEVICE_CLASS_CURRENT,
|
||||
accuracy_decimals=3,
|
||||
# DEFAULT DISABLE. A few others, too.
|
||||
),
|
||||
|
||||
# Configuration.
|
||||
@@ -203,8 +205,11 @@ CONFIG_SCHEMA = lock.LOCK_SCHEMA.extend({
|
||||
entity_category=c.ENTITY_CATEGORY_CONFIG,
|
||||
icon="mdi:close",
|
||||
),
|
||||
}).extend(cv.polling_component_schema("30min"))
|
||||
|
||||
# Time autoupdate.
|
||||
cv.Optional(c.CONF_PIN): cv.positive_int,
|
||||
cv.Optional(CONF_TIME_SOURCE): cv.use_id(time.RealTimeClock),
|
||||
}).extend(cv.polling_component_schema("30min"))
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[c.CONF_ID])
|
||||
@@ -269,3 +274,9 @@ async def to_code(config):
|
||||
|
||||
if CONF_RESTART_AFTER_BEACON_LATENCY in config:
|
||||
cg.add(var.set_restart_after_beacon_latency(config[CONF_RESTART_AFTER_BEACON_LATENCY]))
|
||||
|
||||
if c.CONF_PIN in config:
|
||||
cg.add(var.set_pin(config[c.CONF_PIN]))
|
||||
|
||||
if CONF_TIME_SOURCE in config:
|
||||
cg.add(var.set_time_source(await cg.get_variable(config[CONF_TIME_SOURCE])))
|
||||
|
||||
@@ -231,6 +231,49 @@ bool NukiLockComponent::update_key_turner_state_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NukiLockComponent::update_lock_time_() {
|
||||
if (time_source_ == nullptr) {
|
||||
ESP_LOGE(TAG, "updating lock time failed: time source not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
Nuki::CmdResult result = nuki_lock_->verifySecurityPin();
|
||||
if (result != Nuki::CmdResult::Success) {
|
||||
ESP_LOGE(TAG, "verifying security pin for lock time update failed: %s",
|
||||
Adapt::cmd_result_to_string(result).c_str());
|
||||
error_text_sensor_->publish_state(
|
||||
Adapt::error_to_string(nuki_lock_->getLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
ESPTime now = time_source_->utcnow();
|
||||
if (!now.is_valid()) {
|
||||
ESP_LOGE(TAG, "updating lock time failed: time source is invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
Nuki::TimeValue tv = {
|
||||
.year = now.year,
|
||||
.month = now.month,
|
||||
.day = now.day_of_month,
|
||||
.hour = now.hour,
|
||||
.minute = now.minute,
|
||||
.second = now.second,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Setting lock time: %.4d-%.2d-%.2d %.2d:%.2d:%.2d", tv.year,
|
||||
tv.month, tv.day, tv.hour, tv.minute, tv.second);
|
||||
result = nuki_lock_->updateTime(tv);
|
||||
if (result != Nuki::CmdResult::Success) {
|
||||
ESP_LOGE(TAG, "updating lock time failed: %s",
|
||||
Adapt::cmd_result_to_string(result).c_str());
|
||||
error_text_sensor_->publish_state(
|
||||
Adapt::error_to_string(nuki_lock_->getLastError()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NukiLockComponent::update_battery_report_() {
|
||||
NukiLock::BatteryReport batteryReport;
|
||||
Nuki::CmdResult result =
|
||||
@@ -304,6 +347,10 @@ void NukiLockComponent::setup() {
|
||||
nuki_lock_->initialize();
|
||||
nuki_lock_->setEventHandler(this);
|
||||
|
||||
if (pin_set_) {
|
||||
nuki_lock_->saveSecurityPincode(pin_);
|
||||
}
|
||||
|
||||
bool paired = nuki_lock_->isPairedWithLock();
|
||||
paired_binary_sensor_->publish_initial_state(paired);
|
||||
error_text_sensor_->publish_state("No error");
|
||||
@@ -387,6 +434,10 @@ void NukiLockComponent::ble_loop_() {
|
||||
void NukiLockComponent::update() {
|
||||
bool paired = nuki_lock_->isPairedWithLock();
|
||||
if (paired) {
|
||||
if (time_source_ != nullptr) {
|
||||
// Update time first, then request the state so we get updated time.
|
||||
update_lock_time_();
|
||||
}
|
||||
update_key_turner_state_();
|
||||
if (request_battery_reports_) {
|
||||
update_battery_report_();
|
||||
@@ -477,6 +528,14 @@ void NukiLockComponent::dump_config() {
|
||||
|
||||
ESP_LOGCONFIG(TAG, " Restart after beacon latency: %dms",
|
||||
restart_after_beacon_latency_);
|
||||
if (time_source_ != nullptr) {
|
||||
ESP_LOGCONFIG(TAG, " Time source: set");
|
||||
}
|
||||
if (pin_set_) {
|
||||
ESP_LOGCONFIG(
|
||||
TAG, " Pin: " ESPHOME_LOG_SECRET_BEGIN "%d" ESPHOME_LOG_SECRET_END,
|
||||
pin_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nuki_lock
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "esphome/components/lock/lock.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/time.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nuki_lock {
|
||||
@@ -22,7 +24,8 @@ class NukiLockComponent : public lock::Lock,
|
||||
last_passive_update_millis_(0),
|
||||
restart_after_beacon_latency_(0),
|
||||
update_scheduled_(false),
|
||||
request_battery_reports_(false){};
|
||||
request_battery_reports_(false),
|
||||
pin_set_(false){};
|
||||
|
||||
// Component.
|
||||
void setup() override;
|
||||
@@ -81,6 +84,13 @@ class NukiLockComponent : public lock::Lock,
|
||||
restart_after_beacon_latency_ = value;
|
||||
}
|
||||
|
||||
void set_pin(unsigned long value) {
|
||||
pin_ = value;
|
||||
pin_set_ = true;
|
||||
}
|
||||
|
||||
void set_time_source(time::RealTimeClock *value) { time_source_ = value; }
|
||||
|
||||
// RequestStateButton.
|
||||
void schedule_update();
|
||||
|
||||
@@ -95,11 +105,15 @@ class NukiLockComponent : public lock::Lock,
|
||||
private:
|
||||
bool update_key_turner_state_();
|
||||
bool update_battery_report_();
|
||||
bool update_lock_time_();
|
||||
void ble_loop_();
|
||||
void send_action_(NukiLock::LockAction action);
|
||||
|
||||
NukiLock::NukiLock *nuki_lock_;
|
||||
BleScanner::Scanner scanner_;
|
||||
time::RealTimeClock *time_source_{nullptr};
|
||||
uint16_t pin_;
|
||||
bool pin_set_;
|
||||
|
||||
unsigned long last_passive_update_millis_;
|
||||
unsigned long restart_after_beacon_latency_;
|
||||
|
||||
Reference in New Issue
Block a user