From da8ba7c95a0d8a3c6e3f33d392d8e058aa81bf28 Mon Sep 17 00:00:00 2001 From: Djordje <6750655+DjordjeMandic@users.noreply.github.com> Date: Wed, 19 Feb 2025 01:04:40 +0100 Subject: [PATCH] [remote_base] NEC, binary sensor add suport for button hold / repeat codes --- .../components/remote_base/nec_protocol.cpp | 41 +++++++++++++++++++ esphome/components/remote_base/nec_protocol.h | 33 ++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/esphome/components/remote_base/nec_protocol.cpp b/esphome/components/remote_base/nec_protocol.cpp index 6be62b499d..256e350d0e 100644 --- a/esphome/components/remote_base/nec_protocol.cpp +++ b/esphome/components/remote_base/nec_protocol.cpp @@ -184,5 +184,46 @@ std::string NECProtocol::get_protocol_type_and_fields(const NECData &data) const return debug_message; } + +bool NECBinarySensor::matches(RemoteReceiveData src) { + auto proto = NECProtocol(); + auto res = proto.decode(src); + + if (!res.has_value()) { + return false; + } + + switch (res.value().type) { + case NECCodeType::FRAME_WITH_REPEATS: + // Set waiting to true only if currently not waiting and this is our desired frame. + this->waiting_for_repeat_code_ = (!this->waiting_for_repeat_code_ && (res.value() == this->data_)); + break; + + case NECCodeType::REPEATS_ONLY: + // Stay waiting only if we were already waiting and we got a valid repeat code. + this->waiting_for_repeat_code_ = (this->waiting_for_repeat_code_ && (res.value() == NEC_REPEAT_CODE_DATA)); + break; + + default: + this->waiting_for_repeat_code_ = false; + break; + } + + return this->waiting_for_repeat_code_; +} + +bool NECBinarySensor::on_receive(RemoteReceiveData src) { + if (!this->matches(src)) { + return false; + } + + this->publish_state(true); + this->set_timeout("repeat", this->repeat_timeout_ms_, [this]() { + this->waiting_for_repeat_code_ = false; + this->publish_state(false); + }); + + return true; +} } // namespace remote_base } // namespace esphome diff --git a/esphome/components/remote_base/nec_protocol.h b/esphome/components/remote_base/nec_protocol.h index 6ed251d2a3..4544b67348 100644 --- a/esphome/components/remote_base/nec_protocol.h +++ b/esphome/components/remote_base/nec_protocol.h @@ -50,7 +50,7 @@ struct NECData { }; }; -/// @brief Predefined single repeat code `NECData` returned by `NECProtocol::decode` +/// @brief Predefined single repeat code `NECData` returned by `NECProtocol::decode(RemoteReceiveData)` static const NECData NEC_REPEAT_CODE_DATA = {{0}, {0}, 1, NECCodeType::REPEATS_ONLY}; class NECProtocol : public RemoteProtocol { @@ -92,7 +92,36 @@ class NECProtocol : public RemoteProtocol { std::string get_protocol_type_and_fields(const NECData &data) const; }; -DECLARE_REMOTE_PROTOCOL(NEC) +class NECBinarySensor : public RemoteReceiverBinarySensor { + public: + NECBinarySensor() : RemoteReceiverBinarySensor() {} + + /// @brief Checks if the incoming data matches this sensor's expected NEC code. + /// @details + /// - If not currently waiting for a repeat code, only a matching new frame sets the waiting state. + /// - If waiting for a repeat code, only a valid repeat code keeps the sensor active. + /// - Otherwise, the sensor stops waiting. + /// @param[in] src The raw IR data to process. + /// @return True if the decoded data matches the expected code and is valid in the current state; false otherwise. + bool matches(RemoteReceiveData src) override; + /// @brief Called when new data arrives. If it matches, turn the sensor on and set a timeout to turn it off. + /// @details + /// - Internally calls `NECBinarySensor::matches(RemoteReceiveData)`. + /// - If a match occurs, publishes an "on" state and sets a timeout to revert to "off" if no repeat codes arrive. + /// @note The off state is published automatically when the timeout expires. + /// @param[in] src The raw IR data to process. + /// @return True if this data is handled and match found, false otherwise. + bool on_receive(RemoteReceiveData src) override; + + protected: + /// @brief The timeout in milliseconds to wait for repeat codes before turning the sensor off. + uint8_t repeat_timeout_ms_{130}; + /// @brief Indicates whether the sensor is currently expecting a repeat code. + bool waiting_for_repeat_code_{false}; +}; + +using NECTrigger = RemoteReceiverTrigger; +using NECDumper = RemoteReceiverDumper; template class NECAction : public RemoteTransmitterActionBase { public: