add_learn

This commit is contained in:
mulcmu 2023-12-18 12:56:25 -05:00
parent 587ff4c7c5
commit 3744f571d3
8 changed files with 194 additions and 3 deletions

View File

@ -58,6 +58,13 @@ switch:
output: true
name: "Status obstruction"
entity_category: diagnostic
- platform: ratgdo
id: "${id_prefix}_learn"
type: learn
ratgdo_id: ${id_prefix}
name: "Learn"
icon: mdi:plus-box
entity_category: diagnostic
binary_sensor:
- platform: ratgdo

View File

@ -194,6 +194,16 @@ namespace ratgdo {
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
if (*this->learn_state != static_cast<LearnState>((byte2 >> 5) & 1)) {
this->learn_state = static_cast<LearnState>((byte2 >> 5) & 1);
if (*this->learn_state == LearnState::ACTIVE && this->learn_poll_status_) {
set_interval("learn_poll", 1000, [=] { this->send_command(Command::GET_STATUS); });
} else {
cancel_interval("learn_poll");
learn_poll_status_ = true;
}
}
if (this->obstruction_from_status_) {
// ESP_LOGD(TAG, "Obstruction: reading from byte2, bit2, status=%d", ((byte2 >> 2) & 1) == 1);
this->obstruction_state = static_cast<ObstructionState>((byte1 >> 6) & 1);
@ -207,10 +217,12 @@ namespace ratgdo {
this->send_command(Command::GET_OPENINGS);
}
ESP_LOGD(TAG, "Status: door=%s light=%s lock=%s",
ESP_LOGD(TAG, "Status: door=%s light=%s lock=%s learn=%s",
DoorState_to_string(*this->door_state),
LightState_to_string(*this->light_state),
LockState_to_string(*this->lock_state));
LockState_to_string(*this->lock_state),
LearnState_to_string(*this->learn_state));
} else if (cmd == Command::LIGHT) {
if (nibble == 0) {
this->light_state = LightState::OFF;
@ -251,6 +263,11 @@ namespace ratgdo {
auto seconds = (byte1 << 8) | byte2;
ESP_LOGD(TAG, "Time to close (TTC): %ds", seconds);
}
if (cmd == Command::LEARN) {
if (nibble == 1) { // LEARN sent from wall control, it will poll status every second
learn_poll_status_ = false;
}
}
return cmd;
}
@ -724,6 +741,30 @@ namespace ratgdo {
return *this->light_state;
}
// Learn functions
void RATGDOComponent::activate_learn()
{
// Send LEARN with nibble = 0 then nibble = 1 to mimic wall control learn button
learn_poll_status_ = true;
this->send_command(Command::LEARN, 0);
set_timeout(150, [=] { this->send_command(Command::LEARN, 1); });
set_timeout(500, [=] { this->send_command(Command::GET_STATUS); });
}
void RATGDOComponent::inactivate_learn()
{
// Send LEARN twice with nibble = 0 to inactivate learn and get status to update switch state
this->send_command(Command::LEARN, 0);
set_timeout(150, [=] { this->send_command(Command::LEARN, 0); });
set_timeout(500, [=] { this->send_command(Command::GET_STATUS); });
}
void RATGDOComponent::toggle_learn()
{
this->learn_state = learn_state_toggle(*this->learn_state);
// this->send_command(Command::learn, data::LOCK_TOGGLE);
}
void RATGDOComponent::subscribe_rolling_code_counter(std::function<void(uint32_t)>&& f)
{
// change update to children is defered until after component loop
@ -779,6 +820,10 @@ namespace ratgdo {
{
this->sync_failed.subscribe(std::move(f));
}
void RATGDOComponent::subscribe_learn_state(std::function<void(LearnState)>&& f)
{
this->learn_state.subscribe([=](LearnState state) { defer("learn_state", [=] { f(state); }); });
}
} // namespace ratgdo
} // namespace esphome

View File

@ -64,7 +64,7 @@ namespace ratgdo {
(PAIR_3, 0x0a0),
(PAIR_3_RESP, 0x0a1),
(LEARN_2, 0x181),
(LEARN, 0x181),
(LOCK, 0x18c),
(DOOR_ACTION, 0x280),
(LIGHT, 0x281),
@ -124,6 +124,7 @@ namespace ratgdo {
observable<MotorState> motor_state { MotorState::UNKNOWN };
observable<ButtonState> button_state { ButtonState::UNKNOWN };
observable<MotionState> motion_state { MotionState::UNKNOWN };
observable<LearnState> learn_state { LearnState::UNKNOWN };
OnceCallbacks<void(DoorState)> door_state_received;
OnceCallbacks<void()> command_sent;
@ -173,6 +174,10 @@ namespace ratgdo {
void lock();
void unlock();
void toggle_learn();
void activate_learn();
void inactivate_learn();
// button functionality
void query_status();
void query_openings();
@ -191,6 +196,7 @@ namespace ratgdo {
void subscribe_button_state(std::function<void(ButtonState)>&& f);
void subscribe_motion_state(std::function<void(MotionState)>&& f);
void subscribe_sync_failed(std::function<void(bool)>&& f);
void subscribe_learn_state(std::function<void(LearnState)>&& f);
protected:
// tx data
@ -203,6 +209,8 @@ namespace ratgdo {
bool obstruction_from_status_ { false };
bool learn_poll_status_ { true };
InternalGPIOPin* output_gdo_pin_;
InternalGPIOPin* input_gdo_pin_;
InternalGPIOPin* input_obst_pin_;

View File

@ -31,5 +31,19 @@ namespace ratgdo {
}
}
LearnState learn_state_toggle(LearnState state)
{
switch (state) {
case LearnState::ACTIVE:
return LearnState::INACTIVE;
case LearnState::INACTIVE:
return LearnState::ACTIVE;
// 2 and 3 appears sometimes
case LearnState::UNKNOWN:
default:
return LearnState::UNKNOWN;
}
}
} // namespace ratgdo
} // namespace esphome

View File

@ -64,5 +64,12 @@ namespace ratgdo {
(RELEASED, 1),
(UNKNOWN, 2))
/// Enum for learn states.
ENUM(LearnState, uint8_t,
(INACTIVE, 0),
(ACTIVE, 1),
(UNKNOWN, 2))
LearnState learn_state_toggle(LearnState state);
} // namespace ratgdo
} // namespace esphome

View File

@ -0,0 +1,35 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import switch
from esphome.const import CONF_ID
from .. import RATGDO_CLIENT_SCHMEA, ratgdo_ns, register_ratgdo_child
DEPENDENCIES = ["ratgdo"]
RATGDOSwitch = ratgdo_ns.class_("RATGDOSwitch", switch.Switch, cg.Component)
SwitchType = ratgdo_ns.enum("SwitchType")
CONF_TYPE = "type"
TYPES = {
"learn": SwitchType.RATGDO_LEARN,
}
CONFIG_SCHEMA = (
switch.switch_schema(RATGDOSwitch)
.extend(
{
cv.Required(CONF_TYPE): cv.enum(TYPES, lower=True),
}
)
.extend(RATGDO_CLIENT_SCHMEA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await switch.register_switch(var, config)
await cg.register_component(var, config)
cg.add(var.set_switch_type(config[CONF_TYPE]))
await register_ratgdo_child(var, config)

View File

@ -0,0 +1,46 @@
#include "ratgdo_switch.h"
#include "../ratgdo_state.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ratgdo {
static const char* const TAG = "ratgdo.switch";
void RATGDOSwitch::dump_config()
{
LOG_SWITCH("", "RATGDO Switch", this);
if (this->switch_type_ == SwitchType::RATGDO_LEARN) {
ESP_LOGCONFIG(TAG, " Type: Learn");
}
}
void RATGDOSwitch::setup()
{
if (this->switch_type_ == SwitchType::RATGDO_LEARN) {
this->parent_->subscribe_learn_state([=](LearnState state) {
this->on_learn_state(state);
});
}
}
void RATGDOSwitch::on_learn_state(LearnState state)
{
bool value = state == LearnState::ACTIVE;
this->state = value;
this->publish_state(value);
}
void RATGDOSwitch::write_state(bool state)
{
if (this->switch_type_ == SwitchType::RATGDO_LEARN) {
if (state) {
this->parent_->activate_learn();
} else {
this->parent_->inactivate_learn();
}
}
}
} // namespace ratgdo
} // namespace esphome

View File

@ -0,0 +1,29 @@
#pragma once
#include "../ratgdo.h"
#include "../ratgdo_state.h"
#include "esphome/components/switch/switch.h"
#include "esphome/core/component.h"
namespace esphome {
namespace ratgdo {
enum SwitchType {
RATGDO_LEARN
};
class RATGDOSwitch : public switch_::Switch, public RATGDOClient, public Component {
public:
void dump_config() override;
void setup() override;
void set_switch_type(SwitchType switch_type_) { this->switch_type_ = switch_type_; }
void on_learn_state(LearnState state);
void write_state(bool state) override;
protected:
SwitchType switch_type_;
};
} // namespace ratgdo
} // namespace esphome