Files
esphome/components/ratgdo/secplus2.h
T
2026-07-12 12:56:02 +00:00

198 lines
5.4 KiB
C++

#pragma once
#include "callbacks.h"
#include "common.h"
#include "esphome/components/mqtt/mqtt_client.h"
#include "esphome/core/optional.h"
#include "esphome/core/preferences.h"
#include "observable.h"
#include "ratgdo_state.h"
#include "ratgdo_uart_esp32.h"
namespace esphome {
class Scheduler;
class InternalGPIOPin;
} // namespace esphome
namespace esphome::ratgdo {
class RATGDOComponent;
namespace secplus2 {
static const uint8_t PACKET_LENGTH = 19;
typedef uint8_t WirePacket[PACKET_LENGTH];
enum class CommandType : uint16_t {
UNKNOWN = 0x000,
GET_STATUS = 0x080,
STATUS = 0x081,
LOCK = 0x18c,
DOOR_ACTION = 0x280,
LIGHT = 0x281,
GET_OPENINGS = 0x48b,
OPENINGS = 0x48c // openings = (byte1<<8)+byte2
};
inline const char* CommandType_to_string(CommandType e) {
switch (e) {
case CommandType::UNKNOWN:
return "UNKNOWN";
case CommandType::GET_STATUS:
return "GET_STATUS";
case CommandType::STATUS:
return "STATUS";
case CommandType::LOCK:
return "LOCK";
case CommandType::DOOR_ACTION:
return "DOOR_ACTION";
case CommandType::LIGHT:
return "LIGHT";
case CommandType::GET_OPENINGS:
return "GET_OPENINGS";
case CommandType::OPENINGS:
return "OPENINGS";
default:
return "UNKNOWN";
}
}
inline CommandType to_CommandType(uint16_t t, CommandType unknown) {
switch (t) {
case static_cast<uint16_t>(CommandType::UNKNOWN):
return CommandType::UNKNOWN;
case static_cast<uint16_t>(CommandType::GET_STATUS):
return CommandType::GET_STATUS;
case static_cast<uint16_t>(CommandType::STATUS):
return CommandType::STATUS;
case static_cast<uint16_t>(CommandType::LOCK):
return CommandType::LOCK;
case static_cast<uint16_t>(CommandType::DOOR_ACTION):
return CommandType::DOOR_ACTION;
case static_cast<uint16_t>(CommandType::LIGHT):
return CommandType::LIGHT;
case static_cast<uint16_t>(CommandType::GET_OPENINGS):
return CommandType::GET_OPENINGS;
case static_cast<uint16_t>(CommandType::OPENINGS):
return CommandType::OPENINGS;
default:
return unknown;
}
}
inline bool operator==(const uint16_t cmd_i, const CommandType& cmd_e) {
return cmd_i == static_cast<uint16_t>(cmd_e);
}
inline bool operator==(const CommandType& cmd_e, const uint16_t cmd_i) {
return cmd_i == static_cast<uint16_t>(cmd_e);
}
enum class IncrementRollingCode {
NO,
YES,
};
struct Command {
CommandType type;
uint8_t nibble;
uint8_t byte1;
uint8_t byte2;
Command() : type(CommandType::UNKNOWN) {}
Command(CommandType type_, uint8_t nibble_ = 0, uint8_t byte1_ = 0,
uint8_t byte2_ = 0)
: type(type_), nibble(nibble_), byte1(byte1_), byte2(byte2_) {}
};
class Secplus2 {
public:
void setup(RATGDOComponent* ratgdo, Scheduler* scheduler,
InternalGPIOPin* rx_pin, InternalGPIOPin* tx_pin);
void loop();
void dump_config();
void on_shutdown();
void sync();
void light_action(LightAction action);
void lock_action(LockAction action);
void door_action(DoorAction action);
void query_status();
void query_openings();
protected:
void increment_rolling_code_counter(int delta = 1);
void set_rolling_code_counter(uint32_t counter);
void set_client_id(uint64_t client_id);
optional<Command> read_command();
void handle_command(const Command& cmd);
void send_command(Command cmd,
IncrementRollingCode increment = IncrementRollingCode::YES);
template <typename F>
void send_command(Command cmd, IncrementRollingCode increment, F&& on_sent) {
// Only register the callback if the command will be accepted.
// If transmit_pending is set the command will be dropped, and
// a stale callback would fire when the previous pending packet
// transmits -- executing logic (e.g. the second phase of a
// door_command) at the wrong time.
//
// Register before send_command() because transmit_packet() may
// succeed immediately and call on_command_sent_.trigger() inline.
if (this->flags_.transmit_pending) {
return;
}
this->on_command_sent_(std::forward<F>(on_sent));
this->send_command(cmd, increment);
}
void encode_packet(Command cmd, WirePacket& packet);
bool transmit_packet();
void door_command(DoorAction action);
void print_packet(const esphome::LogString* prefix,
const WirePacket& packet) const;
optional<Command> decode_packet(const WirePacket& packet) const;
void sync_helper(uint32_t start, uint32_t delay, uint8_t tries);
// 8-byte member first (may require 8-byte alignment on some 32-bit systems)
uint64_t client_id_{0x539};
// Pointers (4-byte aligned)
InternalGPIOPin* tx_pin_;
InternalGPIOPin* rx_pin_;
RATGDOComponent* ratgdo_;
Scheduler* scheduler_;
// 4-byte members
uint32_t transmit_pending_start_{0};
uint32_t rx_msg_start_{0};
uint32_t rx_last_read_{0};
// Larger structures
single_observable<uint32_t> rolling_code_counter_{0};
ESPPreferenceObject rolling_code_pref_;
ESPPreferenceObject client_id_pref_;
std::string mqtt_rolling_code_topic_;
std::string mqtt_client_id_topic_;
OnceCallbacks<void()> on_command_sent_;
RatgdoUART uart_;
// 19-byte arrays
WirePacket tx_packet_;
WirePacket rx_packet_;
// Small members at the end
uint16_t rx_byte_count_{0};
struct {
uint8_t transmit_pending : 1;
uint8_t rx_reading_msg : 1;
} flags_{0};
};
} // namespace secplus2
} // namespace esphome::ratgdo