Door opening/close relliability updates (#64)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Marius Muja 2023-10-18 17:18:42 -07:00 committed by GitHub
parent 575471bda1
commit 6d8f3e0e86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 27 deletions

View File

@ -210,4 +210,12 @@ button:
on_press:
then:
lambda: !lambda |-
id($id_prefix).sync();
id($id_prefix).sync();
- platform: template
id: ${id_prefix}_toggle_door
name: "Toggle door"
on_press:
then:
lambda: !lambda |-
id($id_prefix).toggle_door();

View File

@ -0,0 +1,30 @@
#pragma once
#include <functional>
#include <utility>
#include <vector>
namespace esphome {
namespace ratgdo {
template <typename... X>
class OnceCallbacks;
template <typename... Ts>
class OnceCallbacks<void(Ts...)> {
public:
template <typename Callback>
void then(Callback&& callback) { this->callbacks_.push_back(std::forward<Callback>(callback)); }
void operator()(Ts... args)
{
for (auto& cb : this->callbacks_)
cb(args...);
this->callbacks_.clear();
}
protected:
std::vector<std::function<void(Ts...)>> callbacks_;
};
} // namespace ratgdo
} // namespace esphome

View File

@ -1,4 +1,7 @@
#pragma once
#include <functional>
#include <utility>
#include <vector>
namespace esphome {
namespace ratgdo {

View File

@ -159,12 +159,6 @@ namespace ratgdo {
this->door_move_delta = 1.0 - this->door_start_position;
}
this->schedule_door_position_sync();
// this would only get called if no status message is received after door stops moving
// request a status message in that case
set_timeout("door_status_update", (*this->opening_duration + 1) * 1000, [=]() {
this->send_command(Command::GET_STATUS);
});
} else if (door_state == DoorState::CLOSING) {
// door started closing
if (prev_door_state == DoorState::OPENING) {
@ -178,12 +172,6 @@ namespace ratgdo {
this->door_move_delta = 0.0 - this->door_start_position;
}
this->schedule_door_position_sync();
// this would only get called if no status message is received after door stops moving
// request a status message in that case
set_timeout("door_status_update", (*this->closing_duration + 1) * 1000, [=]() {
this->send_command(Command::GET_STATUS);
});
} else if (door_state == DoorState::STOPPED) {
this->door_position_update();
if (*this->door_position == DOOR_POSITION_UNKNOWN) {
@ -199,6 +187,7 @@ namespace ratgdo {
}
this->door_state = door_state;
this->door_state_received(door_state);
this->light_state = static_cast<LightState>((byte2 >> 1) & 1); // safe because it can only be 0 or 1
this->lock_state = static_cast<LockState>(byte2 & 1); // safe because it can only be 0 or 1
this->motion_state = MotionState::CLEAR; // when the status message is read, reset motion state to 0|clear
@ -246,7 +235,7 @@ namespace ratgdo {
this->openings = (byte1 << 8) | byte2;
ESP_LOGD(TAG, "Openings: %d", *this->openings);
} else {
ESP_LOGD(TAG, "Ignoreing openings, not from our request");
ESP_LOGD(TAG, "Ignoring openings, not from our request");
}
} else if (cmd == Command::MOTION) {
this->motion_state = MotionState::DETECTED;
@ -325,7 +314,7 @@ namespace ratgdo {
void RATGDOComponent::print_packet(const WirePacket& packet) const
{
ESP_LOG2(TAG, "Counter: %d Send code: [%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X]",
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,
packet[0],
packet[1],
@ -448,6 +437,7 @@ namespace ratgdo {
void RATGDOComponent::send_command(Command command, uint32_t data, bool increment)
{
ESP_LOG1(TAG, "Send command: %s, data: %08" PRIx32, Command_to_string(command), data);
if (!this->transmit_pending_) { // have an untransmitted packet
this->encode_packet(command, data, increment, this->tx_packet_);
} else {
@ -458,6 +448,12 @@ namespace ratgdo {
this->transmit_packet();
}
void RATGDOComponent::send_command(Command command, uint32_t data, bool increment, std::function<void()>&& on_sent)
{
this->command_sent.then(on_sent);
this->send_command(command, data, increment);
}
bool RATGDOComponent::transmit_packet()
{
auto now = micros();
@ -483,6 +479,7 @@ namespace ratgdo {
this->sw_serial_.write(this->tx_packet_, PACKET_LENGTH);
this->transmit_pending_ = false;
this->command_sent();
return true;
}
@ -531,10 +528,21 @@ namespace ratgdo {
void RATGDOComponent::close_door()
{
if (*this->door_state == DoorState::CLOSING || *this->door_state == DoorState::OPENING) {
if (*this->door_state == DoorState::CLOSING) {
return; // gets ignored by opener
}
if (*this->door_state == DoorState::OPENING) {
// have to stop door first, otherwise close command is ignored
this->door_command(data::DOOR_STOP);
this->door_state_received.then([=](DoorState s) {
if (s == DoorState::STOPPED) {
this->door_command(data::DOOR_CLOSE);
}
});
return;
}
this->door_command(data::DOOR_CLOSE);
}
@ -549,17 +557,18 @@ namespace ratgdo {
void RATGDOComponent::toggle_door()
{
if (*this->door_state == DoorState::OPENING) {
return; // gets ignored by opener
}
this->door_command(data::DOOR_TOGGLE);
}
void RATGDOComponent::door_move_to_position(float position)
{
if (*this->door_state == DoorState::OPENING || *this->door_state == DoorState::CLOSING) {
ESP_LOGW(TAG, "The door is moving, ignoring.");
this->door_command(data::DOOR_STOP);
this->door_state_received.then([=](DoorState s) {
if (s == DoorState::STOPPED) {
this->door_move_to_position(position);
}
});
return;
}
@ -581,7 +590,7 @@ namespace ratgdo {
this->door_command(delta > 0 ? data::DOOR_OPEN : data::DOOR_CLOSE);
set_timeout("move_to_position", operation_time, [=] {
this->door_command(data::DOOR_STOP);
this->ensure_door_command(data::DOOR_STOP);
});
}
@ -591,7 +600,6 @@ namespace ratgdo {
ESP_LOGD(TAG, "Cancelling position callbacks");
cancel_timeout("move_to_position");
cancel_retry("position_sync_while_moving");
cancel_timeout("door_status_update");
this->door_start_moving = 0;
this->door_start_position = DOOR_POSITION_UNKNOWN;
@ -603,10 +611,38 @@ namespace ratgdo {
{
data |= (1 << 16); // button 1 ?
data |= (1 << 8); // button press
this->send_command(Command::DOOR_ACTION, data, false);
set_timeout(200, [=] {
auto data2 = data & ~(1 << 8); // button release
this->send_command(Command::DOOR_ACTION, data2);
this->send_command(Command::DOOR_ACTION, data, false, [=]() {
set_timeout(100, [=] {
auto data2 = data & ~(1 << 8); // button release
this->send_command(Command::DOOR_ACTION, data2);
});
});
}
void RATGDOComponent::ensure_door_command(uint32_t data, uint32_t delay)
{
if (data == data::DOOR_TOGGLE) {
ESP_LOGW(TAG, "It's not recommended to use ensure_door_command with non-idempotent commands such as DOOR_TOGGLE");
}
auto prev_door_state = *this->door_state;
this->door_state_received.then([=](DoorState s) {
if ((data == data::DOOR_STOP) && (s != DoorState::STOPPED) && !(prev_door_state == DoorState::OPENING && s == DoorState::OPEN) && !(prev_door_state == DoorState::CLOSING && s == DoorState::CLOSED)) {
return;
}
if (data == data::DOOR_OPEN && !(s == DoorState::OPENING || s == DoorState::OPEN)) {
return;
}
if (data == data::DOOR_CLOSE && !(s == DoorState::CLOSED || s == DoorState::CLOSING)) {
return;
}
ESP_LOG1(TAG, "Received door status, cancel door command retry");
cancel_timeout("door_command_retry");
});
this->door_command(data);
ESP_LOG1(TAG, "Ensure door command, setup door command retry");
set_timeout("door_command_retry", delay, [=]() {
this->ensure_door_command(data);
});
}

View File

@ -13,6 +13,7 @@
#pragma once
#include "SoftwareSerial.h" // Using espsoftwareserial https://github.com/plerup/espsoftwareserial
#include "callbacks.h"
#include "enum.h"
#include "esphome/core/component.h"
#include "esphome/core/gpio.h"
@ -124,6 +125,9 @@ namespace ratgdo {
observable<ButtonState> button_state { ButtonState::UNKNOWN };
observable<MotionState> motion_state { MotionState::UNKNOWN };
OnceCallbacks<void(DoorState)> door_state_received;
OnceCallbacks<void()> command_sent;
observable<bool> sync_failed { false };
void set_output_gdo_pin(InternalGPIOPin* pin) { this->output_gdo_pin_ = pin; }
@ -136,6 +140,7 @@ namespace ratgdo {
uint16_t decode_packet(const WirePacket& packet);
void obstruction_loop();
void send_command(Command command, uint32_t data = 0, bool increment = true);
void send_command(Command command, uint32_t data, bool increment, std::function<void()>&& on_sent);
bool transmit_packet();
void encode_packet(Command command, uint32_t data, bool increment, WirePacket& packet);
void print_packet(const WirePacket& packet) const;
@ -145,6 +150,7 @@ namespace ratgdo {
// door
void door_command(uint32_t data);
void ensure_door_command(uint32_t data, uint32_t delay = 1500);
void toggle_door();
void open_door();
void close_door();