diff --git a/esphome/components/climate_ir/__init__.py b/esphome/components/climate_ir/__init__.py index 0cf1339971..c7c286d679 100644 --- a/esphome/components/climate_ir/__init__.py +++ b/esphome/components/climate_ir/__init__.py @@ -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( - { - 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) +CLIMATE_IR_SCHEMA = ( + climate.CLIMATE_SCHEMA.extend( + { + 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(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)) diff --git a/esphome/components/climate_ir/climate_ir.h b/esphome/components/climate_ir/climate_ir.h index 5be4fc06f5..ea0656121f 100644 --- a/esphome/components/climate_ir/climate_ir.h +++ b/esphome/components/climate_ir/climate_ir.h @@ -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 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 swing_modes_ = {}; std::set presets_ = {}; - remote_transmitter::RemoteTransmitterComponent *transmitter_; sensor::Sensor *sensor_{nullptr}; }; diff --git a/esphome/components/coolix/coolix.cpp b/esphome/components/coolix/coolix.cpp index f4309419a4..22b3431c3e 100644 --- a/esphome/components/coolix/coolix.cpp +++ b/esphome/components/coolix/coolix.cpp @@ -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_state); } bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteReceiveData data) { diff --git a/esphome/components/heatpumpir/ir_sender_esphome.h b/esphome/components/heatpumpir/ir_sender_esphome.h index 7546d990ea..944d0e859c 100644 --- a/esphome/components/heatpumpir/ir_sender_esphome.h +++ b/esphome/components/heatpumpir/ir_sender_esphome.h @@ -3,7 +3,6 @@ #ifdef USE_ARDUINO #include "esphome/components/remote_base/remote_base.h" -#include "esphome/components/remote_transmitter/remote_transmitter.h" #include // 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 diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 25dedd71d8..a2411b1b12 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -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) diff --git a/esphome/components/remote_base/midea_protocol.h b/esphome/components/remote_base/midea_protocol.h index 6925686b34..94fb6f3d94 100644 --- a/esphome/components/remote_base/midea_protocol.h +++ b/esphome/components/remote_base/midea_protocol.h @@ -67,20 +67,7 @@ class MideaProtocol : public RemoteProtocol { 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 &code) { this->data_ = code; } - - protected: - MideaData data_; -}; - -using MideaTrigger = RemoteReceiverTrigger; -using MideaDumper = RemoteReceiverDumper; +DECLARE_REMOTE_PROTOCOL(Midea) template class MideaAction : public RemoteTransmitterActionBase { TEMPLATABLE_VALUE(std::vector, code) diff --git a/esphome/components/remote_base/rc_switch_protocol.h b/esphome/components/remote_base/rc_switch_protocol.h index fc465dbd5d..96cbbd1467 100644 --- a/esphome/components/remote_base/rc_switch_protocol.h +++ b/esphome/components/remote_base/rc_switch_protocol.h @@ -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; +using RCSwitchTrigger = RemoteReceiverTrigger; } // namespace remote_base } // namespace esphome diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index a456007655..ebbb528a23 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -127,6 +127,14 @@ class RemoteTransmitterBase : public RemoteComponentBase { this->temp_.reset(); return TransmitCall(this); } + template + 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 class RemoteProtocol { public: - virtual void encode(RemoteTransmitData *dst, const T &data) = 0; - virtual optional 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 decode(RemoteReceiveData src) = 0; + virtual void dump(const ProtocolData &data) = 0; }; -template class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase { +template class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase { public: RemoteReceiverBinarySensor() : RemoteReceiverBinarySensorBase() {} @@ -201,13 +210,14 @@ template 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 class RemoteReceiverTrigger : public Trigger, public RemoteReceiverListener { +template +class RemoteReceiverTrigger : public Trigger, public RemoteReceiverListener { protected: bool on_receive(RemoteReceiveData src) override { auto proto = T(); @@ -220,28 +230,36 @@ template class RemoteReceiverTrigger : public Trigger } }; -template class RemoteTransmitterActionBase : public Action { +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 + void transmit_(const typename Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) { + this->transmitter_->transmit(data, send_times, send_wait); + } + RemoteTransmitterBase *transmitter_; +}; +template class RemoteTransmitterActionBase : public RemoteTransmittable, public Action { + 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 class RemoteReceiverDumper : public RemoteReceiverDumperBase { +template class RemoteReceiverDumper : public RemoteReceiverDumperBase { public: bool dump(RemoteReceiveData src) override { auto proto = T(); @@ -254,9 +272,9 @@ template class RemoteReceiverDumper : public RemoteRecei }; #define DECLARE_REMOTE_PROTOCOL_(prefix) \ - using prefix##BinarySensor = RemoteReceiverBinarySensor; \ - using prefix##Trigger = RemoteReceiverTrigger; \ - using prefix##Dumper = RemoteReceiverDumper; + using prefix##BinarySensor = RemoteReceiverBinarySensor; \ + using prefix##Trigger = RemoteReceiverTrigger; \ + using prefix##Dumper = RemoteReceiverDumper; #define DECLARE_REMOTE_PROTOCOL(prefix) DECLARE_REMOTE_PROTOCOL_(prefix) } // namespace remote_base diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index 62f199b040..d027f48244 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -74,12 +74,12 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: # The default/recommended arduino framework version # - https://github.com/earlephilhower/arduino-pico/releases # - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico -RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 4, 0) +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 6, 0) # The platformio/raspberrypi version to use for arduino frameworks # - https://github.com/platformio/platform-raspberrypi/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/raspberrypi -ARDUINO_PLATFORM_VERSION = cv.Version(1, 9, 0) +ARDUINO_PLATFORM_VERSION = cv.Version(1, 10, 0) def _arduino_check_versions(value): diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index d15d702d4b..fc5dd6e4e4 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -610,6 +610,11 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { if (code == "wake-word-timeout" || code == "wake_word_detection_aborted") { // Don't change state here since either the "tts-end" or "run-end" events will do it. return; + } else if (code == "wake-provider-missing" || code == "wake-engine-missing") { + // Wake word is not set up or not ready on Home Assistant so stop and do not retry until user starts again. + this->request_stop(); + this->error_trigger_->trigger(code, message); + return; } ESP_LOGE(TAG, "Error: %s - %s", code.c_str(), message.c_str()); if (this->state_ != State::IDLE) { diff --git a/platformio.ini b/platformio.ini index 73cd7c65c8..cbd87155be 100644 --- a/platformio.ini +++ b/platformio.ini @@ -159,7 +159,7 @@ board_build.filesystem_size = 0.5m platform = https://github.com/maxgerhardt/platform-raspberrypi.git platform_packages = ; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted - earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.4.0/rp2040-3.4.0.zip + earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.6.0/rp2040-3.6.0.zip framework = arduino lib_deps = diff --git a/requirements.txt b/requirements.txt index 51799bc685..3140d0a8a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ esptool==4.6.2 click==8.1.7 esphome-dashboard==20231107.0 aioesphomeapi==18.4.0 -zeroconf==0.122.3 +zeroconf==0.126.0 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 diff --git a/tests/test1.yaml b/tests/test1.yaml index 32e92440c1..61d28faf73 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -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