This commit is contained in:
Marius Muja 2024-01-16 15:22:57 -08:00
parent 554689e6c0
commit a93d8e407f
7 changed files with 131 additions and 34 deletions

View File

@ -19,6 +19,9 @@ struct QueryStatus{};
struct QueryOpenings{};
struct ActivateLearn {};
struct InactivateLearn {};
struct QueryPairedDevices { PairedDevice kind; };
struct QueryPairedDevicesAll {};
struct ClearPairedDevices { PairedDevice kind; };
// a poor man's sum-type, because C++
@ -32,6 +35,9 @@ public:
QueryOpenings query_openings;
ActivateLearn activate_learn;
InactivateLearn inactivate_learn;
QueryPairedDevices query_paired_devices;
QueryPairedDevicesAll query_paired_devices_all;
ClearPairedDevices clear_paired_devices;
} value;
enum class Tag {
@ -42,6 +48,9 @@ public:
query_openings,
activate_learn,
inactivate_learn,
query_paired_devices,
query_paired_devices_all,
clear_paired_devices,
} tag;
Args(GetRollingCodeCounter&& arg): tag(Tag::get_rolling_code_counter) {
@ -65,6 +74,15 @@ public:
Args(InactivateLearn&& arg): tag(Tag::inactivate_learn) {
value.inactivate_learn = std::move(arg);
}
Args(QueryPairedDevices&& arg): tag(Tag::query_paired_devices) {
value.query_paired_devices = std::move(arg);
}
Args(QueryPairedDevicesAll&& arg): tag(Tag::query_paired_devices_all) {
value.query_paired_devices_all = std::move(arg);
}
Args(ClearPairedDevices&& arg): tag(Tag::clear_paired_devices) {
value.clear_paired_devices = std::move(arg);
}
};

View File

@ -7,19 +7,46 @@ namespace esphome {
namespace ratgdo {
template <typename T>
class observable {
class distinct_observable;
template <typename T>
class observable_base {
public:
observable(const T& value)
: value_(value)
template <typename Observer>
void subscribe(Observer&& observer)
{
this->observers_.push_back(std::forward<Observer>(observer));
}
void notify(T value) const
{
for (const auto& observer : this->observers_) {
observer(value);
}
}
distinct_observable<T> distinct()
{
return std::make_shared(this);
}
private:
std::vector<std::function<void(T)>> observers_;
};
template <typename T>
class observable : public observable_base<T> {
public:
observable(const T& value) : value_(value) {}
template <typename U>
observable& operator=(U value)
{
if (value != this->value_) {
this->value_ = value;
this->notify();
this->notify(value);
}
return *this;
}
@ -27,22 +54,27 @@ namespace ratgdo {
T const* operator&() const { return &this->value_; }
T const& operator*() const { return this->value_; }
template <typename Observer>
void subscribe(Observer&& observer)
{
this->observers_.push_back(std::forward<Observer>(observer));
}
private:
T value_;
};
void notify() const
{
for (const auto& observer : this->observers_) {
observer(this->value_);
}
template <typename T>
class distinct_observable : public observable<T> {
public:
distinct_observable(std::shared_ptr<observable<T>> inner) : inner_(inner) {
inner.subscribe([=] (T value) {
if (value != this->value_) {
this->value_ = value;
this->notify(value);
}
});
}
private:
std::shared_ptr<observable<T>> inner_;
T value_;
std::vector<std::function<void(T)>> observers_;
};
} // namespace ratgdo

View File

@ -288,6 +288,11 @@ namespace ratgdo {
ESP_LOGD(TAG, "Time to close (TTC): %ds", ttc.seconds);
}
void RATGDOComponent::received(const BatteryState battery_state)
{
ESP_LOGD(TAG, "Battery state=%s", BatteryState_to_string(battery_state));
}
void RATGDOComponent::schedule_door_position_sync(float update_period)
{
ESP_LOG1(TAG, "Schedule position sync: delta %f, start position: %f, start moving: %d",
@ -379,10 +384,8 @@ namespace ratgdo {
}
}
void RATGDOComponent::query_status()
{
ESP_LOG2(TAG, "Query status action");
this->protocol_->call(QueryStatus{});
}
@ -391,6 +394,21 @@ namespace ratgdo {
this->protocol_->call(QueryOpenings{});
}
void RATGDOComponent::query_paired_devices()
{
this->protocol_->call(QueryPairedDevicesAll{});
}
void RATGDOComponent::query_paired_devices(PairedDevice kind)
{
this->protocol_->call(QueryPairedDevices{kind});
}
void RATGDOComponent::clear_paired_devices(PairedDevice kind)
{
this->protocol_->call(ClearPairedDevices{kind});
}
void RATGDOComponent::sync()
{
this->protocol_->sync();

View File

@ -106,6 +106,7 @@ namespace ratgdo {
void received(const Openings openings);
void received(const TimeToClose ttc);
void received(const PairedDeviceCount pdc);
void received(const BatteryState pdc);
// door
void door_toggle();

View File

@ -64,6 +64,13 @@ namespace ratgdo {
(RELEASED, 1),
(UNKNOWN, 2))
ENUM(BatteryState, uint8_t,
(UNKNOWN, 0),
(CHARGING, 0x6),
(FULL, 0x8))
/// Enum for learn states.
ENUM(LearnState, uint8_t,
(INACTIVE, 0),

View File

@ -145,22 +145,32 @@ namespace secplus2 {
{
using Tag = Args::Tag;
if (args.tag == Tag::query_status) {
this->send_command(Command{CommandType::GET_STATUS});
this->send_command(CommandType::GET_STATUS);
} else if (args.tag == Tag::query_openings) {
this->send_command(Command{CommandType::GET_OPENINGS});
this->send_command(CommandType::GET_OPENINGS);
} else if (args.tag == Tag::get_rolling_code_counter) {
return Result(RollingCodeCounter{std::addressof(this->rolling_code_counter_)});
} else if (args.tag == Tag::set_rolling_code_counter) {
this->set_rolling_code_counter(args.value.set_rolling_code_counter.counter);
} else if (args.tag == Tag::set_client_id) {
this->set_client_id(args.value.set_client_id.client_id);
} else if (args.tag == Tag::query_paired_devices) {
this->query_paired_devices(args.value.query_paired_devices.kind);
} else if (args.tag == Tag::query_paired_devices_all) {
this->query_paired_devices();
} else if (args.tag == Tag::clear_paired_devices) {
this->clear_paired_devices(args.value.clear_paired_devices.kind);
} else if (args.tag == Tag::activate_learn) {
this->activate_learn();
} else if (args.tag == Tag::inactivate_learn) {
this->inactivate_learn();
}
return {};
}
void Secplus2::door_command(DoorAction action)
{
this->send_command(Command(CommandType::DOOR_ACTION, static_cast<uint8_t>(action), 1, 1), false, [=]() {
this->send_command(Command(CommandType::DOOR_ACTION, static_cast<uint8_t>(action), 1, 1), IncrementRollingCode::NO, [=]() {
this->scheduler_->set_timeout(this->ratgdo_, "", 150, [=] {
this->send_command(Command(CommandType::DOOR_ACTION, static_cast<uint8_t>(action), 0, 1));
});
@ -197,7 +207,7 @@ namespace secplus2 {
void Secplus2::query_paired_devices(PairedDevice kind)
{
ESP_LOGD(TAG, "Query paired devices of type: %s", PairedDevice_to_string(kind));
this->send_command(CommandType::GET_PAIRED_DEVICES, static_cast<uint8_t>(kind));
this->send_command(Command{CommandType::GET_PAIRED_DEVICES, static_cast<uint8_t>(kind)});
}
// wipe devices from memory based on get paired devices nibble values
@ -208,14 +218,15 @@ namespace secplus2 {
}
ESP_LOGW(TAG, "Clear paired devices of type: %s", PairedDevice_to_string(kind));
if (kind == PairedDevice::ALL) {
this->scheduler_->set_timeout(this->ratgdo_, "", 200, [=] { this->send_command(CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(PairedDevice::REMOTE)-1); }); // wireless
this->scheduler_->set_timeout(this->ratgdo_, "", 400, [=] { this->send_command(CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(PairedDevice::KEYPAD)-1); }); // keypads
this->scheduler_->set_timeout(this->ratgdo_, "", 600, [=] { this->send_command(CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(PairedDevice::WALL_CONTROL)-1); }); // wall controls
this->scheduler_->set_timeout(this->ratgdo_, "", 800, [=] { this->send_command(CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(PairedDevice::ACCESSORY)-1); }); // accessories
this->scheduler_->set_timeout(this->ratgdo_, "", 200, [=] { this->send_command(Command{CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(PairedDevice::REMOTE)-1}); }); // wireless
this->scheduler_->set_timeout(this->ratgdo_, "", 400, [=] { this->send_command(Command{CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(PairedDevice::KEYPAD)-1}); }); // keypads
this->scheduler_->set_timeout(this->ratgdo_, "", 600, [=] { this->send_command(Command{CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(PairedDevice::WALL_CONTROL)-1}); }); // wall controls
this->scheduler_->set_timeout(this->ratgdo_, "", 800, [=] { this->send_command(Command{CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(PairedDevice::ACCESSORY)-1}); }); // accessories
this->scheduler_->set_timeout(this->ratgdo_, "", 1000, [=] { this->query_status(); });
this->scheduler_->set_timeout(this->ratgdo_, "", 1200, [=] { this->query_paired_devices(); });
} else {
this->send_command(CommandType::CLEAR_PAIRED_DEVICES, static_cast<uint8_t>(kind) - 1); // just requested device
uint8_t dev_kind = static_cast<uint8_t>(kind)-1;
this->send_command(Command{CommandType::CLEAR_PAIRED_DEVICES, dev_kind}); // just requested device
this->scheduler_->set_timeout(this->ratgdo_, "", 200, [=] { this->query_status(); });
this->scheduler_->set_timeout(this->ratgdo_, "", 400, [=] { this->query_paired_devices(kind); });
}
@ -371,20 +382,21 @@ namespace secplus2 {
auto learn_state = to_LearnState((cmd.byte2 >> 5) & 1, LearnState::UNKNOWN);
if (this->learn_state_ != learn_state) {
ESP_LOG1(TAG, "Learn state handle: %d", (int)this->learn_poll_status_);
if (learn_state == LearnState::ACTIVE && this->learn_poll_status_) {
this->scheduler_->set_timeout(this->ratgdo_, "learn_poll", 1000, [=] {
this->scheduler_->set_interval(this->ratgdo_, "learn_poll", 1000, [=] {
this->query_status();
});
} else {
this->scheduler_->cancel_timeout(this->ratgdo_, "learn_poll");
this->scheduler_->cancel_interval(this->ratgdo_, "learn_poll");
this->learn_poll_status_ = true;
}
if (learn_state == LearnState::INACTIVE) {
this->query_paired_devices();
}
this->learn_state_ = learn_state;
this->ratgdo_->received(learn_state);
}
this->ratgdo_->received(learn_state);
}
else if (cmd.type == CommandType::LIGHT) {
this->ratgdo_->received(to_LightAction(cmd.nibble, LightAction::UNKNOWN));
@ -427,16 +439,19 @@ namespace secplus2 {
this->learn_poll_status_ = false;
}
}
else if (cmd.type == CommandType::BATTERY_STATUS) {
this->ratgdo_->received(to_BatteryState(cmd.byte1, BatteryState::UNKNOWN));
}
ESP_LOG1(TAG, "Done handle command: %s", CommandType_to_string(cmd.type));
}
void Secplus2::send_command(Command command, bool increment)
void Secplus2::send_command(Command command, IncrementRollingCode increment)
{
ESP_LOG1(TAG, "Send command: %s, data: %02X%02X%02X", CommandType_to_string(command.type), command.byte2, command.byte1, command.nibble);
if (!this->transmit_pending_) { // have an untransmitted packet
this->encode_packet(command, this->tx_packet_);
if (increment) {
if (increment == IncrementRollingCode::YES) {
this->increment_rolling_code_counter();
}
} else {
@ -452,7 +467,7 @@ namespace secplus2 {
}
void Secplus2::send_command(Command command, bool increment, std::function<void()>&& on_sent)
void Secplus2::send_command(Command command, IncrementRollingCode increment, std::function<void()>&& on_sent)
{
this->command_sent_.then(on_sent);
this->send_command(command, increment);

View File

@ -30,6 +30,7 @@ namespace secplus2 {
(STATUS, 0x081),
(OBST_1, 0x084), // sent when an obstruction happens?
(OBST_2, 0x085), // sent when an obstruction happens?
(BATTERY_STATUS, 0x9d),
(PAIR_3, 0x0a0),
(PAIR_3_RESP, 0x0a1),
@ -61,6 +62,11 @@ namespace secplus2 {
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;
@ -93,8 +99,8 @@ namespace secplus2 {
optional<Command> read_command();
void handle_command(const Command& cmd);
void send_command(Command cmd, bool increment = true);
void send_command(Command cmd, bool increment, std::function<void()>&& on_sent);
void send_command(Command cmd, IncrementRollingCode increment = IncrementRollingCode::YES);
void send_command(Command cmd, IncrementRollingCode increment, std::function<void()>&& on_sent);
void encode_packet(Command cmd, WirePacket& packet);
bool transmit_packet();