mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	remote_base: added helper class and schemas (#5169)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -1,38 +1,37 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import ( | ||||
|     climate, | ||||
|     remote_transmitter, | ||||
|     remote_receiver, | ||||
|     sensor, | ||||
|     remote_base, | ||||
| ) | ||||
| from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID | ||||
| from esphome.components import climate, sensor, remote_base | ||||
| from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR | ||||
|  | ||||
| DEPENDENCIES = ["remote_transmitter"] | ||||
| AUTO_LOAD = ["sensor", "remote_base"] | ||||
| CODEOWNERS = ["@glmnet"] | ||||
|  | ||||
| climate_ir_ns = cg.esphome_ns.namespace("climate_ir") | ||||
| 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_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( | ||||
|     { | ||||
|         cv.Optional(CONF_RECEIVER_ID): cv.use_id( | ||||
|             remote_receiver.RemoteReceiverComponent | ||||
|         cv.Optional(remote_base.CONF_RECEIVER_ID): cv.use_id( | ||||
|             remote_base.RemoteReceiverBase | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
| @@ -41,15 +40,11 @@ CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend( | ||||
| async def register_climate_ir(var, config): | ||||
|     await cg.register_component(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_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): | ||||
|         sens = await cg.get_variable(sensor_id) | ||||
|         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)) | ||||
|   | ||||
| @@ -18,7 +18,10 @@ namespace climate_ir { | ||||
|     Likewise to decode a IR into the AC state, implement | ||||
|       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: | ||||
|   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 = {}, | ||||
| @@ -35,9 +38,6 @@ class ClimateIR : public climate::Climate, public Component, public remote_base: | ||||
|  | ||||
|   void setup() 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_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } | ||||
|   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::ClimatePreset> presets_ = {}; | ||||
|  | ||||
|   remote_transmitter::RemoteTransmitterComponent *transmitter_; | ||||
|   sensor::Sensor *sensor_{nullptr}; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -102,11 +102,7 @@ void CoolixClimate::transmit_state() { | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGV(TAG, "Sending coolix code: 0x%06" PRIX32, remote_state); | ||||
|  | ||||
|   auto transmit = this->transmitter_->transmit(); | ||||
|   auto *data = transmit.get_data(); | ||||
|   remote_base::CoolixProtocol().encode(data, remote_state); | ||||
|   transmit.perform(); | ||||
|   this->transmit_<remote_base::CoolixProtocol>(remote_state); | ||||
| } | ||||
|  | ||||
| bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteReceiveData data) { | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| #ifdef USE_ARDUINO | ||||
|  | ||||
| #include "esphome/components/remote_base/remote_base.h" | ||||
| #include "esphome/components/remote_transmitter/remote_transmitter.h" | ||||
| #include <IRSender.h>  // arduino-heatpump library | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -11,14 +10,13 @@ namespace heatpumpir { | ||||
|  | ||||
| class IRSenderESPHome : public IRSender { | ||||
|  public: | ||||
|   IRSenderESPHome(remote_transmitter::RemoteTransmitterComponent *transmitter) | ||||
|       : IRSender(0), transmit_(transmitter->transmit()){}; | ||||
|   IRSenderESPHome(remote_base::RemoteTransmitterBase *transmitter) : IRSender(0), transmit_(transmitter->transmit()){}; | ||||
|   void setFrequency(int frequency) override;  // NOLINT(readability-identifier-naming) | ||||
|   void space(int space_length) override; | ||||
|   void mark(int mark_length) override; | ||||
|  | ||||
|  protected: | ||||
|   remote_transmitter::RemoteTransmitterComponent::TransmitCall transmit_; | ||||
|   remote_base::RemoteTransmitterBase::TransmitCall transmit_; | ||||
| }; | ||||
|  | ||||
| }  // namespace heatpumpir | ||||
|   | ||||
| @@ -52,8 +52,9 @@ RemoteReceiverTrigger = ns.class_( | ||||
|     "RemoteReceiverTrigger", automation.Trigger, RemoteReceiverListener | ||||
| ) | ||||
| RemoteTransmitterDumper = ns.class_("RemoteTransmitterDumper") | ||||
| RemoteTransmittable = ns.class_("RemoteTransmittable") | ||||
| RemoteTransmitterActionBase = ns.class_( | ||||
|     "RemoteTransmitterActionBase", automation.Action | ||||
|     "RemoteTransmitterActionBase", RemoteTransmittable, automation.Action | ||||
| ) | ||||
| RemoteReceiverBase = ns.class_("RemoteReceiverBase") | ||||
| RemoteTransmitterBase = ns.class_("RemoteTransmitterBase") | ||||
| @@ -68,11 +69,30 @@ def templatize(value): | ||||
|     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): | ||||
|     receiver = await cg.get_variable(config[CONF_RECEIVER_ID]) | ||||
|     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): | ||||
|     return BINARY_SENSOR_REGISTRY.register(name, type, schema) | ||||
|  | ||||
| @@ -129,10 +149,9 @@ def validate_repeat(value): | ||||
|  | ||||
| BASE_REMOTE_TRANSMITTER_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase), | ||||
|         cv.Optional(CONF_REPEAT): validate_repeat, | ||||
|     } | ||||
| ) | ||||
| ).extend(REMOTE_TRANSMITTABLE_SCHEMA) | ||||
|  | ||||
|  | ||||
| def register_action(name, type_, schema): | ||||
| @@ -143,9 +162,8 @@ def register_action(name, type_, schema): | ||||
|  | ||||
|     def decorator(func): | ||||
|         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) | ||||
|             cg.add(var.set_parent(transmitter)) | ||||
|             await register_transmittable(var, config) | ||||
|             if CONF_REPEAT in config: | ||||
|                 conf = config[CONF_REPEAT] | ||||
|                 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) | ||||
| 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) | ||||
|   | ||||
| @@ -67,20 +67,7 @@ class MideaProtocol : public RemoteProtocol<MideaData> { | ||||
|   void dump(const MideaData &data) override; | ||||
| }; | ||||
|  | ||||
| class MideaBinarySensor : public RemoteReceiverBinarySensorBase { | ||||
|  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>; | ||||
| DECLARE_REMOTE_PROTOCOL(Midea) | ||||
|  | ||||
| template<typename... Ts> class MideaAction : public RemoteTransmitterActionBase<Ts...> { | ||||
|   TEMPLATABLE_VALUE(std::vector<uint8_t>, code) | ||||
|   | ||||
| @@ -15,6 +15,8 @@ struct RCSwitchData { | ||||
|  | ||||
| class RCSwitchBase { | ||||
|  public: | ||||
|   using ProtocolData = RCSwitchData; | ||||
|  | ||||
|   RCSwitchBase() = default; | ||||
|   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); | ||||
| @@ -213,7 +215,7 @@ class RCSwitchDumper : public RemoteReceiverDumperBase { | ||||
|   bool dump(RemoteReceiveData src) override; | ||||
| }; | ||||
|  | ||||
| using RCSwitchTrigger = RemoteReceiverTrigger<RCSwitchBase, RCSwitchData>; | ||||
| using RCSwitchTrigger = RemoteReceiverTrigger<RCSwitchBase>; | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -127,6 +127,14 @@ class RemoteTransmitterBase : public RemoteComponentBase { | ||||
|     this->temp_.reset(); | ||||
|     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: | ||||
|   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 { | ||||
|  public: | ||||
|   virtual void encode(RemoteTransmitData *dst, const T &data) = 0; | ||||
|   virtual optional<T> decode(RemoteReceiveData src) = 0; | ||||
|   virtual void dump(const T &data) = 0; | ||||
|   using ProtocolData = T; | ||||
|   virtual void encode(RemoteTransmitData *dst, const ProtocolData &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: | ||||
|   RemoteReceiverBinarySensor() : RemoteReceiverBinarySensorBase() {} | ||||
|  | ||||
| @@ -201,13 +210,14 @@ template<typename T, typename D> class RemoteReceiverBinarySensor : public Remot | ||||
|   } | ||||
|  | ||||
|  public: | ||||
|   void set_data(D data) { data_ = data; } | ||||
|   void set_data(typename T::ProtocolData data) { data_ = data; } | ||||
|  | ||||
|  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: | ||||
|   bool on_receive(RemoteReceiveData src) override { | ||||
|     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: | ||||
|   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); | ||||
|   TEMPLATABLE_VALUE(uint32_t, send_wait); | ||||
|  protected: | ||||
|   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 { | ||||
|     auto call = this->parent_->transmit(); | ||||
|     auto call = this->transmitter_->transmit(); | ||||
|     this->encode(call.get_data(), x...); | ||||
|     call.set_send_times(this->send_times_.value_or(x..., 1)); | ||||
|     call.set_send_wait(this->send_wait_.value_or(x..., 0)); | ||||
|     call.perform(); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   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: | ||||
|   bool dump(RemoteReceiveData src) override { | ||||
|     auto proto = T(); | ||||
| @@ -254,9 +272,9 @@ template<typename T, typename D> class RemoteReceiverDumper : public RemoteRecei | ||||
| }; | ||||
|  | ||||
| #define DECLARE_REMOTE_PROTOCOL_(prefix) \ | ||||
|   using prefix##BinarySensor = RemoteReceiverBinarySensor<prefix##Protocol, prefix##Data>; \ | ||||
|   using prefix##Trigger = RemoteReceiverTrigger<prefix##Protocol, prefix##Data>; \ | ||||
|   using prefix##Dumper = RemoteReceiverDumper<prefix##Protocol, prefix##Data>; | ||||
|   using prefix##BinarySensor = RemoteReceiverBinarySensor<prefix##Protocol>; \ | ||||
|   using prefix##Trigger = RemoteReceiverTrigger<prefix##Protocol>; \ | ||||
|   using prefix##Dumper = RemoteReceiverDumper<prefix##Protocol>; | ||||
| #define DECLARE_REMOTE_PROTOCOL(prefix) DECLARE_REMOTE_PROTOCOL_(prefix) | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -3050,6 +3050,9 @@ remote_receiver: | ||||
|   on_coolix: | ||||
|     then: | ||||
|       delay: !lambda "return x.first + x.second;" | ||||
|   on_rc_switch: | ||||
|     then: | ||||
|       delay: !lambda "return uint32_t(x.code) + x.protocol;" | ||||
|  | ||||
| status_led: | ||||
|   pin: GPIO2 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user