Make sync() operation protocol specific. Added sec+v1 wall panel emulation.

This commit is contained in:
Marius Muja 2024-01-09 00:35:29 -08:00
parent 608e0faeb2
commit ca26b6dfe8
7 changed files with 128 additions and 63 deletions

View File

@ -13,7 +13,6 @@ namespace ratgdo {
struct SetRollingCodeCounter { uint32_t counter; };
struct GetRollingCodeCounter {};
struct RollingCodeCounter { observable<uint32_t>* 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);
}

View File

@ -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);

View File

@ -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()

View File

@ -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);
});

View File

@ -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<Command> 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 };

View File

@ -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);
}

View File

@ -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);