Door opening/close relliability updates (#64)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
575471bda1
commit
6d8f3e0e86
|
@ -211,3 +211,11 @@ button:
|
||||||
then:
|
then:
|
||||||
lambda: !lambda |-
|
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();
|
||||||
|
|
|
@ -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
|
|
@ -1,4 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ratgdo {
|
namespace ratgdo {
|
||||||
|
|
|
@ -159,12 +159,6 @@ namespace ratgdo {
|
||||||
this->door_move_delta = 1.0 - this->door_start_position;
|
this->door_move_delta = 1.0 - this->door_start_position;
|
||||||
}
|
}
|
||||||
this->schedule_door_position_sync();
|
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) {
|
} else if (door_state == DoorState::CLOSING) {
|
||||||
// door started closing
|
// door started closing
|
||||||
if (prev_door_state == DoorState::OPENING) {
|
if (prev_door_state == DoorState::OPENING) {
|
||||||
|
@ -178,12 +172,6 @@ namespace ratgdo {
|
||||||
this->door_move_delta = 0.0 - this->door_start_position;
|
this->door_move_delta = 0.0 - this->door_start_position;
|
||||||
}
|
}
|
||||||
this->schedule_door_position_sync();
|
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) {
|
} else if (door_state == DoorState::STOPPED) {
|
||||||
this->door_position_update();
|
this->door_position_update();
|
||||||
if (*this->door_position == DOOR_POSITION_UNKNOWN) {
|
if (*this->door_position == DOOR_POSITION_UNKNOWN) {
|
||||||
|
@ -199,6 +187,7 @@ namespace ratgdo {
|
||||||
}
|
}
|
||||||
|
|
||||||
this->door_state = door_state;
|
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->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->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
|
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;
|
this->openings = (byte1 << 8) | byte2;
|
||||||
ESP_LOGD(TAG, "Openings: %d", *this->openings);
|
ESP_LOGD(TAG, "Openings: %d", *this->openings);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGD(TAG, "Ignoreing openings, not from our request");
|
ESP_LOGD(TAG, "Ignoring openings, not from our request");
|
||||||
}
|
}
|
||||||
} else if (cmd == Command::MOTION) {
|
} else if (cmd == Command::MOTION) {
|
||||||
this->motion_state = MotionState::DETECTED;
|
this->motion_state = MotionState::DETECTED;
|
||||||
|
@ -325,7 +314,7 @@ namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOComponent::print_packet(const WirePacket& packet) const
|
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,
|
*this->rolling_code_counter,
|
||||||
packet[0],
|
packet[0],
|
||||||
packet[1],
|
packet[1],
|
||||||
|
@ -448,6 +437,7 @@ namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOComponent::send_command(Command command, uint32_t data, bool increment)
|
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
|
if (!this->transmit_pending_) { // have an untransmitted packet
|
||||||
this->encode_packet(command, data, increment, this->tx_packet_);
|
this->encode_packet(command, data, increment, this->tx_packet_);
|
||||||
} else {
|
} else {
|
||||||
|
@ -458,6 +448,12 @@ namespace ratgdo {
|
||||||
this->transmit_packet();
|
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()
|
bool RATGDOComponent::transmit_packet()
|
||||||
{
|
{
|
||||||
auto now = micros();
|
auto now = micros();
|
||||||
|
@ -483,6 +479,7 @@ namespace ratgdo {
|
||||||
|
|
||||||
this->sw_serial_.write(this->tx_packet_, PACKET_LENGTH);
|
this->sw_serial_.write(this->tx_packet_, PACKET_LENGTH);
|
||||||
this->transmit_pending_ = false;
|
this->transmit_pending_ = false;
|
||||||
|
this->command_sent();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,10 +528,21 @@ namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOComponent::close_door()
|
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
|
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);
|
this->door_command(data::DOOR_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,17 +557,18 @@ namespace ratgdo {
|
||||||
|
|
||||||
void RATGDOComponent::toggle_door()
|
void RATGDOComponent::toggle_door()
|
||||||
{
|
{
|
||||||
if (*this->door_state == DoorState::OPENING) {
|
|
||||||
return; // gets ignored by opener
|
|
||||||
}
|
|
||||||
|
|
||||||
this->door_command(data::DOOR_TOGGLE);
|
this->door_command(data::DOOR_TOGGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::door_move_to_position(float position)
|
void RATGDOComponent::door_move_to_position(float position)
|
||||||
{
|
{
|
||||||
if (*this->door_state == DoorState::OPENING || *this->door_state == DoorState::CLOSING) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,7 +590,7 @@ namespace ratgdo {
|
||||||
|
|
||||||
this->door_command(delta > 0 ? data::DOOR_OPEN : data::DOOR_CLOSE);
|
this->door_command(delta > 0 ? data::DOOR_OPEN : data::DOOR_CLOSE);
|
||||||
set_timeout("move_to_position", operation_time, [=] {
|
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");
|
ESP_LOGD(TAG, "Cancelling position callbacks");
|
||||||
cancel_timeout("move_to_position");
|
cancel_timeout("move_to_position");
|
||||||
cancel_retry("position_sync_while_moving");
|
cancel_retry("position_sync_while_moving");
|
||||||
cancel_timeout("door_status_update");
|
|
||||||
|
|
||||||
this->door_start_moving = 0;
|
this->door_start_moving = 0;
|
||||||
this->door_start_position = DOOR_POSITION_UNKNOWN;
|
this->door_start_position = DOOR_POSITION_UNKNOWN;
|
||||||
|
@ -603,11 +611,39 @@ namespace ratgdo {
|
||||||
{
|
{
|
||||||
data |= (1 << 16); // button 1 ?
|
data |= (1 << 16); // button 1 ?
|
||||||
data |= (1 << 8); // button press
|
data |= (1 << 8); // button press
|
||||||
this->send_command(Command::DOOR_ACTION, data, false);
|
this->send_command(Command::DOOR_ACTION, data, false, [=]() {
|
||||||
set_timeout(200, [=] {
|
set_timeout(100, [=] {
|
||||||
auto data2 = data & ~(1 << 8); // button release
|
auto data2 = data & ~(1 << 8); // button release
|
||||||
this->send_command(Command::DOOR_ACTION, data2);
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RATGDOComponent::light_on()
|
void RATGDOComponent::light_on()
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "SoftwareSerial.h" // Using espsoftwareserial https://github.com/plerup/espsoftwareserial
|
#include "SoftwareSerial.h" // Using espsoftwareserial https://github.com/plerup/espsoftwareserial
|
||||||
|
#include "callbacks.h"
|
||||||
#include "enum.h"
|
#include "enum.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/gpio.h"
|
#include "esphome/core/gpio.h"
|
||||||
|
@ -124,6 +125,9 @@ namespace ratgdo {
|
||||||
observable<ButtonState> button_state { ButtonState::UNKNOWN };
|
observable<ButtonState> button_state { ButtonState::UNKNOWN };
|
||||||
observable<MotionState> motion_state { MotionState::UNKNOWN };
|
observable<MotionState> motion_state { MotionState::UNKNOWN };
|
||||||
|
|
||||||
|
OnceCallbacks<void(DoorState)> door_state_received;
|
||||||
|
OnceCallbacks<void()> command_sent;
|
||||||
|
|
||||||
observable<bool> sync_failed { false };
|
observable<bool> sync_failed { false };
|
||||||
|
|
||||||
void set_output_gdo_pin(InternalGPIOPin* pin) { this->output_gdo_pin_ = pin; }
|
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);
|
uint16_t decode_packet(const WirePacket& packet);
|
||||||
void obstruction_loop();
|
void obstruction_loop();
|
||||||
void send_command(Command command, uint32_t data = 0, bool increment = true);
|
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();
|
bool transmit_packet();
|
||||||
void encode_packet(Command command, uint32_t data, bool increment, WirePacket& packet);
|
void encode_packet(Command command, uint32_t data, bool increment, WirePacket& packet);
|
||||||
void print_packet(const WirePacket& packet) const;
|
void print_packet(const WirePacket& packet) const;
|
||||||
|
@ -145,6 +150,7 @@ namespace ratgdo {
|
||||||
|
|
||||||
// door
|
// door
|
||||||
void door_command(uint32_t data);
|
void door_command(uint32_t data);
|
||||||
|
void ensure_door_command(uint32_t data, uint32_t delay = 1500);
|
||||||
void toggle_door();
|
void toggle_door();
|
||||||
void open_door();
|
void open_door();
|
||||||
void close_door();
|
void close_door();
|
||||||
|
|
Loading…
Reference in New Issue