2023-06-05 17:12:51 +00:00
|
|
|
/************************************
|
|
|
|
* Rage
|
|
|
|
* Against
|
|
|
|
* The
|
|
|
|
* Garage
|
|
|
|
* Door
|
|
|
|
* Opener
|
|
|
|
*
|
|
|
|
* Copyright (C) 2022 Paul Wieland
|
|
|
|
*
|
|
|
|
* GNU GENERAL PUBLIC LICENSE
|
|
|
|
************************************/
|
|
|
|
|
|
|
|
#include "ratgdo.h"
|
2023-06-07 15:07:15 +00:00
|
|
|
#include "ratgdo_state.h"
|
2023-06-07 14:51:22 +00:00
|
|
|
|
2023-06-05 17:12:51 +00:00
|
|
|
#include "esphome/core/log.h"
|
|
|
|
|
2023-06-05 17:13:01 +00:00
|
|
|
namespace esphome {
|
|
|
|
namespace ratgdo {
|
|
|
|
|
2023-06-05 18:56:03 +00:00
|
|
|
static const char* const TAG = "ratgdo";
|
2023-06-26 19:41:31 +00:00
|
|
|
static const int SYNC_DELAY = 1000;
|
2023-06-25 15:45:37 +00:00
|
|
|
//
|
|
|
|
// MAX_CODES_WITHOUT_FLASH_WRITE is a bit of a guess
|
|
|
|
// since we write the flash at most every every 5s
|
|
|
|
//
|
|
|
|
// We want the rolling counter to be high enough that the
|
|
|
|
// GDO will accept the command after an unexpected reboot
|
|
|
|
// that did not save the counter to flash in time which
|
|
|
|
// results in the rolling counter being behind what the GDO
|
|
|
|
// expects.
|
|
|
|
//
|
2023-06-26 19:41:31 +00:00
|
|
|
static const uint8_t MAX_CODES_WITHOUT_FLASH_WRITE = 10;
|
2023-06-05 21:26:28 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void IRAM_ATTR HOT RATGDOStore::isr_obstruction(RATGDOStore* arg)
|
2023-06-05 21:26:28 +00:00
|
|
|
{
|
2023-06-06 00:59:20 +00:00
|
|
|
if (arg->input_obst.digital_read()) {
|
2023-07-03 02:44:34 +00:00
|
|
|
arg->last_obstruction_high = millis();
|
2023-06-05 21:26:28 +00:00
|
|
|
} else {
|
2023-07-03 02:44:34 +00:00
|
|
|
arg->obstruction_low_count++;
|
2023-06-05 21:26:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-06 01:06:12 +00:00
|
|
|
void RATGDOComponent::setup()
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->rolling_code_counter_pref_ = global_preferences->make_preference<int>(734874333U);
|
2023-07-01 14:13:38 +00:00
|
|
|
uint32_t rolling_code_counter = 0;
|
2023-07-03 02:44:34 +00:00
|
|
|
this->rolling_code_counter_pref_.load(&rolling_code_counter);
|
|
|
|
this->rolling_code_counter = rolling_code_counter;
|
2023-07-01 14:13:38 +00:00
|
|
|
// observers are subscribed in the setup() of children defer notify until after setup()
|
2023-07-03 02:44:34 +00:00
|
|
|
defer([=] { this->rolling_code_counter.notify(); });
|
2023-07-01 14:13:38 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
this->opening_duration_pref_ = global_preferences->make_preference<float>(734874334U);
|
2023-07-01 14:13:38 +00:00
|
|
|
float opening_duration = 0;
|
2023-07-03 02:44:34 +00:00
|
|
|
this->opening_duration_pref_.load(&opening_duration);
|
|
|
|
this->set_opening_duration(opening_duration);
|
|
|
|
defer([=] { this->opening_duration.notify(); });
|
2023-06-25 23:03:39 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
this->closing_duration_pref_ = global_preferences->make_preference<float>(734874335U);
|
2023-07-01 14:13:38 +00:00
|
|
|
float closing_duration = 0;
|
2023-07-03 02:44:34 +00:00
|
|
|
this->closing_duration_pref_.load(&closing_duration);
|
|
|
|
this->set_closing_duration(closing_duration);
|
|
|
|
defer([=] { this->closing_duration.notify(); });
|
2023-06-06 00:57:06 +00:00
|
|
|
|
2023-06-06 01:06:12 +00:00
|
|
|
this->output_gdo_pin_->setup();
|
|
|
|
this->input_gdo_pin_->setup();
|
|
|
|
this->input_obst_pin_->setup();
|
2023-06-05 18:56:03 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
this->isr_store_.input_obst = this->input_obst_pin_->to_isr();
|
2023-06-05 19:39:46 +00:00
|
|
|
|
2023-06-06 01:29:06 +00:00
|
|
|
this->output_gdo_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
|
|
|
this->input_gdo_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
|
2023-06-06 01:06:12 +00:00
|
|
|
this->input_obst_pin_->pin_mode(gpio::FLAG_INPUT);
|
2023-06-05 23:07:10 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
this->sw_serial_.begin(9600, SWSERIAL_8N1, this->input_gdo_pin_->get_pin(), this->output_gdo_pin_->get_pin(), true);
|
2023-06-06 01:29:06 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
this->input_obst_pin_->attach_interrupt(RATGDOStore::isr_obstruction, &this->isr_store_, gpio::INTERRUPT_ANY_EDGE);
|
2023-06-05 19:39:46 +00:00
|
|
|
|
2023-06-10 12:58:51 +00:00
|
|
|
ESP_LOGV(TAG, "Syncing rolling code counter after reboot...");
|
2023-06-24 22:10:41 +00:00
|
|
|
|
|
|
|
// many things happening at startup, use some delay for sync
|
2023-06-26 19:41:31 +00:00
|
|
|
set_timeout(SYNC_DELAY, [=] { this->sync(); });
|
2023-06-06 01:06:12 +00:00
|
|
|
}
|
2023-06-05 18:56:03 +00:00
|
|
|
|
2023-06-06 01:06:12 +00:00
|
|
|
void RATGDOComponent::loop()
|
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
this->obstruction_loop();
|
|
|
|
this->gdo_state_loop();
|
2023-06-06 01:06:12 +00:00
|
|
|
}
|
2023-06-05 18:26:26 +00:00
|
|
|
|
2023-06-06 01:37:29 +00:00
|
|
|
void RATGDOComponent::dump_config()
|
|
|
|
{
|
2023-06-06 01:37:27 +00:00
|
|
|
ESP_LOGCONFIG(TAG, "Setting up RATGDO...");
|
2023-06-06 02:36:52 +00:00
|
|
|
LOG_PIN(" Output GDO Pin: ", this->output_gdo_pin_);
|
|
|
|
LOG_PIN(" Input GDO Pin: ", this->input_gdo_pin_);
|
|
|
|
LOG_PIN(" Input Obstruction Pin: ", this->input_obst_pin_);
|
2023-07-03 02:44:34 +00:00
|
|
|
ESP_LOGCONFIG(TAG, " Rolling Code Counter: %d", *this->rolling_code_counter);
|
|
|
|
ESP_LOGCONFIG(TAG, " Remote ID: %d", this->remote_id_);
|
2023-06-06 01:37:27 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
uint16_t RATGDOComponent::decode_packet(const WirePacket& packet)
|
2023-06-06 01:06:12 +00:00
|
|
|
{
|
2023-06-09 23:33:16 +00:00
|
|
|
uint32_t rolling = 0;
|
|
|
|
uint64_t fixed = 0;
|
|
|
|
uint32_t data = 0;
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
decode_wireline(packet, &rolling, &fixed, &data);
|
2023-06-09 23:33:16 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
uint16_t cmd = ((fixed >> 24) & 0xf00) | (data & 0xff);
|
2023-06-24 20:38:44 +00:00
|
|
|
data &= ~0xf000; // clear parity nibble
|
2023-07-03 16:47:00 +00:00
|
|
|
|
|
|
|
Command cmd_enum = to_Command(cmd, Command::UNKNOWN);
|
2023-06-09 23:33:16 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
if ((fixed & 0xfffffff) == this->remote_id_) { // my commands
|
2023-07-01 14:36:30 +00:00
|
|
|
ESP_LOGV(TAG, "[%ld] received mine: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
|
2023-07-03 16:47:00 +00:00
|
|
|
return static_cast<uint16_t>(Command::UNKNOWN);
|
2023-06-24 20:38:44 +00:00
|
|
|
} else {
|
2023-07-01 14:36:30 +00:00
|
|
|
ESP_LOGV(TAG, "[%ld] received rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
|
2023-06-24 20:38:44 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
uint8_t nibble = (data >> 8) & 0xff;
|
|
|
|
uint8_t byte1 = (data >> 16) & 0xff;
|
|
|
|
uint8_t byte2 = (data >> 24) & 0xff;
|
2023-06-09 23:33:16 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
ESP_LOGV(TAG, "cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, Command_to_string(cmd_enum), byte2, byte1, nibble);
|
2023-06-24 20:38:44 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
if (cmd == Command::STATUS) {
|
2023-06-25 23:03:39 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
auto door_state = to_DoorState(nibble, DoorState::UNKNOWN);
|
2023-07-03 02:44:34 +00:00
|
|
|
auto prev_door_state = *this->door_state;
|
2023-06-25 23:03:39 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::OPENING && prev_door_state == DoorState::CLOSED) {
|
2023-07-03 02:44:34 +00:00
|
|
|
this->start_opening = millis();
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::OPEN && prev_door_state == DoorState::OPENING) {
|
2023-07-03 02:44:34 +00:00
|
|
|
if (this->start_opening > 0) {
|
|
|
|
auto duration = (millis() - this->start_opening) / 1000;
|
|
|
|
duration = *this->opening_duration > 0 ? (duration + *this->opening_duration) / 2 : duration;
|
|
|
|
this->set_opening_duration(round(duration * 10) / 10);
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::CLOSING && prev_door_state == DoorState::OPEN) {
|
2023-07-03 02:44:34 +00:00
|
|
|
this->start_closing = millis();
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::CLOSED && prev_door_state == DoorState::CLOSING) {
|
2023-07-03 02:44:34 +00:00
|
|
|
if (this->start_closing > 0) {
|
|
|
|
auto duration = (millis() - this->start_closing) / 1000;
|
|
|
|
duration = *this->closing_duration > 0 ? (duration + *this->closing_duration) / 2 : duration;
|
|
|
|
this->set_closing_duration(round(duration * 10) / 10);
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::STOPPED) {
|
2023-07-03 02:44:34 +00:00
|
|
|
this->start_opening = -1;
|
|
|
|
this->start_closing = -1;
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::OPEN) {
|
2023-07-03 02:44:34 +00:00
|
|
|
this->door_position = 1.0;
|
2023-07-03 16:47:00 +00:00
|
|
|
} else if (door_state == DoorState::CLOSED) {
|
2023-07-03 02:44:34 +00:00
|
|
|
this->door_position = 0.0;
|
2023-06-25 23:03:39 +00:00
|
|
|
} else {
|
2023-07-03 02:44:34 +00:00
|
|
|
if (*this->closing_duration == 0 || *this->opening_duration == 0 || *this->door_position == DOOR_POSITION_UNKNOWN) {
|
|
|
|
this->door_position = 0.5; // best guess
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::OPENING && !this->moving_to_position) {
|
2023-07-03 02:44:34 +00:00
|
|
|
this->position_sync_while_opening(1.0 - *this->door_position);
|
|
|
|
this->moving_to_position = true;
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::CLOSING && !this->moving_to_position) {
|
2023-07-03 02:44:34 +00:00
|
|
|
this->position_sync_while_closing(*this->door_position);
|
|
|
|
this->moving_to_position = true;
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::OPEN || door_state == DoorState::CLOSED || door_state == DoorState::STOPPED) {
|
2023-07-03 02:44:34 +00:00
|
|
|
this->cancel_position_sync_callbacks();
|
2023-06-24 20:38:44 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
this->door_state = door_state;
|
2023-07-03 16:47:00 +00:00
|
|
|
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
|
|
|
|
this->motor_state = MotorState::OFF; // when the status message is read, reset motor state to 0|off
|
2023-07-03 02:44:34 +00:00
|
|
|
// this->obstruction_state = static_cast<ObstructionState>((byte1 >> 6) & 1);
|
2023-06-25 23:03:39 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
if (door_state == DoorState::CLOSED && door_state != prev_door_state) {
|
|
|
|
this->transmit(Command::GET_OPENINGS);
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ESP_LOGD(TAG, "Status: door=%s light=%s lock=%s",
|
2023-07-03 16:47:00 +00:00
|
|
|
DoorState_to_string(*this->door_state),
|
|
|
|
LightState_to_string(*this->light_state),
|
|
|
|
LockState_to_string(*this->lock_state));
|
|
|
|
} else if (cmd == Command::LIGHT) {
|
2023-06-24 20:38:44 +00:00
|
|
|
if (nibble == 0) {
|
2023-07-03 16:47:00 +00:00
|
|
|
this->light_state = LightState::OFF;
|
2023-06-24 20:38:44 +00:00
|
|
|
} else if (nibble == 1) {
|
2023-07-03 16:47:00 +00:00
|
|
|
this->light_state = LightState::ON;
|
2023-06-24 20:38:44 +00:00
|
|
|
} else if (nibble == 2) { // toggle
|
2023-07-03 02:44:34 +00:00
|
|
|
this->light_state = light_state_toggle(*this->light_state);
|
2023-06-10 00:40:09 +00:00
|
|
|
}
|
2023-06-25 23:03:39 +00:00
|
|
|
ESP_LOGD(TAG, "Light: action=%s state=%s",
|
2023-06-24 20:38:44 +00:00
|
|
|
nibble == 0 ? "OFF" : nibble == 1 ? "ON"
|
|
|
|
: "TOGGLE",
|
2023-07-03 16:47:00 +00:00
|
|
|
LightState_to_string(*this->light_state));
|
|
|
|
} else if (cmd == Command::MOTOR_ON) {
|
|
|
|
this->motor_state = MotorState::ON;
|
|
|
|
ESP_LOGD(TAG, "Motor: state=%s", MotorState_to_string(*this->motor_state));
|
|
|
|
} else if (cmd == Command::OPEN) {
|
|
|
|
this->button_state = (byte1 & 1) == 1 ? ButtonState::PRESSED : ButtonState::RELEASED;
|
|
|
|
ESP_LOGD(TAG, "Open: button=%s", ButtonState_to_string(*this->button_state));
|
|
|
|
} else if (cmd == Command::OPENINGS) {
|
2023-06-09 23:33:16 +00:00
|
|
|
this->openings = (byte1 << 8) | byte2;
|
2023-07-01 14:13:38 +00:00
|
|
|
ESP_LOGD(TAG, "Openings: %d", *this->openings);
|
2023-07-03 16:47:00 +00:00
|
|
|
} else if (cmd == Command::MOTION) {
|
|
|
|
this->motion_state = MotionState::DETECTED;
|
|
|
|
if (*this->light_state == LightState::OFF) {
|
|
|
|
this->transmit(Command::GET_STATUS);
|
2023-06-24 20:38:44 +00:00
|
|
|
}
|
2023-07-03 16:47:00 +00:00
|
|
|
ESP_LOGD(TAG, "Motion: %s", MotionState_to_string(*this->motion_state));
|
|
|
|
} else if (cmd == Command::SET_TTC) {
|
|
|
|
auto seconds = (byte1 << 8) | byte2;
|
|
|
|
ESP_LOGD(TAG, "Time to close (TTC): %ds", seconds);
|
2023-06-06 00:57:06 +00:00
|
|
|
}
|
2023-07-03 16:47:00 +00:00
|
|
|
|
2023-06-09 23:33:16 +00:00
|
|
|
return cmd;
|
|
|
|
}
|
2023-06-05 18:56:03 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
void RATGDOComponent::encode_packet(Command command, uint32_t data, bool increment, WirePacket& packet)
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
auto cmd = static_cast<uint64_t>(command);
|
|
|
|
uint64_t fixed = ((cmd & ~0xff) << 24) | this->remote_id_;
|
|
|
|
uint32_t send_data = (data << 8) | (cmd & 0xff);
|
2023-06-24 20:38:44 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
ESP_LOGV(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), *this->rolling_code_counter, fixed, send_data);
|
|
|
|
encode_wireline(*this->rolling_code_counter, fixed, send_data, packet);
|
2023-06-24 20:38:44 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
this->print_packet(packet);
|
2023-06-24 20:38:44 +00:00
|
|
|
if (increment) {
|
2023-07-03 16:47:00 +00:00
|
|
|
this->increment_rolling_code_counter();
|
2023-06-05 18:57:01 +00:00
|
|
|
}
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 18:57:01 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::set_opening_duration(float duration)
|
2023-06-25 23:03:39 +00:00
|
|
|
{
|
|
|
|
ESP_LOGD(TAG, "Set opening duration: %.1fs", duration);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->opening_duration = duration;
|
|
|
|
this->opening_duration_pref_.save(&this->opening_duration);
|
2023-06-25 23:03:39 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
if (*this->closing_duration == 0 && duration != 0) {
|
|
|
|
this->set_closing_duration(duration);
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::set_closing_duration(float duration)
|
2023-06-25 23:03:39 +00:00
|
|
|
{
|
|
|
|
ESP_LOGD(TAG, "Set closing duration: %.1fs", duration);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->closing_duration = duration;
|
|
|
|
this->closing_duration_pref_.save(&this->closing_duration);
|
2023-06-25 23:03:39 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
if (*this->opening_duration == 0 && duration != 0) {
|
|
|
|
this->set_opening_duration(duration);
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::set_rolling_code_counter(uint32_t counter)
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-06-10 12:58:51 +00:00
|
|
|
ESP_LOGV(TAG, "Set rolling code counter to %d", counter);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->rolling_code_counter = counter;
|
|
|
|
this->rolling_code_counter_pref_.save(&this->rolling_code_counter);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-07 23:10:02 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::increment_rolling_code_counter(int delta)
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->rolling_code_counter = (*this->rolling_code_counter + delta) & 0xfffffff;
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 18:57:01 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::print_packet(const WirePacket& packet) const
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-06-10 12:58:51 +00:00
|
|
|
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]",
|
2023-07-01 14:13:38 +00:00
|
|
|
*this->rollingCodeCounter,
|
2023-07-03 02:44:34 +00:00
|
|
|
packet[0],
|
|
|
|
packet[1],
|
|
|
|
packet[2],
|
|
|
|
packet[3],
|
|
|
|
packet[4],
|
|
|
|
packet[5],
|
|
|
|
packet[6],
|
|
|
|
packet[7],
|
|
|
|
packet[8],
|
|
|
|
packet[9],
|
|
|
|
packet[10],
|
|
|
|
packet[11],
|
|
|
|
packet[12],
|
|
|
|
packet[13],
|
|
|
|
packet[14],
|
|
|
|
packet[15],
|
|
|
|
packet[16],
|
|
|
|
packet[17],
|
|
|
|
packet[18]);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 18:56:03 +00:00
|
|
|
|
2023-06-09 23:33:16 +00:00
|
|
|
/*************************** OBSTRUCTION DETECTION ***************************/
|
2023-06-06 01:06:12 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::obstruction_loop()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
long current_millis = millis();
|
|
|
|
static unsigned long last_millis = 0;
|
2023-06-09 23:33:16 +00:00
|
|
|
|
|
|
|
// the obstruction sensor has 3 states: clear (HIGH with LOW pulse every 7ms), obstructed (HIGH), asleep (LOW)
|
|
|
|
// the transitions between awake and asleep are tricky because the voltage drops slowly when falling asleep
|
|
|
|
// and is high without pulses when waking up
|
|
|
|
|
|
|
|
// If at least 3 low pulses are counted within 50ms, the door is awake, not obstructed and we don't have to check anything else
|
|
|
|
|
|
|
|
// Every 50ms
|
2023-07-03 02:44:34 +00:00
|
|
|
if (current_millis - last_millis > 50) {
|
2023-06-09 23:33:16 +00:00
|
|
|
// check to see if we got between 3 and 8 low pulses on the line
|
2023-07-03 02:44:34 +00:00
|
|
|
if (this->isr_store_.obstruction_low_count >= 3 && this->isr_store_.obstruction_low_count <= 8) {
|
2023-06-09 23:33:16 +00:00
|
|
|
// obstructionCleared();
|
2023-07-03 16:47:00 +00:00
|
|
|
this->obstruction_state = ObstructionState::CLEAR;
|
2023-06-09 23:33:16 +00:00
|
|
|
|
|
|
|
// if there have been no pulses the line is steady high or low
|
2023-07-03 02:44:34 +00:00
|
|
|
} else if (this->isr_store_.obstruction_low_count == 0) {
|
2023-06-09 23:33:16 +00:00
|
|
|
// if the line is high and the last high pulse was more than 70ms ago, then there is an obstruction present
|
2023-07-03 02:44:34 +00:00
|
|
|
if (this->input_obst_pin_->digital_read() && current_millis - this->isr_store_.last_obstruction_high > 70) {
|
2023-07-03 16:47:00 +00:00
|
|
|
this->obstruction_state = ObstructionState::OBSTRUCTED;
|
2023-06-09 23:33:16 +00:00
|
|
|
// obstructionDetected();
|
|
|
|
} else {
|
|
|
|
// asleep
|
|
|
|
}
|
2023-06-09 23:30:56 +00:00
|
|
|
}
|
2023-06-05 18:56:03 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
last_millis = current_millis;
|
|
|
|
this->isr_store_.obstruction_low_count = 0;
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-09 23:30:56 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::gdo_state_loop()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-06-23 23:18:32 +00:00
|
|
|
static bool reading_msg = false;
|
|
|
|
static uint32_t msg_start = 0;
|
|
|
|
static uint16_t byte_count = 0;
|
2023-07-03 02:44:34 +00:00
|
|
|
static WirePacket rx_packet;
|
2023-06-23 23:18:32 +00:00
|
|
|
|
|
|
|
if (!reading_msg) {
|
2023-07-03 16:47:00 +00:00
|
|
|
while (this->sw_serial_.available()) {
|
|
|
|
uint8_t ser_byte = this->sw_serial_.read();
|
2023-06-23 23:18:32 +00:00
|
|
|
if (ser_byte != 0x55 && ser_byte != 0x01 && ser_byte != 0x00) {
|
|
|
|
byte_count = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
msg_start = ((msg_start << 8) | ser_byte) & 0xffffff;
|
|
|
|
byte_count++;
|
|
|
|
|
|
|
|
// if we are at the start of a message, capture the next 16 bytes
|
|
|
|
if (msg_start == 0x550100) {
|
2023-07-03 02:44:34 +00:00
|
|
|
rx_packet[0] = 0x55;
|
|
|
|
rx_packet[1] = 0x01;
|
|
|
|
rx_packet[2] = 0x00;
|
2023-06-23 23:18:32 +00:00
|
|
|
|
|
|
|
reading_msg = true;
|
|
|
|
break;
|
|
|
|
}
|
2023-06-09 23:30:56 +00:00
|
|
|
}
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-23 23:18:32 +00:00
|
|
|
if (reading_msg) {
|
2023-07-03 16:47:00 +00:00
|
|
|
while (this->sw_serial_.available()) {
|
|
|
|
uint8_t ser_byte = this->sw_serial_.read();
|
2023-07-03 02:44:34 +00:00
|
|
|
rx_packet[byte_count] = ser_byte;
|
2023-06-23 23:18:32 +00:00
|
|
|
byte_count++;
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
if (byte_count == PACKET_LENGTH) {
|
2023-06-23 23:18:32 +00:00
|
|
|
reading_msg = false;
|
|
|
|
byte_count = 0;
|
2023-07-03 16:47:00 +00:00
|
|
|
this->decode_packet(rx_packet);
|
2023-06-23 23:18:32 +00:00
|
|
|
return;
|
2023-06-09 19:17:24 +00:00
|
|
|
}
|
2023-06-05 23:19:52 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 23:19:52 +00:00
|
|
|
|
2023-06-24 23:51:19 +00:00
|
|
|
void RATGDOComponent::query_status()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
transmit(Command::GET_STATUS);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 23:19:52 +00:00
|
|
|
|
2023-06-24 23:51:19 +00:00
|
|
|
void RATGDOComponent::query_openings()
|
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
transmit(Command::GET_OPENINGS);
|
2023-06-24 23:51:19 +00:00
|
|
|
}
|
|
|
|
|
2023-06-09 23:33:16 +00:00
|
|
|
/************************* DOOR COMMUNICATION *************************/
|
|
|
|
/*
|
|
|
|
* Transmit a message to the door opener over uart1
|
|
|
|
* The TX1 pin is controlling a transistor, so the logic is inverted
|
|
|
|
* A HIGH state on TX1 will pull the 12v line LOW
|
|
|
|
*
|
|
|
|
* The opener requires a specific duration low/high pulse before it will accept
|
|
|
|
* a message
|
|
|
|
*/
|
2023-07-03 16:47:00 +00:00
|
|
|
void RATGDOComponent::transmit(Command command, uint32_t data, bool increment)
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
WirePacket tx_packet;
|
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
this->encode_packet(command, data, increment, tx_packet);
|
2023-06-09 23:33:16 +00:00
|
|
|
this->output_gdo_pin_->digital_write(true); // pull the line high for 1305 micros so the
|
|
|
|
// door opener responds to the message
|
|
|
|
delayMicroseconds(1305);
|
|
|
|
this->output_gdo_pin_->digital_write(false); // bring the line low
|
|
|
|
|
|
|
|
delayMicroseconds(1260); // "LOW" pulse duration before the message start
|
2023-07-03 16:47:00 +00:00
|
|
|
this->sw_serial_.write(tx_packet, PACKET_LENGTH);
|
2023-06-24 23:02:50 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
this->save_rolling_code_counter();
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 17:12:51 +00:00
|
|
|
|
2023-06-09 23:33:16 +00:00
|
|
|
void RATGDOComponent::sync()
|
|
|
|
{
|
2023-06-26 19:41:31 +00:00
|
|
|
// increment rolling code counter by some amount in case we crashed without writing to flash the latest value
|
2023-07-03 02:44:34 +00:00
|
|
|
this->increment_rolling_code_counter(MAX_CODES_WITHOUT_FLASH_WRITE);
|
2023-06-26 19:41:31 +00:00
|
|
|
|
|
|
|
set_retry(
|
2023-07-01 14:13:38 +00:00
|
|
|
300, 10, [=](uint8_t r) {
|
2023-07-03 16:47:00 +00:00
|
|
|
if (*this->door_state != DoorState::UNKNOWN) { // have status
|
2023-07-01 14:13:38 +00:00
|
|
|
if (*this->openings != 0) { // have openings
|
2023-06-26 19:41:31 +00:00
|
|
|
return RetryResult::DONE;
|
|
|
|
} else {
|
2023-07-03 16:47:00 +00:00
|
|
|
this->transmit(Command::GET_OPENINGS);
|
2023-06-26 19:41:31 +00:00
|
|
|
return RetryResult::RETRY;
|
|
|
|
}
|
|
|
|
} else {
|
2023-07-03 16:47:00 +00:00
|
|
|
this->transmit(Command::GET_STATUS);
|
2023-06-26 19:41:31 +00:00
|
|
|
return RetryResult::RETRY;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
1.5f);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 23:19:52 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::open_door()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
if (*this->door_state == DoorState::OPENING) {
|
2023-06-25 23:03:39 +00:00
|
|
|
return; // gets ignored by opener
|
|
|
|
}
|
2023-07-03 02:44:34 +00:00
|
|
|
this->cancel_position_sync_callbacks();
|
2023-06-25 23:03:39 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
this->door_command(data::DOOR_OPEN);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 18:56:03 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::close_door()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
if (*this->door_state == DoorState::CLOSING || *this->door_state == DoorState::OPENING) {
|
2023-06-25 23:03:39 +00:00
|
|
|
return; // gets ignored by opener
|
|
|
|
}
|
2023-07-03 02:44:34 +00:00
|
|
|
this->cancel_position_sync_callbacks();
|
2023-06-25 23:03:39 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
this->door_command(data::DOOR_CLOSE);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 23:40:36 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::stop_door()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
if (*this->door_state != DoorState::OPENING && *this->door_state != DoorState::CLOSING) {
|
2023-06-25 23:03:39 +00:00
|
|
|
ESP_LOGW(TAG, "The door is not moving.");
|
2023-06-09 23:33:16 +00:00
|
|
|
return;
|
2023-06-05 18:56:03 +00:00
|
|
|
}
|
2023-07-03 16:47:00 +00:00
|
|
|
this->door_command(data::DOOR_STOP);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 18:56:03 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::toggle_door()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
if (*this->door_state == DoorState::OPENING) {
|
2023-06-25 23:03:39 +00:00
|
|
|
return; // gets ignored by opener
|
|
|
|
}
|
2023-07-03 02:44:34 +00:00
|
|
|
this->cancel_position_sync_callbacks();
|
2023-06-25 23:43:00 +00:00
|
|
|
|
2023-07-03 16:47:00 +00:00
|
|
|
this->door_command(data::DOOR_TOGGLE);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 23:40:36 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::position_sync_while_opening(float delta, float update_period)
|
2023-06-25 23:43:00 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
if (*this->opening_duration == 0) {
|
2023-06-25 23:03:39 +00:00
|
|
|
ESP_LOGW(TAG, "I don't know opening duration, ignoring position sync");
|
|
|
|
return;
|
|
|
|
}
|
2023-07-03 02:44:34 +00:00
|
|
|
auto updates = *this->opening_duration * 1000 * delta / update_period;
|
2023-06-25 23:43:00 +00:00
|
|
|
auto position_update = delta / updates;
|
2023-06-25 23:03:39 +00:00
|
|
|
auto count = int(updates);
|
2023-07-01 14:42:10 +00:00
|
|
|
ESP_LOGV(TAG, "[Opening] Position sync %d times: ", count);
|
2023-06-25 23:03:39 +00:00
|
|
|
// try to keep position in sync while door is moving
|
|
|
|
set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) {
|
2023-07-01 14:42:10 +00:00
|
|
|
ESP_LOGV(TAG, "[Opening] Position sync: %d: ", r);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->door_position = *this->door_position + position_update;
|
2023-06-25 23:03:39 +00:00
|
|
|
return RetryResult::RETRY;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::position_sync_while_closing(float delta, float update_period)
|
2023-06-25 23:43:00 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
if (*this->closing_duration == 0) {
|
2023-06-25 23:03:39 +00:00
|
|
|
ESP_LOGW(TAG, "I don't know closing duration, ignoring position sync");
|
|
|
|
return;
|
|
|
|
}
|
2023-07-03 02:44:34 +00:00
|
|
|
auto updates = *this->closing_duration * 1000 * delta / update_period;
|
2023-06-25 23:43:00 +00:00
|
|
|
auto position_update = delta / updates;
|
2023-06-25 23:03:39 +00:00
|
|
|
auto count = int(updates);
|
2023-07-01 14:42:10 +00:00
|
|
|
ESP_LOGV(TAG, "[Closing] Position sync %d times: ", count);
|
2023-06-25 23:03:39 +00:00
|
|
|
// try to keep position in sync while door is moving
|
|
|
|
set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) {
|
2023-07-01 14:42:10 +00:00
|
|
|
ESP_LOGV(TAG, "[Closing] Position sync: %d: ", r);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->door_position = *this->door_position - position_update;
|
2023-06-25 23:03:39 +00:00
|
|
|
return RetryResult::RETRY;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::door_move_to_position(float position)
|
2023-06-25 23:03:39 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
if (*this->door_state == DoorState::OPENING || *this->door_state == DoorState::CLOSING) {
|
2023-06-25 23:03:39 +00:00
|
|
|
ESP_LOGW(TAG, "The door is moving, ignoring.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
auto delta = position - *this->door_position;
|
2023-06-25 23:43:00 +00:00
|
|
|
if (delta == 0) {
|
|
|
|
ESP_LOGD(TAG, "Door is already at position %.2f", position);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
auto duration = delta > 0 ? *this->opening_duration : *this->closing_duration;
|
2023-06-25 23:43:00 +00:00
|
|
|
if (duration == 0) {
|
|
|
|
ESP_LOGW(TAG, "I don't know duration, ignoring move to position");
|
|
|
|
return;
|
|
|
|
}
|
2023-06-25 23:44:54 +00:00
|
|
|
|
2023-06-25 23:03:39 +00:00
|
|
|
if (delta > 0) { // open
|
2023-07-03 16:47:00 +00:00
|
|
|
this->door_command(data::DOOR_OPEN);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->position_sync_while_opening(delta);
|
2023-06-25 23:43:00 +00:00
|
|
|
} else { // close
|
2023-06-25 23:03:39 +00:00
|
|
|
delta = -delta;
|
2023-07-03 16:47:00 +00:00
|
|
|
this->door_command(data::DOOR_CLOSE);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->position_sync_while_closing(delta);
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
2023-06-25 23:43:00 +00:00
|
|
|
|
|
|
|
auto operation_time = duration * 1000 * delta;
|
2023-06-25 23:44:24 +00:00
|
|
|
ESP_LOGD(TAG, "Moving to position %.2f in %.1fs", position, operation_time / 1000.0);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->moving_to_position = true;
|
2023-06-25 23:43:00 +00:00
|
|
|
set_timeout("move_to_position", operation_time, [=] {
|
2023-07-03 16:47:00 +00:00
|
|
|
this->door_command(data::DOOR_STOP);
|
2023-07-03 02:44:34 +00:00
|
|
|
this->moving_to_position = false;
|
|
|
|
this->door_position = position;
|
2023-06-25 23:43:00 +00:00
|
|
|
});
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::cancel_position_sync_callbacks()
|
2023-06-25 23:43:00 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
if (this->moving_to_position) {
|
2023-06-25 23:03:39 +00:00
|
|
|
ESP_LOGD(TAG, "Cancelling position callbacks");
|
|
|
|
cancel_timeout("move_to_position");
|
|
|
|
cancel_retry("position_sync_while_moving");
|
|
|
|
}
|
2023-07-03 02:44:34 +00:00
|
|
|
moving_to_position = false;
|
2023-06-25 23:03:39 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::door_command(uint32_t data)
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-06-24 20:38:44 +00:00
|
|
|
data |= (1 << 16); // button 1 ?
|
|
|
|
data |= (1 << 8); // button press
|
2023-07-03 16:47:00 +00:00
|
|
|
this->transmit(Command::OPEN, data, false);
|
2023-06-24 22:10:41 +00:00
|
|
|
set_timeout(100, [=] {
|
|
|
|
auto data2 = data & ~(1 << 8); // button release
|
2023-07-03 16:47:00 +00:00
|
|
|
this->transmit(Command::OPEN, data2);
|
2023-06-24 22:10:41 +00:00
|
|
|
});
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-08 01:46:44 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::light_on()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
this->light_state = LightState::ON;
|
|
|
|
this->transmit(Command::LIGHT, data::LIGHT_ON);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 23:36:08 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::light_off()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
this->light_state = LightState::OFF;
|
|
|
|
this->transmit(Command::LIGHT, data::LIGHT_OFF);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 18:56:03 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::toggle_light()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->light_state = light_state_toggle(*this->light_state);
|
2023-07-03 16:47:00 +00:00
|
|
|
this->transmit(Command::LIGHT, data::LIGHT_TOGGLE);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-06 00:21:59 +00:00
|
|
|
|
2023-06-09 23:33:16 +00:00
|
|
|
// Lock functions
|
|
|
|
void RATGDOComponent::lock()
|
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
this->lock_state = LockState::LOCKED;
|
|
|
|
this->transmit(Command::LOCK, data::LOCK_ON);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-05 18:26:26 +00:00
|
|
|
|
2023-06-09 23:33:16 +00:00
|
|
|
void RATGDOComponent::unlock()
|
|
|
|
{
|
2023-07-03 16:47:00 +00:00
|
|
|
this->lock_state = LockState::UNLOCKED;
|
|
|
|
this->transmit(Command::LOCK, data::LOCK_OFF);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-06 01:06:12 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::toggle_lock()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->lock_state = lock_state_toggle(*this->lock_state);
|
2023-07-03 16:47:00 +00:00
|
|
|
this->transmit(Command::LOCK, data::LOCK_TOGGLE);
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-06 01:06:12 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
void RATGDOComponent::save_rolling_code_counter()
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->rolling_code_counter_pref_.save(&this->rolling_code_counter);
|
2023-06-25 15:45:37 +00:00
|
|
|
// Forcing a sync results in a soft reset if there are too many
|
|
|
|
// writes to flash in a short period of time. To avoid this,
|
|
|
|
// we have configured preferences to write every 5s
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-06 01:06:12 +00:00
|
|
|
|
2023-07-03 02:44:34 +00:00
|
|
|
LightState RATGDOComponent::get_light_state() const
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
return *this->light_state;
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-07-01 14:13:38 +00:00
|
|
|
|
|
|
|
void RATGDOComponent::subscribe_rolling_code_counter(std::function<void(uint32_t)>&& f)
|
|
|
|
{
|
|
|
|
// change update to children is defered until after component loop
|
|
|
|
// if multiple changes occur during component loop, only the last one is notified
|
2023-07-03 02:44:34 +00:00
|
|
|
this->rolling_code_counter.subscribe([=](uint32_t state) { defer("rolling_code_counter", [=] { f(state); }); });
|
2023-07-01 14:13:38 +00:00
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_opening_duration(std::function<void(float)>&& f)
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->opening_duration.subscribe([=](float state) { defer("opening_duration", [=] { f(state); }); });
|
2023-07-01 14:13:38 +00:00
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_closing_duration(std::function<void(float)>&& f)
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->closing_duration.subscribe([=](float state) { defer("closing_duration", [=] { f(state); }); });
|
2023-07-01 14:13:38 +00:00
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_openings(std::function<void(uint16_t)>&& f)
|
|
|
|
{
|
|
|
|
this->openings.subscribe([=](uint16_t state) { defer("openings", [=] { f(state); }); });
|
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_door_state(std::function<void(DoorState, float)>&& f)
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->door_state.subscribe([=](DoorState state) {
|
|
|
|
defer("door_state", [=] { f(state, *this->door_position); });
|
2023-07-01 14:13:38 +00:00
|
|
|
});
|
2023-07-03 02:44:34 +00:00
|
|
|
this->door_position.subscribe([=](float position) {
|
|
|
|
defer("door_state", [=] { f(*this->door_state, position); });
|
2023-07-01 14:13:38 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_light_state(std::function<void(LightState)>&& f)
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->light_state.subscribe([=](LightState state) { defer("light_state", [=] { f(state); }); });
|
2023-07-01 14:13:38 +00:00
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_lock_state(std::function<void(LockState)>&& f)
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->lock_state.subscribe([=](LockState state) { defer("lock_state", [=] { f(state); }); });
|
2023-07-01 14:13:38 +00:00
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_obstruction_state(std::function<void(ObstructionState)>&& f)
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->obstruction_state.subscribe([=](ObstructionState state) { defer("obstruction_state", [=] { f(state); }); });
|
2023-07-01 14:13:38 +00:00
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_motor_state(std::function<void(MotorState)>&& f)
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->motor_state.subscribe([=](MotorState state) { defer("motor_state", [=] { f(state); }); });
|
2023-07-01 14:13:38 +00:00
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_button_state(std::function<void(ButtonState)>&& f)
|
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->button_state.subscribe([=](ButtonState state) { defer("button_state", [=] { f(state); }); });
|
2023-07-01 14:13:38 +00:00
|
|
|
}
|
|
|
|
void RATGDOComponent::subscribe_motion_state(std::function<void(MotionState)>&& f)
|
2023-06-09 23:33:16 +00:00
|
|
|
{
|
2023-07-03 02:44:34 +00:00
|
|
|
this->motion_state.subscribe([=](MotionState state) { defer("motion_state", [=] { f(state); }); });
|
2023-06-09 23:33:16 +00:00
|
|
|
}
|
2023-06-07 14:45:49 +00:00
|
|
|
|
2023-06-09 23:33:16 +00:00
|
|
|
} // namespace ratgdo
|
2023-06-05 17:54:46 +00:00
|
|
|
} // namespace esphome
|