From ca26b6dfe8a7a6df4a3a3c294ca8d601c9c62b12 Mon Sep 17 00:00:00 2001 From: Marius Muja Date: Tue, 9 Jan 2024 00:35:29 -0800 Subject: [PATCH] Make sync() operation protocol specific. Added sec+v1 wall panel emulation. --- components/ratgdo/common.h | 6 ---- components/ratgdo/protocol.h | 2 ++ components/ratgdo/ratgdo.cpp | 54 ++---------------------------- components/ratgdo/secplus1.cpp | 50 ++++++++++++++++++++++++++++ components/ratgdo/secplus1.h | 17 ++++++++-- components/ratgdo/secplus2.cpp | 60 ++++++++++++++++++++++++++++++++-- components/ratgdo/secplus2.h | 2 ++ 7 files changed, 128 insertions(+), 63 deletions(-) diff --git a/components/ratgdo/common.h b/components/ratgdo/common.h index 9252c0f..9f7105b 100644 --- a/components/ratgdo/common.h +++ b/components/ratgdo/common.h @@ -13,7 +13,6 @@ namespace ratgdo { struct SetRollingCodeCounter { uint32_t counter; }; struct GetRollingCodeCounter {}; struct RollingCodeCounter { observable* counter; }; -struct IncrementRollingCodeCounter { uint32_t increment; }; struct SetClientID { uint64_t client_id; }; struct ActivateLearn {}; struct InactivateLearn {}; @@ -26,7 +25,6 @@ public: SetRollingCodeCounter set_rolling_code_counter; GetRollingCodeCounter get_rolling_code_counter; RollingCodeCounter rolling_code_counter; - IncrementRollingCodeCounter increment_rolling_code_counter; SetClientID set_client_id; ActivateLearn activate_learn; InactivateLearn inactivate_learn; @@ -36,7 +34,6 @@ public: set_rolling_code_counter, get_rolling_code_counter, rolling_code_counter, - increment_rolling_code_counter, set_client_id, activate_learn, inactivate_learn, @@ -55,9 +52,6 @@ public: ProtocolArgs(RollingCodeCounter&& arg): tag(Tag::rolling_code_counter) { value.rolling_code_counter = std::move(arg); } - ProtocolArgs(IncrementRollingCodeCounter&& arg): tag(Tag::increment_rolling_code_counter) { - value.increment_rolling_code_counter = std::move(arg); - } ProtocolArgs(SetClientID&& arg): tag(Tag::set_client_id) { value.set_client_id = std::move(arg); } diff --git a/components/ratgdo/protocol.h b/components/ratgdo/protocol.h index bb6c44f..a874ee0 100644 --- a/components/ratgdo/protocol.h +++ b/components/ratgdo/protocol.h @@ -18,6 +18,8 @@ namespace ratgdo { virtual void loop(); virtual void dump_config(); + virtual void sync(); + virtual void light_action(LightAction action); virtual void lock_action(LockAction action); virtual void door_action(DoorAction action); diff --git a/components/ratgdo/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index 82654f2..0176f9b 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -36,8 +36,7 @@ namespace ratgdo { // that did not save the counter to flash in time which // results in the rolling counter being behind what the GDO // expects. - // - static const uint8_t MAX_CODES_WITHOUT_FLASH_WRITE = 10; + void RATGDOComponent::setup() { @@ -391,56 +390,7 @@ namespace ratgdo { void RATGDOComponent::sync() { - auto sync_step = [=]() { - if (*this->door_state == DoorState::UNKNOWN) { - this->query_status(); - return RetryResult::RETRY; - } - if (*this->openings == 0) { - this->query_openings(); - return RetryResult::RETRY; - } - if (*this->paired_total == PAIRED_DEVICES_UNKNOWN) { - this->query_paired_devices(PairedDevice::ALL); - return RetryResult::RETRY; - } - if (*this->paired_remotes == PAIRED_DEVICES_UNKNOWN) { - this->query_paired_devices(PairedDevice::REMOTE); - return RetryResult::RETRY; - } - if (*this->paired_keypads == PAIRED_DEVICES_UNKNOWN) { - this->query_paired_devices(PairedDevice::KEYPAD); - return RetryResult::RETRY; - } - if (*this->paired_wall_controls == PAIRED_DEVICES_UNKNOWN) { - this->query_paired_devices(PairedDevice::WALL_CONTROL); - return RetryResult::RETRY; - } - if (*this->paired_accessories == PAIRED_DEVICES_UNKNOWN) { - this->query_paired_devices(PairedDevice::ACCESSORY); - return RetryResult::RETRY; - } - return RetryResult::DONE; - }; - - const uint8_t MAX_ATTEMPTS = 10; - set_retry( - 500, MAX_ATTEMPTS, [=](uint8_t r) { - auto result = sync_step(); - if (result == RetryResult::RETRY) { - if (r == MAX_ATTEMPTS - 2 && *this->door_state == DoorState::UNKNOWN) { // made a few attempts and no progress (door state is the first sync request) - // increment rolling code counter by some amount in case we crashed without writing to flash the latest value - this->protocol_->call(IncrementRollingCodeCounter{MAX_CODES_WITHOUT_FLASH_WRITE}); - } - if (r == 0) { - // this was last attempt, notify of sync failure - ESP_LOGD(TAG, "Triggering sync failed actions."); - this->sync_failed = true; - } - } - return result; - }, - 1.5f); + this->protocol_->sync(); } void RATGDOComponent::open_door() diff --git a/components/ratgdo/secplus1.cpp b/components/ratgdo/secplus1.cpp index 12c823a..115f985 100644 --- a/components/ratgdo/secplus1.cpp +++ b/components/ratgdo/secplus1.cpp @@ -38,6 +38,44 @@ namespace secplus1 { ESP_LOGCONFIG(TAG, " Protocol: SEC+ v1"); } + + void Secplus1::sync() + { + this->wall_panel_emulation_state_ = WallPanelEmulationState::WAITING; + wall_panel_emulation_start_ = millis(); + this->wall_panel_emulation(0); + } + + void Secplus1::wall_panel_emulation(size_t index) + { + if (this->wall_panel_emulation_state_ == WallPanelEmulationState::WAITING) { + ESP_LOG1(TAG, "Looking for security+ 1.0 wall panel..."); + + if (this->door_state != DoorState::UNKNOWN || this->light_state != LightState::UNKNOWN) { + ESP_LOG1(TAG, "Wall panel detected"); + return; + } + if (millis() - wall_panel_emulation_start_ > 35000 && !this->wall_panel_starting_) { + ESP_LOG1(TAG, "No wall panel detected. Switching to emulation mode."); + this->wall_panel_emulation_state_ = WallPanelEmulationState::RUNNING; + } + this->scheduler_->set_timeout(this->ratgdo_, "", 2000, [=] { + this->wall_panel_emulation(index); + }); + return; + } else if (this->wall_panel_emulation_state_ == WallPanelEmulationState::RUNNING) { + ESP_LOG2(TAG, "[Wall panel emulation] Sending byte: [%02X]", secplus1_states[index]); + this->sw_serial_.write(&secplus1_states[index], 1); + index += 1; + if (index == 18) { + index = 15; + } + this->scheduler_->set_timeout(this->ratgdo_, "", 250, [=] { + this->wall_panel_emulation(index); + }); + } + } + void Secplus1::light_action(LightAction action) { if (action == LightAction::UNKNOWN) { @@ -98,8 +136,14 @@ namespace secplus1 { void Secplus1::query_action(QueryAction action) { + bool sync = false; ESP_LOG2(TAG, "Query action: %s", QueryAction_to_string(action)); if (action == QueryAction::STATUS) { + if (!sync) { + this->transmit_packet_delayed(secplus1_states, 19, 250); + // // sync = true; + // this->sw_serial_.write(secplus1_states, 19); + } } } @@ -225,6 +269,11 @@ namespace secplus1 { this->ratgdo_->received(lock_state); } } + else if (cmd.type == CommandType::WALL_PANEL_SYNC) { + if (cmd.value == 0x31) { + this->wall_panel_starting_ = true; + } + } } void Secplus1::transmit_packet(const uint8_t packet[], uint32_t len) @@ -254,6 +303,7 @@ namespace secplus1 { } this->scheduler_->set_timeout(this->ratgdo_, "", delay, [=] { + ESP_LOG2(TAG, "Sending byte: [%02X]", packet[0]); this->sw_serial_.write(packet[0]); this->transmit_packet_delayed(packet+1, len-1, delay); }); diff --git a/components/ratgdo/secplus1.h b/components/ratgdo/secplus1.h index d07cf51..77c8bc5 100644 --- a/components/ratgdo/secplus1.h +++ b/components/ratgdo/secplus1.h @@ -29,6 +29,7 @@ namespace secplus1 { static const uint8_t secplus1_states[] = {0x35,0x35,0x35,0x35,0x33,0x33,0x53,0x53,0x38,0x3A,0x3A,0x3A,0x39,0x38,0x3A, 0x38,0x3A,0x39,0x3A}; ENUM(CommandType, uint16_t, + (WALL_PANEL_SYNC, 0x31), (DOOR_STATUS, 0x38), (OBSTRUCTION, 0x39), // (OTHER_STATUS, 0x3A), @@ -43,6 +44,10 @@ namespace secplus1 { Command(CommandType type_, uint8_t value_ = 0) : type(type_), value(value_) {} }; + enum class WallPanelEmulationState { + WAITING, + RUNNING, + }; class Secplus1 : public Protocol { public: @@ -50,6 +55,8 @@ namespace secplus1 { void loop(); void dump_config(); + void sync(); + void light_action(LightAction action); void lock_action(LockAction action); void door_action(DoorAction action); @@ -58,7 +65,7 @@ namespace secplus1 { ProtocolArgs call(ProtocolArgs args); protected: - friend class RATGDOComponent; + void wall_panel_emulation(size_t index); optional read_command(); void handle_command(const Command& cmd); @@ -76,8 +83,12 @@ namespace secplus1 { DoorState door_state { DoorState::UNKNOWN }; DoorState prev_door_state { DoorState::UNKNOWN }; - bool transmit_pending_ { false }; - uint32_t transmit_pending_start_ { 0 }; + bool wall_panel_starting_ { false }; + uint32_t wall_panel_emulation_start_ { 0 }; + WallPanelEmulationState wall_panel_emulation_state_ { WallPanelEmulationState::WAITING }; + + // bool transmit_pending_ { false }; + // uint32_t transmit_pending_start_ { 0 }; TxPacket tx_packet_; uint32_t last_rx_ { 0 }; diff --git a/components/ratgdo/secplus2.cpp b/components/ratgdo/secplus2.cpp index aa43ea0..192af33 100644 --- a/components/ratgdo/secplus2.cpp +++ b/components/ratgdo/secplus2.cpp @@ -14,6 +14,8 @@ namespace esphome { namespace ratgdo { namespace secplus2 { + static const uint8_t MAX_CODES_WITHOUT_FLASH_WRITE = 10; + static const char* const TAG = "ratgdo_secplus2"; void Secplus2::setup(RATGDOComponent* ratgdo, Scheduler* scheduler, InternalGPIOPin* rx_pin, InternalGPIOPin* tx_pin) @@ -50,6 +52,62 @@ namespace secplus2 { ESP_LOGCONFIG(TAG, " Protocol: SEC+ v2"); } + + void Secplus2::sync() + { + auto sync_step = [=]() { + if (*this->ratgdo_->door_state == DoorState::UNKNOWN) { + this->query_status(); + return RetryResult::RETRY; + } + if (*this->ratgdo_->openings == 0) { + this->query_openings(); + return RetryResult::RETRY; + } + if (*this->ratgdo_->paired_total == PAIRED_DEVICES_UNKNOWN) { + this->query_paired_devices(PairedDevice::ALL); + return RetryResult::RETRY; + } + if (*this->ratgdo_->paired_remotes == PAIRED_DEVICES_UNKNOWN) { + this->query_paired_devices(PairedDevice::REMOTE); + return RetryResult::RETRY; + } + if (*this->ratgdo_->paired_keypads == PAIRED_DEVICES_UNKNOWN) { + this->query_paired_devices(PairedDevice::KEYPAD); + return RetryResult::RETRY; + } + if (*this->ratgdo_->paired_wall_controls == PAIRED_DEVICES_UNKNOWN) { + this->query_paired_devices(PairedDevice::WALL_CONTROL); + return RetryResult::RETRY; + } + if (*this->ratgdo_->paired_accessories == PAIRED_DEVICES_UNKNOWN) { + this->query_paired_devices(PairedDevice::ACCESSORY); + return RetryResult::RETRY; + } + return RetryResult::DONE; + }; + + const uint8_t MAX_ATTEMPTS = 10; + this->scheduler_->set_retry(this->ratgdo_, "", + 500, MAX_ATTEMPTS, [=](uint8_t r) { + auto result = sync_step(); + if (result == RetryResult::RETRY) { + if (r == MAX_ATTEMPTS - 2 && *this->ratgdo_->door_state == DoorState::UNKNOWN) { // made a few attempts and no progress (door state is the first sync request) + // increment rolling code counter by some amount in case we crashed without writing to flash the latest value + this->increment_rolling_code_counter(MAX_CODES_WITHOUT_FLASH_WRITE); + } + if (r == 0) { + // this was last attempt, notify of sync failure + ESP_LOGD(TAG, "Triggering sync failed actions."); + this->ratgdo_->sync_failed = true; + } + } + return result; + }, + 1.5f); + } + + void Secplus2::light_action(LightAction action) { if (action == LightAction::UNKNOWN) { @@ -92,8 +150,6 @@ namespace secplus2 { return ProtocolArgs(RollingCodeCounter{std::addressof(this->rolling_code_counter_)}); } else if (args.tag == Tag::set_rolling_code_counter) { this->set_rolling_code_counter(args.value.set_rolling_code_counter.counter); - } else if (args.tag == Tag::increment_rolling_code_counter) { - this->increment_rolling_code_counter(args.value.increment_rolling_code_counter.increment); } else if (args.tag == Tag::set_client_id) { this->set_client_id(args.value.set_client_id.client_id); } diff --git a/components/ratgdo/secplus2.h b/components/ratgdo/secplus2.h index 4d0ce51..6a51f1d 100644 --- a/components/ratgdo/secplus2.h +++ b/components/ratgdo/secplus2.h @@ -75,6 +75,8 @@ namespace secplus2 { void loop(); void dump_config(); + void sync(); + void light_action(LightAction action); void lock_action(LockAction action); void door_action(DoorAction action);