From 2ea7ca6e3e259dc5691874b7552db90e89928e14 Mon Sep 17 00:00:00 2001 From: Marius Muja Date: Tue, 5 Dec 2023 16:12:01 -0800 Subject: [PATCH 1/3] Convert lock remotes switch to a lock component (#132) Co-authored-by: J. Nick Koston --- base.yaml | 6 +- .../ratgdo/{switch => lock}/__init__.py | 19 ++----- components/ratgdo/lock/ratgdo_lock.cpp | 55 +++++++++++++++++++ components/ratgdo/lock/ratgdo_lock.h | 21 +++++++ components/ratgdo/switch/ratgdo_switch.cpp | 39 ------------- components/ratgdo/switch/ratgdo_switch.h | 29 ---------- 6 files changed, 84 insertions(+), 85 deletions(-) rename components/ratgdo/{switch => lock}/__init__.py (51%) create mode 100644 components/ratgdo/lock/ratgdo_lock.cpp create mode 100644 components/ratgdo/lock/ratgdo_lock.h delete mode 100644 components/ratgdo/switch/ratgdo_switch.cpp delete mode 100644 components/ratgdo/switch/ratgdo_switch.h diff --git a/base.yaml b/base.yaml index 5d29ea2..68e8977 100644 --- a/base.yaml +++ b/base.yaml @@ -33,13 +33,13 @@ sensor: unit_of_measurement: "openings" icon: mdi:open-in-app -switch: +lock: - platform: ratgdo id: ${id_prefix}_lock_remotes - type: lock - entity_category: config ratgdo_id: ${id_prefix} name: "Lock remotes" + +switch: - platform: gpio id: "${id_prefix}_status_door" internal: true diff --git a/components/ratgdo/switch/__init__.py b/components/ratgdo/lock/__init__.py similarity index 51% rename from components/ratgdo/switch/__init__.py rename to components/ratgdo/lock/__init__.py index a827bff..fc646d3 100644 --- a/components/ratgdo/switch/__init__.py +++ b/components/ratgdo/lock/__init__.py @@ -1,35 +1,26 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import switch +from esphome.components import lock from esphome.const import CONF_ID from .. import RATGDO_CLIENT_SCHMEA, ratgdo_ns, register_ratgdo_child DEPENDENCIES = ["ratgdo"] -RATGDOSwitch = ratgdo_ns.class_("RATGDOSwitch", switch.Switch, cg.Component) -SwitchType = ratgdo_ns.enum("SwitchType") - -CONF_TYPE = "type" -TYPES = { - "lock": SwitchType.RATGDO_LOCK, -} - +RATGDOLock = ratgdo_ns.class_("RATGDOLock", lock.Lock, cg.Component) CONFIG_SCHEMA = ( - switch.switch_schema(RATGDOSwitch) + lock.LOCK_SCHEMA .extend( { - cv.Required(CONF_TYPE): cv.enum(TYPES, lower=True), + cv.GenerateID(): cv.declare_id(RATGDOLock), } ) .extend(RATGDO_CLIENT_SCHMEA) ) - async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await switch.register_switch(var, config) + await lock.register_lock(var, config) await cg.register_component(var, config) - cg.add(var.set_switch_type(config[CONF_TYPE])) await register_ratgdo_child(var, config) diff --git a/components/ratgdo/lock/ratgdo_lock.cpp b/components/ratgdo/lock/ratgdo_lock.cpp new file mode 100644 index 0000000..7758424 --- /dev/null +++ b/components/ratgdo/lock/ratgdo_lock.cpp @@ -0,0 +1,55 @@ +#include "ratgdo_lock.h" +#include "../ratgdo_state.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace ratgdo { + + static const char* const TAG = "ratgdo.lock"; + + void RATGDOLock::dump_config() + { + LOG_LOCK("", "RATGDO Lock", this); + ESP_LOGCONFIG(TAG, " Type: Lock"); + } + + void RATGDOLock::setup() + { + this->parent_->subscribe_lock_state([=](LockState state) { + this->on_lock_state(state); + }); + } + + void RATGDOLock::on_lock_state(LockState state) + { + if (state == LockState::LOCKED && this->state == lock::LockState::LOCK_STATE_LOCKED) { + return; + } + if (state == LockState::UNLOCKED && this->state == lock::LockState::LOCK_STATE_UNLOCKED) { + return; + } + + auto call = this->make_call(); + if (state == LockState::LOCKED) { + call.set_state(lock::LockState::LOCK_STATE_LOCKED); + } else if (state == LockState::UNLOCKED) { + call.set_state(lock::LockState::LOCK_STATE_UNLOCKED); + } + this->control(call); + } + + void RATGDOLock::control(const lock::LockCall& call) + { + auto state = *call.get_state(); + + if (state == lock::LockState::LOCK_STATE_LOCKED) { + this->parent_->lock(); + } else if (state == lock::LockState::LOCK_STATE_UNLOCKED) { + this->parent_->unlock(); + } + + this->publish_state(state); + } + +} // namespace ratgdo +} // namespace esphome diff --git a/components/ratgdo/lock/ratgdo_lock.h b/components/ratgdo/lock/ratgdo_lock.h new file mode 100644 index 0000000..3ffb95a --- /dev/null +++ b/components/ratgdo/lock/ratgdo_lock.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../ratgdo.h" +#include "../ratgdo_state.h" +#include "esphome/components/lock/lock.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace ratgdo { + + class RATGDOLock : public lock::Lock, public RATGDOClient, public Component { + public: + void dump_config() override; + void setup() override; + + void on_lock_state(LockState state); + void control(const lock::LockCall& call) override; + }; + +} // namespace ratgdo +} // namespace esphome diff --git a/components/ratgdo/switch/ratgdo_switch.cpp b/components/ratgdo/switch/ratgdo_switch.cpp deleted file mode 100644 index c6ba4e6..0000000 --- a/components/ratgdo/switch/ratgdo_switch.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "ratgdo_switch.h" -#include "../ratgdo_state.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace ratgdo { - - static const char* const TAG = "ratgdo.switch"; - - void RATGDOSwitch::dump_config() - { - LOG_SWITCH("", "RATGDO Switch", this); - ESP_LOGCONFIG(TAG, " Type: Lock"); - } - - void RATGDOSwitch::setup() - { - this->parent_->subscribe_lock_state([=](LockState state) { - this->on_lock_state(state); - }); - } - - void RATGDOSwitch::on_lock_state(LockState state) - { - bool value = state == LockState::LOCKED; - this->state = value; - this->publish_state(value); - } - void RATGDOSwitch::write_state(bool state) - { - if (state) { - this->parent_->lock(); - } else { - this->parent_->unlock(); - } - } - -} // namespace ratgdo -} // namespace esphome diff --git a/components/ratgdo/switch/ratgdo_switch.h b/components/ratgdo/switch/ratgdo_switch.h deleted file mode 100644 index e29ad99..0000000 --- a/components/ratgdo/switch/ratgdo_switch.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "../ratgdo.h" -#include "../ratgdo_state.h" -#include "esphome/components/switch/switch.h" -#include "esphome/core/component.h" - -namespace esphome { -namespace ratgdo { - - enum SwitchType { - RATGDO_LOCK - }; - - class RATGDOSwitch : public switch_::Switch, public RATGDOClient, public Component { - public: - void dump_config() override; - void setup() override; - void set_switch_type(SwitchType switch_type_) { this->switch_type_ = switch_type_; } - - void on_lock_state(LockState state); - void write_state(bool state) override; - - protected: - SwitchType switch_type_; - }; - -} // namespace ratgdo -} // namespace esphome From 892c4e2872f120d84b172a6f130c706ceec9930b Mon Sep 17 00:00:00 2001 From: Marius Muja Date: Fri, 15 Dec 2023 22:26:30 -0800 Subject: [PATCH 2/3] Detect "not connected to GDO" condition (#143) Co-authored-by: J. Nick Koston --- components/ratgdo/ratgdo.cpp | 23 +++++++++++++++++++---- components/ratgdo/ratgdo.h | 1 + 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/components/ratgdo/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index b6ad370..66fb3fd 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -446,9 +446,13 @@ namespace ratgdo { if (!this->transmit_pending_) { // have an untransmitted packet this->encode_packet(command, data, increment, this->tx_packet_); } else { - // unlikely this would happed, we're ensuring any pending packet + // unlikely this would happed (unless not connected to GDO), we're ensuring any pending packet // is transmitted each loop before doing anyting else - ESP_LOGW(TAG, "Have untransmitted packet, ignoring command: %s", Command_to_string(command)); + if (this->transmit_pending_start_ > 0) { + ESP_LOGW(TAG, "Have untransmitted packet, ignoring command: %s", Command_to_string(command)); + } else { + ESP_LOGW(TAG, "Not connected to GDO, ignoring command: %s", Command_to_string(command)); + } } this->transmit_packet(); } @@ -462,10 +466,20 @@ namespace ratgdo { bool RATGDOComponent::transmit_packet() { auto now = micros(); + while (micros() - now < 1300) { if (this->input_gdo_pin_->digital_read()) { - ESP_LOGD(TAG, "Collision detected, waiting to send packet"); - this->transmit_pending_ = true; + if (!this->transmit_pending_) { + this->transmit_pending_ = true; + this->transmit_pending_start_ = millis(); + ESP_LOGD(TAG, "Collision detected, waiting to send packet"); + } else { + if (millis() - this->transmit_pending_start_ < 5000) { + ESP_LOGD(TAG, "Collision detected, waiting to send packet"); + } else { + this->transmit_pending_start_ = 0; // to indicate GDO not connected state + } + } return false; } delayMicroseconds(100); @@ -484,6 +498,7 @@ namespace ratgdo { this->sw_serial_.write(this->tx_packet_, PACKET_LENGTH); this->transmit_pending_ = false; + this->transmit_pending_start_ = 0; this->command_sent(); return true; } diff --git a/components/ratgdo/ratgdo.h b/components/ratgdo/ratgdo.h index a42af7e..147b012 100644 --- a/components/ratgdo/ratgdo.h +++ b/components/ratgdo/ratgdo.h @@ -195,6 +195,7 @@ namespace ratgdo { protected: // tx data bool transmit_pending_ { false }; + uint32_t transmit_pending_start_ { 0 }; WirePacket tx_packet_; RATGDOStore isr_store_ {}; From 53752d588c9de6080d4649d0bf9b755066ab1545 Mon Sep 17 00:00:00 2001 From: Marius Muja Date: Sat, 16 Dec 2023 09:50:51 -0800 Subject: [PATCH 3/3] Drop an incomplete packet if the missing bytes are lost (#144) Co-authored-by: J. Nick Koston --- components/ratgdo/ratgdo.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/components/ratgdo/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index 66fb3fd..21b97ca 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -318,8 +318,7 @@ namespace ratgdo { void RATGDOComponent::print_packet(const WirePacket& packet) const { - ESP_LOGV(TAG, "Counter: %d Send code: [%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X]", - *this->rolling_code_counter, + ESP_LOG2(TAG, "Packet: [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X]", packet[0], packet[1], packet[2], @@ -390,12 +389,15 @@ namespace ratgdo { static uint32_t msg_start = 0; static uint16_t byte_count = 0; static WirePacket rx_packet; + static uint32_t last_read = 0; if (!reading_msg) { while (this->sw_serial_.available()) { uint8_t ser_byte = this->sw_serial_.read(); + last_read = millis(); + if (ser_byte != 0x55 && ser_byte != 0x01 && ser_byte != 0x00) { - ESP_LOG2(TAG, "Ignoring byte: %02X, baud: %d", ser_byte, this->sw_serial_.baudRate()); + ESP_LOG2(TAG, "Ignoring byte (%d): %02X, baud: %d", byte_count, ser_byte, this->sw_serial_.baudRate()); byte_count = 0; continue; } @@ -417,16 +419,28 @@ namespace ratgdo { if (reading_msg) { while (this->sw_serial_.available()) { uint8_t ser_byte = this->sw_serial_.read(); + last_read = millis(); rx_packet[byte_count] = ser_byte; byte_count++; + // ESP_LOG2(TAG, "Received byte (%d): %02X, baud: %d", byte_count, ser_byte, this->sw_serial_.baudRate()); if (byte_count == PACKET_LENGTH) { reading_msg = false; byte_count = 0; + this->print_packet(rx_packet); this->decode_packet(rx_packet); return; } } + + if (millis() - last_read > 100) { + // if we have a partial packet and it's been over 100ms since last byte was read, + // the rest is not coming (a full packet should be received in ~20ms), + // discard it so we can read the following packet correctly + ESP_LOGW(TAG, "Discard incomplete packet, length: %d", byte_count); + reading_msg = false; + byte_count = 0; + } } }