remote_base: added helper class and schemas (#5169)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Sergey Dudanov 2023-11-13 21:55:36 +04:00 committed by GitHub
parent 0a4853ba7b
commit 00eedeb8b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 99 additions and 83 deletions

View file

@ -1,38 +1,37 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import ( from esphome.components import climate, sensor, remote_base
climate,
remote_transmitter,
remote_receiver,
sensor,
remote_base,
)
from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID
from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR
DEPENDENCIES = ["remote_transmitter"]
AUTO_LOAD = ["sensor", "remote_base"] AUTO_LOAD = ["sensor", "remote_base"]
CODEOWNERS = ["@glmnet"] CODEOWNERS = ["@glmnet"]
climate_ir_ns = cg.esphome_ns.namespace("climate_ir") climate_ir_ns = cg.esphome_ns.namespace("climate_ir")
ClimateIR = climate_ir_ns.class_( ClimateIR = climate_ir_ns.class_(
"ClimateIR", climate.Climate, cg.Component, remote_base.RemoteReceiverListener "ClimateIR",
climate.Climate,
cg.Component,
remote_base.RemoteReceiverListener,
remote_base.RemoteTransmittable,
) )
CLIMATE_IR_SCHEMA = climate.CLIMATE_SCHEMA.extend( CLIMATE_IR_SCHEMA = (
{ climate.CLIMATE_SCHEMA.extend(
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id( {
remote_transmitter.RemoteTransmitterComponent cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
), cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean, cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean, }
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), )
} .extend(cv.COMPONENT_SCHEMA)
).extend(cv.COMPONENT_SCHEMA) .extend(remote_base.REMOTE_TRANSMITTABLE_SCHEMA)
)
CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend( CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend(
{ {
cv.Optional(CONF_RECEIVER_ID): cv.use_id( cv.Optional(remote_base.CONF_RECEIVER_ID): cv.use_id(
remote_receiver.RemoteReceiverComponent remote_base.RemoteReceiverBase
), ),
} }
) )
@ -41,15 +40,11 @@ CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend(
async def register_climate_ir(var, config): async def register_climate_ir(var, config):
await cg.register_component(var, config) await cg.register_component(var, config)
await climate.register_climate(var, config) await climate.register_climate(var, config)
await remote_base.register_transmittable(var, config)
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL])) cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT])) cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
if remote_base.CONF_RECEIVER_ID in config:
await remote_base.register_listener(var, config)
if sensor_id := config.get(CONF_SENSOR): if sensor_id := config.get(CONF_SENSOR):
sens = await cg.get_variable(sensor_id) sens = await cg.get_variable(sensor_id)
cg.add(var.set_sensor(sens)) cg.add(var.set_sensor(sens))
if receiver_id := config.get(CONF_RECEIVER_ID):
receiver = await cg.get_variable(receiver_id)
cg.add(receiver.register_listener(var))
transmitter = await cg.get_variable(config[CONF_TRANSMITTER_ID])
cg.add(var.set_transmitter(transmitter))

View file

@ -18,7 +18,10 @@ namespace climate_ir {
Likewise to decode a IR into the AC state, implement Likewise to decode a IR into the AC state, implement
bool RemoteReceiverListener::on_receive(remote_base::RemoteReceiveData data) and return true bool RemoteReceiverListener::on_receive(remote_base::RemoteReceiveData data) and return true
*/ */
class ClimateIR : public climate::Climate, public Component, public remote_base::RemoteReceiverListener { class ClimateIR : public Component,
public climate::Climate,
public remote_base::RemoteReceiverListener,
public remote_base::RemoteTransmittable {
public: public:
ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f, ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f,
bool supports_dry = false, bool supports_fan_only = false, std::set<climate::ClimateFanMode> fan_modes = {}, bool supports_dry = false, bool supports_fan_only = false, std::set<climate::ClimateFanMode> fan_modes = {},
@ -35,9 +38,6 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) {
this->transmitter_ = transmitter;
}
void set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; } void set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; }
void set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } void set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }
void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; } void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }
@ -64,7 +64,6 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
std::set<climate::ClimateSwingMode> swing_modes_ = {}; std::set<climate::ClimateSwingMode> swing_modes_ = {};
std::set<climate::ClimatePreset> presets_ = {}; std::set<climate::ClimatePreset> presets_ = {};
remote_transmitter::RemoteTransmitterComponent *transmitter_;
sensor::Sensor *sensor_{nullptr}; sensor::Sensor *sensor_{nullptr};
}; };

View file

@ -102,11 +102,7 @@ void CoolixClimate::transmit_state() {
} }
} }
ESP_LOGV(TAG, "Sending coolix code: 0x%06" PRIX32, remote_state); ESP_LOGV(TAG, "Sending coolix code: 0x%06" PRIX32, remote_state);
this->transmit_<remote_base::CoolixProtocol>(remote_state);
auto transmit = this->transmitter_->transmit();
auto *data = transmit.get_data();
remote_base::CoolixProtocol().encode(data, remote_state);
transmit.perform();
} }
bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteReceiveData data) { bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteReceiveData data) {

View file

@ -3,7 +3,6 @@
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
#include "esphome/components/remote_base/remote_base.h" #include "esphome/components/remote_base/remote_base.h"
#include "esphome/components/remote_transmitter/remote_transmitter.h"
#include <IRSender.h> // arduino-heatpump library #include <IRSender.h> // arduino-heatpump library
namespace esphome { namespace esphome {
@ -11,14 +10,13 @@ namespace heatpumpir {
class IRSenderESPHome : public IRSender { class IRSenderESPHome : public IRSender {
public: public:
IRSenderESPHome(remote_transmitter::RemoteTransmitterComponent *transmitter) IRSenderESPHome(remote_base::RemoteTransmitterBase *transmitter) : IRSender(0), transmit_(transmitter->transmit()){};
: IRSender(0), transmit_(transmitter->transmit()){};
void setFrequency(int frequency) override; // NOLINT(readability-identifier-naming) void setFrequency(int frequency) override; // NOLINT(readability-identifier-naming)
void space(int space_length) override; void space(int space_length) override;
void mark(int mark_length) override; void mark(int mark_length) override;
protected: protected:
remote_transmitter::RemoteTransmitterComponent::TransmitCall transmit_; remote_base::RemoteTransmitterBase::TransmitCall transmit_;
}; };
} // namespace heatpumpir } // namespace heatpumpir

View file

@ -52,8 +52,9 @@ RemoteReceiverTrigger = ns.class_(
"RemoteReceiverTrigger", automation.Trigger, RemoteReceiverListener "RemoteReceiverTrigger", automation.Trigger, RemoteReceiverListener
) )
RemoteTransmitterDumper = ns.class_("RemoteTransmitterDumper") RemoteTransmitterDumper = ns.class_("RemoteTransmitterDumper")
RemoteTransmittable = ns.class_("RemoteTransmittable")
RemoteTransmitterActionBase = ns.class_( RemoteTransmitterActionBase = ns.class_(
"RemoteTransmitterActionBase", automation.Action "RemoteTransmitterActionBase", RemoteTransmittable, automation.Action
) )
RemoteReceiverBase = ns.class_("RemoteReceiverBase") RemoteReceiverBase = ns.class_("RemoteReceiverBase")
RemoteTransmitterBase = ns.class_("RemoteTransmitterBase") RemoteTransmitterBase = ns.class_("RemoteTransmitterBase")
@ -68,11 +69,30 @@ def templatize(value):
return cv.Schema(ret) return cv.Schema(ret)
REMOTE_LISTENER_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase),
}
)
REMOTE_TRANSMITTABLE_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase),
}
)
async def register_listener(var, config): async def register_listener(var, config):
receiver = await cg.get_variable(config[CONF_RECEIVER_ID]) receiver = await cg.get_variable(config[CONF_RECEIVER_ID])
cg.add(receiver.register_listener(var)) cg.add(receiver.register_listener(var))
async def register_transmittable(var, config):
transmitter_ = await cg.get_variable(config[CONF_TRANSMITTER_ID])
cg.add(var.set_transmitter(transmitter_))
def register_binary_sensor(name, type, schema): def register_binary_sensor(name, type, schema):
return BINARY_SENSOR_REGISTRY.register(name, type, schema) return BINARY_SENSOR_REGISTRY.register(name, type, schema)
@ -129,10 +149,9 @@ def validate_repeat(value):
BASE_REMOTE_TRANSMITTER_SCHEMA = cv.Schema( BASE_REMOTE_TRANSMITTER_SCHEMA = cv.Schema(
{ {
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase),
cv.Optional(CONF_REPEAT): validate_repeat, cv.Optional(CONF_REPEAT): validate_repeat,
} }
) ).extend(REMOTE_TRANSMITTABLE_SCHEMA)
def register_action(name, type_, schema): def register_action(name, type_, schema):
@ -143,9 +162,8 @@ def register_action(name, type_, schema):
def decorator(func): def decorator(func):
async def new_func(config, action_id, template_arg, args): async def new_func(config, action_id, template_arg, args):
transmitter = await cg.get_variable(config[CONF_TRANSMITTER_ID])
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
cg.add(var.set_parent(transmitter)) await register_transmittable(var, config)
if CONF_REPEAT in config: if CONF_REPEAT in config:
conf = config[CONF_REPEAT] conf = config[CONF_REPEAT]
template_ = await cg.templatable(conf[CONF_TIMES], args, cg.uint32) template_ = await cg.templatable(conf[CONF_TIMES], args, cg.uint32)
@ -1539,7 +1557,7 @@ MIDEA_SCHEMA = cv.Schema(
@register_binary_sensor("midea", MideaBinarySensor, MIDEA_SCHEMA) @register_binary_sensor("midea", MideaBinarySensor, MIDEA_SCHEMA)
def midea_binary_sensor(var, config): def midea_binary_sensor(var, config):
cg.add(var.set_code(config[CONF_CODE])) cg.add(var.set_data(config[CONF_CODE]))
@register_trigger("midea", MideaTrigger, MideaData) @register_trigger("midea", MideaTrigger, MideaData)

View file

@ -67,20 +67,7 @@ class MideaProtocol : public RemoteProtocol<MideaData> {
void dump(const MideaData &data) override; void dump(const MideaData &data) override;
}; };
class MideaBinarySensor : public RemoteReceiverBinarySensorBase { DECLARE_REMOTE_PROTOCOL(Midea)
public:
bool matches(RemoteReceiveData src) override {
auto data = MideaProtocol().decode(src);
return data.has_value() && data.value() == this->data_;
}
void set_code(const std::vector<uint8_t> &code) { this->data_ = code; }
protected:
MideaData data_;
};
using MideaTrigger = RemoteReceiverTrigger<MideaProtocol, MideaData>;
using MideaDumper = RemoteReceiverDumper<MideaProtocol, MideaData>;
template<typename... Ts> class MideaAction : public RemoteTransmitterActionBase<Ts...> { template<typename... Ts> class MideaAction : public RemoteTransmitterActionBase<Ts...> {
TEMPLATABLE_VALUE(std::vector<uint8_t>, code) TEMPLATABLE_VALUE(std::vector<uint8_t>, code)

View file

@ -15,6 +15,8 @@ struct RCSwitchData {
class RCSwitchBase { class RCSwitchBase {
public: public:
using ProtocolData = RCSwitchData;
RCSwitchBase() = default; RCSwitchBase() = default;
RCSwitchBase(uint32_t sync_high, uint32_t sync_low, uint32_t zero_high, uint32_t zero_low, uint32_t one_high, RCSwitchBase(uint32_t sync_high, uint32_t sync_low, uint32_t zero_high, uint32_t zero_low, uint32_t one_high,
uint32_t one_low, bool inverted); uint32_t one_low, bool inverted);
@ -213,7 +215,7 @@ class RCSwitchDumper : public RemoteReceiverDumperBase {
bool dump(RemoteReceiveData src) override; bool dump(RemoteReceiveData src) override;
}; };
using RCSwitchTrigger = RemoteReceiverTrigger<RCSwitchBase, RCSwitchData>; using RCSwitchTrigger = RemoteReceiverTrigger<RCSwitchBase>;
} // namespace remote_base } // namespace remote_base
} // namespace esphome } // namespace esphome

View file

@ -127,6 +127,14 @@ class RemoteTransmitterBase : public RemoteComponentBase {
this->temp_.reset(); this->temp_.reset();
return TransmitCall(this); return TransmitCall(this);
} }
template<typename Protocol>
void transmit(const typename Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) {
auto call = this->transmit();
Protocol().encode(call.get_data(), data);
call.set_send_times(send_times);
call.set_send_wait(send_wait);
call.perform();
}
protected: protected:
void send_(uint32_t send_times, uint32_t send_wait); void send_(uint32_t send_times, uint32_t send_wait);
@ -184,12 +192,13 @@ class RemoteReceiverBinarySensorBase : public binary_sensor::BinarySensorInitial
template<typename T> class RemoteProtocol { template<typename T> class RemoteProtocol {
public: public:
virtual void encode(RemoteTransmitData *dst, const T &data) = 0; using ProtocolData = T;
virtual optional<T> decode(RemoteReceiveData src) = 0; virtual void encode(RemoteTransmitData *dst, const ProtocolData &data) = 0;
virtual void dump(const T &data) = 0; virtual optional<ProtocolData> decode(RemoteReceiveData src) = 0;
virtual void dump(const ProtocolData &data) = 0;
}; };
template<typename T, typename D> class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase { template<typename T> class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase {
public: public:
RemoteReceiverBinarySensor() : RemoteReceiverBinarySensorBase() {} RemoteReceiverBinarySensor() : RemoteReceiverBinarySensorBase() {}
@ -201,13 +210,14 @@ template<typename T, typename D> class RemoteReceiverBinarySensor : public Remot
} }
public: public:
void set_data(D data) { data_ = data; } void set_data(typename T::ProtocolData data) { data_ = data; }
protected: protected:
D data_; typename T::ProtocolData data_;
}; };
template<typename T, typename D> class RemoteReceiverTrigger : public Trigger<D>, public RemoteReceiverListener { template<typename T>
class RemoteReceiverTrigger : public Trigger<typename T::ProtocolData>, public RemoteReceiverListener {
protected: protected:
bool on_receive(RemoteReceiveData src) override { bool on_receive(RemoteReceiveData src) override {
auto proto = T(); auto proto = T();
@ -220,28 +230,36 @@ template<typename T, typename D> class RemoteReceiverTrigger : public Trigger<D>
} }
}; };
template<typename... Ts> class RemoteTransmitterActionBase : public Action<Ts...> { class RemoteTransmittable {
public: public:
void set_parent(RemoteTransmitterBase *parent) { this->parent_ = parent; } RemoteTransmittable() {}
RemoteTransmittable(RemoteTransmitterBase *transmitter) : transmitter_(transmitter) {}
void set_transmitter(RemoteTransmitterBase *transmitter) { this->transmitter_ = transmitter; }
TEMPLATABLE_VALUE(uint32_t, send_times); protected:
TEMPLATABLE_VALUE(uint32_t, send_wait); template<typename Protocol>
void transmit_(const typename Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) {
this->transmitter_->transmit<Protocol>(data, send_times, send_wait);
}
RemoteTransmitterBase *transmitter_;
};
template<typename... Ts> class RemoteTransmitterActionBase : public RemoteTransmittable, public Action<Ts...> {
TEMPLATABLE_VALUE(uint32_t, send_times)
TEMPLATABLE_VALUE(uint32_t, send_wait)
protected:
void play(Ts... x) override { void play(Ts... x) override {
auto call = this->parent_->transmit(); auto call = this->transmitter_->transmit();
this->encode(call.get_data(), x...); this->encode(call.get_data(), x...);
call.set_send_times(this->send_times_.value_or(x..., 1)); call.set_send_times(this->send_times_.value_or(x..., 1));
call.set_send_wait(this->send_wait_.value_or(x..., 0)); call.set_send_wait(this->send_wait_.value_or(x..., 0));
call.perform(); call.perform();
} }
protected:
virtual void encode(RemoteTransmitData *dst, Ts... x) = 0; virtual void encode(RemoteTransmitData *dst, Ts... x) = 0;
RemoteTransmitterBase *parent_{};
}; };
template<typename T, typename D> class RemoteReceiverDumper : public RemoteReceiverDumperBase { template<typename T> class RemoteReceiverDumper : public RemoteReceiverDumperBase {
public: public:
bool dump(RemoteReceiveData src) override { bool dump(RemoteReceiveData src) override {
auto proto = T(); auto proto = T();
@ -254,9 +272,9 @@ template<typename T, typename D> class RemoteReceiverDumper : public RemoteRecei
}; };
#define DECLARE_REMOTE_PROTOCOL_(prefix) \ #define DECLARE_REMOTE_PROTOCOL_(prefix) \
using prefix##BinarySensor = RemoteReceiverBinarySensor<prefix##Protocol, prefix##Data>; \ using prefix##BinarySensor = RemoteReceiverBinarySensor<prefix##Protocol>; \
using prefix##Trigger = RemoteReceiverTrigger<prefix##Protocol, prefix##Data>; \ using prefix##Trigger = RemoteReceiverTrigger<prefix##Protocol>; \
using prefix##Dumper = RemoteReceiverDumper<prefix##Protocol, prefix##Data>; using prefix##Dumper = RemoteReceiverDumper<prefix##Protocol>;
#define DECLARE_REMOTE_PROTOCOL(prefix) DECLARE_REMOTE_PROTOCOL_(prefix) #define DECLARE_REMOTE_PROTOCOL(prefix) DECLARE_REMOTE_PROTOCOL_(prefix)
} // namespace remote_base } // namespace remote_base

View file

@ -3050,6 +3050,9 @@ remote_receiver:
on_coolix: on_coolix:
then: then:
delay: !lambda "return x.first + x.second;" delay: !lambda "return x.first + x.second;"
on_rc_switch:
then:
delay: !lambda "return uint32_t(x.code) + x.protocol;"
status_led: status_led:
pin: GPIO2 pin: GPIO2