diff --git a/base.yaml b/base.yaml index 59a7658..6eee49c 100644 --- a/base.yaml +++ b/base.yaml @@ -47,19 +47,13 @@ select: ratgdo_id: ${id_prefix} name: "Time to close" -switch: +lock: - platform: ratgdo id: ${id_prefix}_lock_remotes - type: lock - entity_category: config ratgdo_id: ${id_prefix} name: "Lock remotes" - - platform: ratgdo - id: ${id_prefix}_TTC_hold - type: hold - entity_category: config - ratgdo_id: ${id_prefix} - name: "TTC Hold Open" + +switch: - platform: gpio id: "${id_prefix}_status_door" internal: true diff --git a/components/ratgdo/lock/__init__.py b/components/ratgdo/lock/__init__.py new file mode 100644 index 0000000..fc646d3 --- /dev/null +++ b/components/ratgdo/lock/__init__.py @@ -0,0 +1,26 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import lock +from esphome.const import CONF_ID + +from .. import RATGDO_CLIENT_SCHMEA, ratgdo_ns, register_ratgdo_child + +DEPENDENCIES = ["ratgdo"] + +RATGDOLock = ratgdo_ns.class_("RATGDOLock", lock.Lock, cg.Component) + +CONFIG_SCHEMA = ( + lock.LOCK_SCHEMA + .extend( + { + cv.GenerateID(): cv.declare_id(RATGDOLock), + } + ) + .extend(RATGDO_CLIENT_SCHMEA) +) + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await lock.register_lock(var, config) + await cg.register_component(var, config) + 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/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index 0cde8b5..02b0835 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -359,8 +359,7 @@ if (this->input_obst_pin_ == nullptr || this->input_obst_pin_->get_pin() == 0) { 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], @@ -431,12 +430,15 @@ if (this->input_obst_pin_ == nullptr || this->input_obst_pin_->get_pin() == 0) { 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; } @@ -458,16 +460,28 @@ if (this->input_obst_pin_ == nullptr || this->input_obst_pin_->get_pin() == 0) { 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; + } } } @@ -531,9 +545,13 @@ if (this->input_obst_pin_ == nullptr || this->input_obst_pin_->get_pin() == 0) { 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(); } @@ -547,10 +565,20 @@ if (this->input_obst_pin_ == nullptr || this->input_obst_pin_->get_pin() == 0) { 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); @@ -569,6 +597,7 @@ if (this->input_obst_pin_ == nullptr || this->input_obst_pin_->get_pin() == 0) { 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 6bb2e58..24ff844 100644 --- a/components/ratgdo/ratgdo.h +++ b/components/ratgdo/ratgdo.h @@ -227,6 +227,7 @@ namespace ratgdo { protected: // tx data bool transmit_pending_ { false }; + uint32_t transmit_pending_start_ { 0 }; WirePacket tx_packet_; RATGDOStore isr_store_ {}; diff --git a/components/ratgdo/switch/__init__.py b/components/ratgdo/switch/__init__.py deleted file mode 100644 index e015771..0000000 --- a/components/ratgdo/switch/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import switch -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, - "hold": SwitchType.RATGDO_HOLDOPEN, -} - - -CONFIG_SCHEMA = ( - switch.switch_schema(RATGDOSwitch) - .extend( - { - cv.Required(CONF_TYPE): cv.enum(TYPES, lower=True), - } - ) - .extend(RATGDO_CLIENT_SCHMEA) -) - - -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await switch.register_switch(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/switch/ratgdo_switch.cpp b/components/ratgdo/switch/ratgdo_switch.cpp deleted file mode 100644 index a087c95..0000000 --- a/components/ratgdo/switch/ratgdo_switch.cpp +++ /dev/null @@ -1,64 +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); - if (this->switch_type_ == SwitchType::RATGDO_LOCK) { - ESP_LOGCONFIG(TAG, " Type: Lock"); - } else if (this->switch_type_ == SwitchType::RATGDO_HOLDOPEN) { - ESP_LOGCONFIG(TAG, " Type: Hold Open"); - } - } - - void RATGDOSwitch::setup() - { - if (this->switch_type_ == SwitchType::RATGDO_LOCK) { - this->parent_->subscribe_lock_state([=](LockState state) { - this->on_lock_state(state); - }); - } else if (this->switch_type_ == SwitchType::RATGDO_HOLDOPEN) { - this->parent_->subscribe_hold_state([=](HoldState state) { - this->on_hold_state(state); - }); - } - } - - void RATGDOSwitch::on_lock_state(LockState state) - { - bool value = state == LockState::LOCKED; - this->state = value; - this->publish_state(value); - } - void RATGDOSwitch::on_hold_state(HoldState state) - { - bool value = state == HoldState::HOLD_ENABLED; - this->state = value; - this->publish_state(value); - } - - void RATGDOSwitch::write_state(bool state) - { - if (this->switch_type_ == SwitchType::RATGDO_LOCK) { - if (state) { - this->parent_->lock(); - } else { - this->parent_->unlock(); - } - } else if (this->switch_type_ == SwitchType::RATGDO_HOLDOPEN) { - if (state) { - this->parent_->hold_enable(); - } else { - this->parent_->hold_disable(); - } - } - } - -} // 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 43d2d36..0000000 --- a/components/ratgdo/switch/ratgdo_switch.h +++ /dev/null @@ -1,31 +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, - RATGDO_HOLDOPEN - }; - - 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 on_hold_state(HoldState state); - void write_state(bool state) override; - - protected: - SwitchType switch_type_; - }; - -} // namespace ratgdo -} // namespace esphome