mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			264 Commits
		
	
	
		
			jesserockz
			...
			2023.11.3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					cadbf7463e | ||
| 
						 | 
					3f40e32eba | ||
| 
						 | 
					b421fccc08 | ||
| 
						 | 
					10ca05b686 | ||
| 
						 | 
					d0ac202a3f | ||
| 
						 | 
					1c4b06700f | ||
| 
						 | 
					47d42afda3 | ||
| 
						 | 
					1a9f66e630 | ||
| 
						 | 
					8fb6b8f1a2 | ||
| 
						 | 
					22eef036c7 | ||
| 
						 | 
					625ce2b8eb | ||
| 
						 | 
					e5e3b253bc | ||
| 
						 | 
					c369443263 | ||
| 
						 | 
					1e061582d3 | ||
| 
						 | 
					445b13dbc6 | ||
| 
						 | 
					255483de63 | ||
| 
						 | 
					4ac49907ca | ||
| 
						 | 
					c536c976b7 | ||
| 
						 | 
					0c18872888 | ||
| 
						 | 
					197b6b4275 | ||
| 
						 | 
					4e8bdc2155 | ||
| 
						 | 
					f1e8622187 | ||
| 
						 | 
					e0c7a02fbc | ||
| 
						 | 
					2a20a5fc11 | ||
| 
						 | 
					7100d073f8 | ||
| 
						 | 
					1ac6cf2ff9 | ||
| 
						 | 
					2ee089c9d5 | ||
| 
						 | 
					bd568eecf5 | ||
| 
						 | 
					3e2b83acb0 | ||
| 
						 | 
					c1eb5bd675 | ||
| 
						 | 
					a9772ebf3f | ||
| 
						 | 
					a9a17ee89d | ||
| 
						 | 
					f094702a16 | ||
| 
						 | 
					908f56ff46 | ||
| 
						 | 
					bd5905c59a | ||
| 
						 | 
					91299f05f7 | ||
| 
						 | 
					30e5ff9fff | ||
| 
						 | 
					163b38e153 | ||
| 
						 | 
					3b486084c8 | ||
| 
						 | 
					9d453f0ba2 | ||
| 
						 | 
					799851a83a | ||
| 
						 | 
					7a9866f1b6 | ||
| 
						 | 
					3d30f1f733 | ||
| 
						 | 
					1e55764d52 | ||
| 
						 | 
					020da89b6a | ||
| 
						 | 
					6932422104 | ||
| 
						 | 
					29aa15b253 | ||
| 
						 | 
					c40519ec6f | ||
| 
						 | 
					6c62c00963 | ||
| 
						 | 
					1bd2e558d6 | ||
| 
						 | 
					dbb1263a36 | ||
| 
						 | 
					966c6a4531 | ||
| 
						 | 
					fff2d01420 | ||
| 
						 | 
					bf217ce252 | ||
| 
						 | 
					a7ad4482f0 | ||
| 
						 | 
					aa17661002 | ||
| 
						 | 
					4e65aac7ae | ||
| 
						 | 
					229ba18e6c | ||
| 
						 | 
					b99be250a0 | ||
| 
						 | 
					b9d4e2e501 | ||
| 
						 | 
					ef2531edf3 | ||
| 
						 | 
					eae3089201 | ||
| 
						 | 
					0ea4de5f4c | ||
| 
						 | 
					1e0daefa16 | ||
| 
						 | 
					6d991a1fc8 | ||
| 
						 | 
					a1845e1e72 | ||
| 
						 | 
					f96a839bcf | ||
| 
						 | 
					1282a15b14 | ||
| 
						 | 
					35039b45e4 | ||
| 
						 | 
					390766eb67 | ||
| 
						 | 
					899d280ac7 | ||
| 
						 | 
					96dc7f0259 | ||
| 
						 | 
					0104bf3fc8 | ||
| 
						 | 
					9b1e1bf56c | ||
| 
						 | 
					33e0f16b3b | ||
| 
						 | 
					0807d60c6a | ||
| 
						 | 
					f018fde369 | ||
| 
						 | 
					c47f8fc02c | ||
| 
						 | 
					76ab923780 | ||
| 
						 | 
					11dba3147d | ||
| 
						 | 
					8c2d9101d5 | ||
| 
						 | 
					61b8004536 | ||
| 
						 | 
					db02c4ea21 | ||
| 
						 | 
					f077a5962d | ||
| 
						 | 
					fa4ba43eb9 | ||
| 
						 | 
					9579423b24 | ||
| 
						 | 
					02449f24c9 | ||
| 
						 | 
					b973238323 | ||
| 
						 | 
					582b8383d2 | ||
| 
						 | 
					e1c9418aee | ||
| 
						 | 
					2aa787f5f0 | ||
| 
						 | 
					2189a40a39 | ||
| 
						 | 
					51688d4078 | ||
| 
						 | 
					cc4c0e3e0b | ||
| 
						 | 
					1a44c6487e | ||
| 
						 | 
					5e7ce610a0 | ||
| 
						 | 
					1f02096edb | ||
| 
						 | 
					fd7d3c4332 | ||
| 
						 | 
					61cf566560 | ||
| 
						 | 
					97d624114d | ||
| 
						 | 
					52e8a2e9e4 | ||
| 
						 | 
					261c271d60 | ||
| 
						 | 
					cb6e314336 | ||
| 
						 | 
					90315b3c40 | ||
| 
						 | 
					5d7c3d1622 | ||
| 
						 | 
					8c1ad1e9a6 | ||
| 
						 | 
					969f6dbe13 | ||
| 
						 | 
					6cce6d4c36 | ||
| 
						 | 
					d27e5e9c97 | ||
| 
						 | 
					af3b22f8b7 | ||
| 
						 | 
					cbc1b29f3e | ||
| 
						 | 
					54363f1246 | ||
| 
						 | 
					d500531c04 | ||
| 
						 | 
					0d800958aa | ||
| 
						 | 
					471533d041 | ||
| 
						 | 
					7dfc4c74da | ||
| 
						 | 
					f709350b04 | ||
| 
						 | 
					85c5928baa | ||
| 
						 | 
					f5dfbaff4b | ||
| 
						 | 
					689c2f11a3 | ||
| 
						 | 
					f73fd97525 | ||
| 
						 | 
					40523e6823 | ||
| 
						 | 
					5e1472185c | ||
| 
						 | 
					af005a6554 | ||
| 
						 | 
					efd31be21c | ||
| 
						 | 
					e9bda2810f | ||
| 
						 | 
					ec4777b8d0 | ||
| 
						 | 
					9b75121337 | ||
| 
						 | 
					d262548d2e | ||
| 
						 | 
					b5b654e054 | ||
| 
						 | 
					dae8ab563c | ||
| 
						 | 
					5751e9ec59 | ||
| 
						 | 
					cc1b7a7a56 | ||
| 
						 | 
					29249cdc1b | ||
| 
						 | 
					e5bae8187f | ||
| 
						 | 
					69adebfefa | ||
| 
						 | 
					7dabbb65d0 | ||
| 
						 | 
					b30bab8c1b | ||
| 
						 | 
					0a1ed58454 | ||
| 
						 | 
					5f5ee9c920 | ||
| 
						 | 
					0aeebdd289 | ||
| 
						 | 
					33e2aa341e | ||
| 
						 | 
					a42788812e | ||
| 
						 | 
					b07a038bc8 | ||
| 
						 | 
					55e36ab982 | ||
| 
						 | 
					90835ab917 | ||
| 
						 | 
					5b46088ae4 | ||
| 
						 | 
					d7e267eca5 | ||
| 
						 | 
					807c47a076 | ||
| 
						 | 
					7ebe6a5894 | ||
| 
						 | 
					41c829fa32 | ||
| 
						 | 
					8f1ce8c7f7 | ||
| 
						 | 
					e55636ed52 | ||
| 
						 | 
					e886262055 | ||
| 
						 | 
					2fa7f8c511 | ||
| 
						 | 
					4622ef770d | ||
| 
						 | 
					d76f18b4f2 | ||
| 
						 | 
					ec20778d83 | ||
| 
						 | 
					b3ca71c6fb | ||
| 
						 | 
					2d53dd05d8 | ||
| 
						 | 
					68a2c45edf | ||
| 
						 | 
					d2616cd6c6 | ||
| 
						 | 
					01ec414873 | ||
| 
						 | 
					150c9b5fa3 | ||
| 
						 | 
					55df88d7ae | ||
| 
						 | 
					619787e6d2 | ||
| 
						 | 
					3f8bad3ed1 | ||
| 
						 | 
					c146712b16 | ||
| 
						 | 
					2cabe59c22 | ||
| 
						 | 
					a67b92a04c | ||
| 
						 | 
					9fb8e9edef | ||
| 
						 | 
					d2bccbe8ac | ||
| 
						 | 
					e44a60e814 | ||
| 
						 | 
					02a71cb6a7 | ||
| 
						 | 
					e600784ebf | ||
| 
						 | 
					5e19a3b892 | ||
| 
						 | 
					8bf112669f | ||
| 
						 | 
					4278664208 | ||
| 
						 | 
					0789657fd5 | ||
| 
						 | 
					b566c78f00 | ||
| 
						 | 
					a35122231c | ||
| 
						 | 
					7e4ee32b54 | ||
| 
						 | 
					7df80eadcf | ||
| 
						 | 
					2aaba1d2b8 | ||
| 
						 | 
					7c129a4018 | ||
| 
						 | 
					cb66ce069e | ||
| 
						 | 
					a27e72362a | ||
| 
						 | 
					f44e5d3142 | ||
| 
						 | 
					532163738e | ||
| 
						 | 
					63fa922547 | ||
| 
						 | 
					48e4cb5ae2 | ||
| 
						 | 
					ff8a73c2d1 | ||
| 
						 | 
					afd26c6f1a | ||
| 
						 | 
					67b06a88b2 | ||
| 
						 | 
					265e019381 | ||
| 
						 | 
					560e36a65c | ||
| 
						 | 
					b05a3fbb55 | ||
| 
						 | 
					3a899e28dc | ||
| 
						 | 
					f26238e824 | ||
| 
						 | 
					3717e34bba | ||
| 
						 | 
					be6f95d43e | ||
| 
						 | 
					99a765dc06 | ||
| 
						 | 
					351e7ea16b | ||
| 
						 | 
					2fa79a2e2f | ||
| 
						 | 
					44a917929d | ||
| 
						 | 
					21ebc7f95b | ||
| 
						 | 
					72e72d7d4b | ||
| 
						 | 
					02ed2c0ebe | ||
| 
						 | 
					0f506ea8eb | ||
| 
						 | 
					b914d6e305 | ||
| 
						 | 
					956e19be7d | ||
| 
						 | 
					b3d5a4dfdb | ||
| 
						 | 
					c63cdae84f | ||
| 
						 | 
					dec044ad8b | ||
| 
						 | 
					2a12ec09fb | ||
| 
						 | 
					91e920c498 | ||
| 
						 | 
					9b19c45735 | ||
| 
						 | 
					3843d21dbf | ||
| 
						 | 
					73db164fb1 | ||
| 
						 | 
					ab32dd7420 | ||
| 
						 | 
					2a7aa2fc0d | ||
| 
						 | 
					f5e98eb86f | ||
| 
						 | 
					362a19c2e1 | ||
| 
						 | 
					f4a4956dd4 | ||
| 
						 | 
					746488cabf | ||
| 
						 | 
					4449248c6f | ||
| 
						 | 
					036e14ab7f | ||
| 
						 | 
					f840eee1b7 | ||
| 
						 | 
					553132443f | ||
| 
						 | 
					d20242f589 | ||
| 
						 | 
					68affce274 | ||
| 
						 | 
					c4b9065749 | ||
| 
						 | 
					d57a5d1793 | ||
| 
						 | 
					74e062fdb3 | ||
| 
						 | 
					6bdc0c92fe | ||
| 
						 | 
					d7945de001 | ||
| 
						 | 
					3ba2a29e54 | ||
| 
						 | 
					76b438f79c | ||
| 
						 | 
					bc14f06a07 | ||
| 
						 | 
					feee075122 | ||
| 
						 | 
					a77cf1beec | ||
| 
						 | 
					d7bfdd0efc | ||
| 
						 | 
					62aee36f82 | ||
| 
						 | 
					0709367587 | ||
| 
						 | 
					98277f6ceb | ||
| 
						 | 
					8dd509ba53 | ||
| 
						 | 
					8df455f55b | ||
| 
						 | 
					36782f13bf | ||
| 
						 | 
					e823067a6b | ||
| 
						 | 
					c3ef12d580 | ||
| 
						 | 
					321155eb40 | ||
| 
						 | 
					d34c074b92 | ||
| 
						 | 
					abc8e903c1 | ||
| 
						 | 
					832ba38f1b | ||
| 
						 | 
					70de2f5278 | ||
| 
						 | 
					604d4eec79 | ||
| 
						 | 
					b806eb6a61 | ||
| 
						 | 
					39948db59a | ||
| 
						 | 
					fbfb4e2a73 | ||
| 
						 | 
					595ac84779 | ||
| 
						 | 
					746f72a279 | ||
| 
						 | 
					dec6f04499 | ||
| 
						 | 
					a90d266017 | ||
| 
						 | 
					df9fcf9850 | 
@@ -3,7 +3,7 @@
 | 
			
		||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/psf/black-pre-commit-mirror
 | 
			
		||||
    rev: 23.11.0
 | 
			
		||||
    rev: 23.10.1
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: black
 | 
			
		||||
        args:
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,8 @@ RUN \
 | 
			
		||||
          libfreetype-dev=2.12.1+dfsg-5 \
 | 
			
		||||
          libssl-dev=3.0.11-1~deb12u2 \
 | 
			
		||||
          libffi-dev=3.4.4-1 \
 | 
			
		||||
          libopenjp2-7=2.5.0-2 \
 | 
			
		||||
          libtiff6=4.5.0-6 \
 | 
			
		||||
          cargo=0.66.0+ds1-1 \
 | 
			
		||||
          pkg-config=1.8.1-1 \
 | 
			
		||||
          gcc-arm-linux-gnueabihf=4:12.2.0-3; \
 | 
			
		||||
@@ -68,7 +70,7 @@ ENV \
 | 
			
		||||
# See: https://unix.stackexchange.com/questions/553743/correct-way-to-add-lib-ld-linux-so-3-in-debian
 | 
			
		||||
RUN \
 | 
			
		||||
    if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
 | 
			
		||||
        ln -s /lib/arm-linux-gnueabihf/ld-linux.so.3 /lib/ld-linux.so.3; \
 | 
			
		||||
        ln -s /lib/arm-linux-gnueabihf/ld-linux-armhf.so.3 /lib/ld-linux.so.3; \
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
RUN \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +1,38 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import climate, sensor, remote_base
 | 
			
		||||
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.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,
 | 
			
		||||
    remote_base.RemoteTransmittable,
 | 
			
		||||
    "ClimateIR", climate.Climate, cg.Component, remote_base.RemoteReceiverListener
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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_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_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Optional(remote_base.CONF_RECEIVER_ID): cv.use_id(
 | 
			
		||||
            remote_base.RemoteReceiverBase
 | 
			
		||||
        cv.Optional(CONF_RECEIVER_ID): cv.use_id(
 | 
			
		||||
            remote_receiver.RemoteReceiverComponent
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
@@ -40,11 +41,15 @@ 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,10 +18,7 @@ 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 Component,
 | 
			
		||||
                  public climate::Climate,
 | 
			
		||||
                  public remote_base::RemoteReceiverListener,
 | 
			
		||||
                  public remote_base::RemoteTransmittable {
 | 
			
		||||
class ClimateIR : public climate::Climate, public Component, public remote_base::RemoteReceiverListener {
 | 
			
		||||
 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 = {},
 | 
			
		||||
@@ -38,6 +35,9 @@ class ClimateIR : public Component,
 | 
			
		||||
 | 
			
		||||
  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,6 +64,7 @@ class ClimateIR : public Component,
 | 
			
		||||
  std::set<climate::ClimateSwingMode> swing_modes_ = {};
 | 
			
		||||
  std::set<climate::ClimatePreset> presets_ = {};
 | 
			
		||||
 | 
			
		||||
  remote_transmitter::RemoteTransmitterComponent *transmitter_;
 | 
			
		||||
  sensor::Sensor *sensor_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -102,7 +102,11 @@ void CoolixClimate::transmit_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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,23 +3,26 @@ from typing import Union, Optional
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import esphome.final_validate as fv
 | 
			
		||||
 | 
			
		||||
from esphome.helpers import copy_file_if_changed, write_file_if_changed, mkdir_p
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ADVANCED,
 | 
			
		||||
    CONF_BOARD,
 | 
			
		||||
    CONF_COMPONENTS,
 | 
			
		||||
    CONF_ESPHOME,
 | 
			
		||||
    CONF_FRAMEWORK,
 | 
			
		||||
    CONF_IGNORE_EFUSE_MAC_CRC,
 | 
			
		||||
    CONF_NAME,
 | 
			
		||||
    CONF_PATH,
 | 
			
		||||
    CONF_PLATFORMIO_OPTIONS,
 | 
			
		||||
    CONF_REF,
 | 
			
		||||
    CONF_REFRESH,
 | 
			
		||||
    CONF_SOURCE,
 | 
			
		||||
    CONF_TYPE,
 | 
			
		||||
    CONF_URL,
 | 
			
		||||
    CONF_VARIANT,
 | 
			
		||||
    CONF_VERSION,
 | 
			
		||||
    CONF_ADVANCED,
 | 
			
		||||
    CONF_REFRESH,
 | 
			
		||||
    CONF_PATH,
 | 
			
		||||
    CONF_URL,
 | 
			
		||||
    CONF_REF,
 | 
			
		||||
    CONF_IGNORE_EFUSE_MAC_CRC,
 | 
			
		||||
    KEY_CORE,
 | 
			
		||||
    KEY_FRAMEWORK_VERSION,
 | 
			
		||||
    KEY_NAME,
 | 
			
		||||
@@ -327,6 +330,32 @@ def _detect_variant(value):
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def final_validate(config):
 | 
			
		||||
    if CONF_PLATFORMIO_OPTIONS not in fv.full_config.get()[CONF_ESPHOME]:
 | 
			
		||||
        return config
 | 
			
		||||
 | 
			
		||||
    pio_flash_size_key = "board_upload.flash_size"
 | 
			
		||||
    pio_partitions_key = "board_build.partitions"
 | 
			
		||||
    if (
 | 
			
		||||
        CONF_PARTITIONS in config
 | 
			
		||||
        and pio_partitions_key
 | 
			
		||||
        in fv.full_config.get()[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS]
 | 
			
		||||
    ):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"Do not specify '{pio_partitions_key}' in '{CONF_PLATFORMIO_OPTIONS}' with '{CONF_PARTITIONS}' in esp32"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
        pio_flash_size_key
 | 
			
		||||
        in fv.full_config.get()[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS]
 | 
			
		||||
    ):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONF_PLATFORM_VERSION = "platform_version"
 | 
			
		||||
 | 
			
		||||
ARDUINO_FRAMEWORK_SCHEMA = cv.All(
 | 
			
		||||
@@ -387,6 +416,7 @@ FRAMEWORK_SCHEMA = cv.typed_schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FLASH_SIZES = [
 | 
			
		||||
    "2MB",
 | 
			
		||||
    "4MB",
 | 
			
		||||
    "8MB",
 | 
			
		||||
    "16MB",
 | 
			
		||||
@@ -394,6 +424,7 @@ FLASH_SIZES = [
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
CONF_FLASH_SIZE = "flash_size"
 | 
			
		||||
CONF_PARTITIONS = "partitions"
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
@@ -401,6 +432,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_FLASH_SIZE, default="4MB"): cv.one_of(
 | 
			
		||||
                *FLASH_SIZES, upper=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PARTITIONS): cv.file_,
 | 
			
		||||
            cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True),
 | 
			
		||||
            cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA,
 | 
			
		||||
        }
 | 
			
		||||
@@ -410,6 +442,9 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = cv.Schema(final_validate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_platformio_option("board", config[CONF_BOARD])
 | 
			
		||||
    cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE])
 | 
			
		||||
@@ -462,7 +497,10 @@ async def to_code(config):
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0", False)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1", False)
 | 
			
		||||
 | 
			
		||||
        cg.add_platformio_option("board_build.partitions", "partitions.csv")
 | 
			
		||||
        if CONF_PARTITIONS in config:
 | 
			
		||||
            cg.add_platformio_option("board_build.partitions", config[CONF_PARTITIONS])
 | 
			
		||||
        else:
 | 
			
		||||
            cg.add_platformio_option("board_build.partitions", "partitions.csv")
 | 
			
		||||
 | 
			
		||||
        for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():
 | 
			
		||||
            add_idf_sdkconfig_option(name, RawSdkconfigValue(value))
 | 
			
		||||
@@ -507,7 +545,10 @@ async def to_code(config):
 | 
			
		||||
            [f"platformio/framework-arduinoespressif32@{conf[CONF_SOURCE]}"],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        cg.add_platformio_option("board_build.partitions", "partitions.csv")
 | 
			
		||||
        if CONF_PARTITIONS in config:
 | 
			
		||||
            cg.add_platformio_option("board_build.partitions", config[CONF_PARTITIONS])
 | 
			
		||||
        else:
 | 
			
		||||
            cg.add_platformio_option("board_build.partitions", "partitions.csv")
 | 
			
		||||
 | 
			
		||||
        cg.add_define(
 | 
			
		||||
            "USE_ARDUINO_VERSION_CODE",
 | 
			
		||||
@@ -518,6 +559,7 @@ async def to_code(config):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
APP_PARTITION_SIZES = {
 | 
			
		||||
    "2MB": 0x0C0000,  # 768 KB
 | 
			
		||||
    "4MB": 0x1C0000,  # 1792 KB
 | 
			
		||||
    "8MB": 0x3C0000,  # 3840 KB
 | 
			
		||||
    "16MB": 0x7C0000,  # 7936 KB
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#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 {
 | 
			
		||||
@@ -10,13 +11,14 @@ namespace heatpumpir {
 | 
			
		||||
 | 
			
		||||
class IRSenderESPHome : public IRSender {
 | 
			
		||||
 public:
 | 
			
		||||
  IRSenderESPHome(remote_base::RemoteTransmitterBase *transmitter) : IRSender(0), transmit_(transmitter->transmit()){};
 | 
			
		||||
  IRSenderESPHome(remote_transmitter::RemoteTransmitterComponent *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_base::RemoteTransmitterBase::TransmitCall transmit_;
 | 
			
		||||
  remote_transmitter::RemoteTransmitterComponent::TransmitCall transmit_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace heatpumpir
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ from esphome.components.esp32.const import (
 | 
			
		||||
    VARIANT_ESP32S2,
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
    VARIANT_ESP32C3,
 | 
			
		||||
    VARIANT_ESP32C6,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
@@ -39,7 +38,6 @@ I2S_PORTS = {
 | 
			
		||||
    VARIANT_ESP32S2: 1,
 | 
			
		||||
    VARIANT_ESP32S3: 2,
 | 
			
		||||
    VARIANT_ESP32C3: 1,
 | 
			
		||||
    VARIANT_ESP32C6: 1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
 
 | 
			
		||||
@@ -147,7 +147,7 @@ void MQTTClientComponent::dump_config() {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Availability: '%s'", this->availability_.topic.c_str());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool MQTTClientComponent::can_proceed() { return this->is_connected(); }
 | 
			
		||||
bool MQTTClientComponent::can_proceed() { return network::is_disabled() || this->is_connected(); }
 | 
			
		||||
 | 
			
		||||
void MQTTClientComponent::start_dnslookup_() {
 | 
			
		||||
  for (auto &subscription : this->subscriptions_) {
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,8 @@ const EntityBase *MQTTLockComponent::get_entity() const { return this->lock_; }
 | 
			
		||||
void MQTTLockComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
 | 
			
		||||
  if (this->lock_->traits.get_assumed_state())
 | 
			
		||||
    root[MQTT_OPTIMISTIC] = true;
 | 
			
		||||
  if (this->lock_->traits.get_supports_open())
 | 
			
		||||
    root[MQTT_PAYLOAD_OPEN] = "OPEN";
 | 
			
		||||
}
 | 
			
		||||
bool MQTTLockComponent::send_initial_state() { return this->publish_state(); }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#include "my9231.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace my9231 {
 | 
			
		||||
@@ -51,7 +52,11 @@ void MY9231OutputComponent::setup() {
 | 
			
		||||
      MY9231_CMD_SCATTER_APDM | MY9231_CMD_FREQUENCY_DIVIDE_1 | MY9231_CMD_REACTION_FAST | MY9231_CMD_ONE_SHOT_DISABLE;
 | 
			
		||||
  ESP_LOGV(TAG, "  Command: 0x%02X", command);
 | 
			
		||||
 | 
			
		||||
  this->init_chips_(command);
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    this->send_dcki_pulses_(32 * this->num_chips_);
 | 
			
		||||
    this->init_chips_(command);
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "  Chips initialized.");
 | 
			
		||||
}
 | 
			
		||||
void MY9231OutputComponent::dump_config() {
 | 
			
		||||
@@ -66,11 +71,14 @@ void MY9231OutputComponent::loop() {
 | 
			
		||||
  if (!this->update_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  for (auto pwm_amount : this->pwm_amounts_) {
 | 
			
		||||
    this->write_word_(pwm_amount, this->bit_depth_);
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    for (auto pwm_amount : this->pwm_amounts_) {
 | 
			
		||||
      this->write_word_(pwm_amount, this->bit_depth_);
 | 
			
		||||
    }
 | 
			
		||||
    // Send 8 DI pulses. After 8 falling edges, the duty data are store.
 | 
			
		||||
    this->send_di_pulses_(8);
 | 
			
		||||
  }
 | 
			
		||||
  // Send 8 DI pulses. After 8 falling edges, the duty data are store.
 | 
			
		||||
  this->send_di_pulses_(8);
 | 
			
		||||
  this->update_ = false;
 | 
			
		||||
}
 | 
			
		||||
void MY9231OutputComponent::set_channel_value_(uint8_t channel, uint16_t value) {
 | 
			
		||||
@@ -92,6 +100,7 @@ void MY9231OutputComponent::init_chips_(uint8_t command) {
 | 
			
		||||
  // Send 16 DI pulse. After 14 falling edges, the command data are
 | 
			
		||||
  // stored and after 16 falling edges the duty mode is activated.
 | 
			
		||||
  this->send_di_pulses_(16);
 | 
			
		||||
  delayMicroseconds(12);
 | 
			
		||||
}
 | 
			
		||||
void MY9231OutputComponent::write_word_(uint16_t value, uint8_t bits) {
 | 
			
		||||
  for (uint8_t i = bits; i > 0; i--) {
 | 
			
		||||
@@ -106,6 +115,13 @@ void MY9231OutputComponent::send_di_pulses_(uint8_t count) {
 | 
			
		||||
    this->pin_di_->digital_write(false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void MY9231OutputComponent::send_dcki_pulses_(uint8_t count) {
 | 
			
		||||
  delayMicroseconds(12);
 | 
			
		||||
  for (uint8_t i = 0; i < count; i++) {
 | 
			
		||||
    this->pin_dcki_->digital_write(true);
 | 
			
		||||
    this->pin_dcki_->digital_write(false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace my9231
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,7 @@ class MY9231OutputComponent : public Component {
 | 
			
		||||
  void init_chips_(uint8_t command);
 | 
			
		||||
  void write_word_(uint16_t value, uint8_t bits);
 | 
			
		||||
  void send_di_pulses_(uint8_t count);
 | 
			
		||||
  void send_dcki_pulses_(uint8_t count);
 | 
			
		||||
 | 
			
		||||
  GPIOPin *pin_di_;
 | 
			
		||||
  GPIOPin *pin_dcki_;
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,14 @@ bool is_connected() {
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool is_disabled() {
 | 
			
		||||
#ifdef USE_WIFI
 | 
			
		||||
  if (wifi::global_wifi_component != nullptr)
 | 
			
		||||
    return wifi::global_wifi_component->is_disabled();
 | 
			
		||||
#endif
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
network::IPAddress get_ip_address() {
 | 
			
		||||
#ifdef USE_ETHERNET
 | 
			
		||||
  if (ethernet::global_eth_component != nullptr)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ namespace network {
 | 
			
		||||
 | 
			
		||||
/// Return whether the node is connected to the network (through wifi, eth, ...)
 | 
			
		||||
bool is_connected();
 | 
			
		||||
/// Return whether the network is disabled (only wifi for now)
 | 
			
		||||
bool is_disabled();
 | 
			
		||||
/// Get the active network hostname
 | 
			
		||||
std::string get_use_address();
 | 
			
		||||
IPAddress get_ip_address();
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ CONFIG_SCHEMA = (
 | 
			
		||||
    display.BASIC_DISPLAY_SCHEMA.extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(Nextion),
 | 
			
		||||
            cv.Optional(CONF_TFT_URL): cv.url,
 | 
			
		||||
            cv.Optional(CONF_TFT_URL): cv.All(cv.string, cv.only_with_arduino),
 | 
			
		||||
            cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
 | 
			
		||||
            cv.Optional(CONF_ON_SETUP): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
@@ -85,10 +85,10 @@ async def to_code(config):
 | 
			
		||||
    if CONF_TFT_URL in config:
 | 
			
		||||
        cg.add_define("USE_NEXTION_TFT_UPLOAD")
 | 
			
		||||
        cg.add(var.set_tft_url(config[CONF_TFT_URL]))
 | 
			
		||||
        if CORE.is_esp32 and CORE.using_arduino:
 | 
			
		||||
        if CORE.is_esp32:
 | 
			
		||||
            cg.add_library("WiFiClientSecure", None)
 | 
			
		||||
            cg.add_library("HTTPClient", None)
 | 
			
		||||
        elif CORE.is_esp8266 and CORE.using_arduino:
 | 
			
		||||
        if CORE.is_esp8266:
 | 
			
		||||
            cg.add_library("ESP8266HTTPClient", None)
 | 
			
		||||
 | 
			
		||||
    if CONF_TOUCH_SLEEP_TIMEOUT in config:
 | 
			
		||||
 
 | 
			
		||||
@@ -128,7 +128,7 @@ void Nextion::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Wake On Touch:    %s", this->auto_wake_on_touch_ ? "True" : "False");
 | 
			
		||||
 | 
			
		||||
  if (this->touch_sleep_timeout_ != 0) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Touch Timeout:       %" PRIu32, this->touch_sleep_timeout_);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Touch Timeout:       %d", this->touch_sleep_timeout_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->wake_up_page_ != -1) {
 | 
			
		||||
@@ -868,12 +868,6 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool
 | 
			
		||||
  start = millis();
 | 
			
		||||
 | 
			
		||||
  while ((timeout == 0 && this->available()) || millis() - start <= timeout) {
 | 
			
		||||
    if (!this->available()) {
 | 
			
		||||
      App.feed_wdt();
 | 
			
		||||
      delay(1);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->read_byte(&c);
 | 
			
		||||
    if (c == 0xFF) {
 | 
			
		||||
      nr_of_ff_bytes++;
 | 
			
		||||
@@ -892,7 +886,7 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    App.feed_wdt();
 | 
			
		||||
    delay(2);
 | 
			
		||||
    delay(1);
 | 
			
		||||
 | 
			
		||||
    if (exit_flag || ff_flag) {
 | 
			
		||||
      break;
 | 
			
		||||
 
 | 
			
		||||
@@ -12,18 +12,14 @@
 | 
			
		||||
#include "esphome/components/display/display_color_utils.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
#ifdef ARDUINO
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#include <HTTPClient.h>
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
#include <ESP8266HTTPClient.h>
 | 
			
		||||
#include <WiFiClientSecure.h>
 | 
			
		||||
#endif  // USE_ESP8266
 | 
			
		||||
#elif defined(USE_ESP_IDF)
 | 
			
		||||
#include <esp_http_client.h>
 | 
			
		||||
#endif  // ARDUINO vs ESP-IDF
 | 
			
		||||
#endif  // USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nextion {
 | 
			
		||||
@@ -689,18 +685,16 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
 | 
			
		||||
 | 
			
		||||
#ifdef USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
  /**
 | 
			
		||||
   * Set the tft file URL. https seems problematic with arduino..
 | 
			
		||||
   * Set the tft file URL. https seems problamtic with arduino..
 | 
			
		||||
   */
 | 
			
		||||
  void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Upload the tft file and soft reset Nextion
 | 
			
		||||
   * @return bool True: Transfer completed successfuly, False: Transfer failed.
 | 
			
		||||
   * Upload the tft file and softreset the Nextion
 | 
			
		||||
   */
 | 
			
		||||
  bool upload_tft();
 | 
			
		||||
 | 
			
		||||
  void upload_tft();
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -823,16 +817,16 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
 | 
			
		||||
  BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr};
 | 
			
		||||
  WiFiClient *get_wifi_client_();
 | 
			
		||||
#endif
 | 
			
		||||
  int content_length_ = 0;
 | 
			
		||||
  int tft_size_ = 0;
 | 
			
		||||
#ifdef ARDUINO
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * will request chunk_size chunks from the web server
 | 
			
		||||
   * and send each to the nextion
 | 
			
		||||
   * @param HTTPClient http HTTP client handler.
 | 
			
		||||
   * @param int range_start Position of next byte to transfer.
 | 
			
		||||
   * @return position of last byte transferred, -1 for failure.
 | 
			
		||||
   * @param int contentLength Total size of the file
 | 
			
		||||
   * @param uint32_t chunk_size
 | 
			
		||||
   * @return true if success, false for failure.
 | 
			
		||||
   */
 | 
			
		||||
  int content_length_ = 0;
 | 
			
		||||
  int tft_size_ = 0;
 | 
			
		||||
  int upload_by_chunks_(HTTPClient *http, int range_start);
 | 
			
		||||
 | 
			
		||||
  bool upload_with_range_(uint32_t range_start, uint32_t range_end);
 | 
			
		||||
@@ -845,30 +839,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
 | 
			
		||||
   * @return true if success, false for failure.
 | 
			
		||||
   */
 | 
			
		||||
  bool upload_from_buffer_(const uint8_t *file_buf, size_t buf_size);
 | 
			
		||||
  /**
 | 
			
		||||
   * Ends the upload process, restart Nextion and, if successful,
 | 
			
		||||
   * restarts ESP
 | 
			
		||||
   * @param bool url successful True: Transfer completed successfuly, False: Transfer failed.
 | 
			
		||||
   * @return bool True: Transfer completed successfuly, False: Transfer failed.
 | 
			
		||||
   */
 | 
			
		||||
  bool upload_end_(bool successful);
 | 
			
		||||
#elif defined(USE_ESP_IDF)
 | 
			
		||||
  /**
 | 
			
		||||
   * will request 4096 bytes chunks from the web server
 | 
			
		||||
   * and send each to Nextion
 | 
			
		||||
   * @param std::string url Full url for download.
 | 
			
		||||
   * @param int range_start Position of next byte to transfer.
 | 
			
		||||
   * @return position of last byte transferred, -1 for failure.
 | 
			
		||||
   */
 | 
			
		||||
  int upload_range(const std::string &url, int range_start);
 | 
			
		||||
  /**
 | 
			
		||||
   * Ends the upload process, restart Nextion and, if successful,
 | 
			
		||||
   * restarts ESP
 | 
			
		||||
   * @param bool url successful True: Transfer completed successfuly, False: Transfer failed.
 | 
			
		||||
   * @return bool True: Transfer completed successfuly, False: Transfer failed.
 | 
			
		||||
   */
 | 
			
		||||
  bool upload_end(bool successful);
 | 
			
		||||
#endif  // ARDUINO vs ESP-IDF
 | 
			
		||||
  void upload_end_();
 | 
			
		||||
 | 
			
		||||
#endif  // USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ void Nextion::set_protocol_reparse_mode(bool active_mode) {
 | 
			
		||||
 | 
			
		||||
// Set Colors
 | 
			
		||||
void Nextion::set_component_background_color(const char *component, uint32_t color) {
 | 
			
		||||
  this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%" PRIu32, component, color);
 | 
			
		||||
  this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%d", component, color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Nextion::set_component_background_color(const char *component, const char *color) {
 | 
			
		||||
@@ -68,8 +68,7 @@ void Nextion::set_component_background_color(const char *component, Color color)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Nextion::set_component_pressed_background_color(const char *component, uint32_t color) {
 | 
			
		||||
  this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%" PRIu32, component,
 | 
			
		||||
                                            color);
 | 
			
		||||
  this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%d", component, color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Nextion::set_component_pressed_background_color(const char *component, const char *color) {
 | 
			
		||||
@@ -90,7 +89,7 @@ void Nextion::set_component_picc(const char *component, uint8_t pic_id) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Nextion::set_component_font_color(const char *component, uint32_t color) {
 | 
			
		||||
  this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%" PRIu32, component, color);
 | 
			
		||||
  this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%d", component, color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Nextion::set_component_font_color(const char *component, const char *color) {
 | 
			
		||||
@@ -103,7 +102,7 @@ void Nextion::set_component_font_color(const char *component, Color color) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Nextion::set_component_pressed_font_color(const char *component, uint32_t color) {
 | 
			
		||||
  this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%" PRIu32, component, color);
 | 
			
		||||
  this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%d", component, color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Nextion::set_component_pressed_font_color(const char *component, const char *color) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
#include "nextion.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO
 | 
			
		||||
#ifdef USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
@@ -129,15 +128,15 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) {
 | 
			
		||||
  return range_end + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Nextion::upload_tft() {
 | 
			
		||||
void Nextion::upload_tft() {
 | 
			
		||||
  if (this->is_updating_) {
 | 
			
		||||
    ESP_LOGD(TAG, "Currently updating");
 | 
			
		||||
    return false;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!network::is_connected()) {
 | 
			
		||||
    ESP_LOGD(TAG, "network is not connected");
 | 
			
		||||
    return false;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->is_updating_ = true;
 | 
			
		||||
@@ -165,7 +164,7 @@ bool Nextion::upload_tft() {
 | 
			
		||||
    ESP_LOGD(TAG, "connection failed");
 | 
			
		||||
    ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
    allocator.deallocate(this->transfer_buffer_, this->transfer_buffer_size_);
 | 
			
		||||
    return false;
 | 
			
		||||
    return;
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGD(TAG, "Connected");
 | 
			
		||||
  }
 | 
			
		||||
@@ -193,7 +192,7 @@ bool Nextion::upload_tft() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((code != 200 && code != 206) || tries > 5) {
 | 
			
		||||
    return this->upload_end_(false);
 | 
			
		||||
    this->upload_end_();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String content_range_string = http.header("Content-Range");
 | 
			
		||||
@@ -204,7 +203,7 @@ bool Nextion::upload_tft() {
 | 
			
		||||
 | 
			
		||||
  if (this->content_length_ < 4096) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to get file size");
 | 
			
		||||
    return this->upload_end_(false);
 | 
			
		||||
    this->upload_end_();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Updating Nextion %s...", this->device_model_.c_str());
 | 
			
		||||
@@ -247,7 +246,7 @@ bool Nextion::upload_tft() {
 | 
			
		||||
    ESP_LOGD(TAG, "preparation for tft update done");
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGD(TAG, "preparation for tft update failed %d \"%s\"", response[0], response.c_str());
 | 
			
		||||
    return this->upload_end_(false);
 | 
			
		||||
    this->upload_end_();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Nextion wants 4096 bytes at a time. Make chunk_size a multiple of 4096
 | 
			
		||||
@@ -281,7 +280,7 @@ bool Nextion::upload_tft() {
 | 
			
		||||
      this->transfer_buffer_ = allocator.allocate(chunk_size);
 | 
			
		||||
 | 
			
		||||
      if (!this->transfer_buffer_)
 | 
			
		||||
        return this->upload_end_(false);
 | 
			
		||||
        this->upload_end_();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->transfer_buffer_size_ = chunk_size;
 | 
			
		||||
@@ -296,7 +295,7 @@ bool Nextion::upload_tft() {
 | 
			
		||||
    result = this->upload_by_chunks_(&http, result);
 | 
			
		||||
    if (result < 0) {
 | 
			
		||||
      ESP_LOGD(TAG, "Error updating Nextion!");
 | 
			
		||||
      return this->upload_end_(false);
 | 
			
		||||
      this->upload_end_();
 | 
			
		||||
    }
 | 
			
		||||
    App.feed_wdt();
 | 
			
		||||
    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
 | 
			
		||||
@@ -304,19 +303,15 @@ bool Nextion::upload_tft() {
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "Successfully updated Nextion!");
 | 
			
		||||
 | 
			
		||||
  return this->upload_end_(true);
 | 
			
		||||
  this->upload_end_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Nextion::upload_end_(bool successful) {
 | 
			
		||||
  this->is_updating_ = false;
 | 
			
		||||
void Nextion::upload_end_() {
 | 
			
		||||
  ESP_LOGD(TAG, "Restarting Nextion");
 | 
			
		||||
  this->soft_reset();
 | 
			
		||||
  if (successful) {
 | 
			
		||||
    delay(1500);  // NOLINT
 | 
			
		||||
    ESP_LOGD(TAG, "Restarting esphome");
 | 
			
		||||
    ESP.restart();  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
  }
 | 
			
		||||
  return successful;
 | 
			
		||||
  delay(1500);  // NOLINT
 | 
			
		||||
  ESP_LOGD(TAG, "Restarting esphome");
 | 
			
		||||
  ESP.restart();  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
@@ -342,4 +337,3 @@ WiFiClient *Nextion::get_wifi_client_() {
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
#endif  // ARDUINO
 | 
			
		||||
@@ -1,268 +0,0 @@
 | 
			
		||||
#include "nextion.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
#ifdef USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
 | 
			
		||||
#include <esp_heap_caps.h>
 | 
			
		||||
#include <esp_http_client.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace nextion {
 | 
			
		||||
static const char *const TAG = "nextion_upload";
 | 
			
		||||
 | 
			
		||||
// Followed guide
 | 
			
		||||
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
 | 
			
		||||
 | 
			
		||||
int Nextion::upload_range(const std::string &url, int range_start) {
 | 
			
		||||
  ESP_LOGVV(TAG, "url: %s", url.c_str());
 | 
			
		||||
  uint range_size = this->tft_size_ - range_start;
 | 
			
		||||
  ESP_LOGVV(TAG, "tft_size_: %i", this->tft_size_);
 | 
			
		||||
  ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size());
 | 
			
		||||
  int range_end = (range_start == 0) ? std::min(this->tft_size_, 16383) : this->tft_size_;
 | 
			
		||||
  if (range_size <= 0 or range_end <= range_start) {
 | 
			
		||||
    ESP_LOGE(TAG, "Invalid range");
 | 
			
		||||
    ESP_LOGD(TAG, "Range start: %i", range_start);
 | 
			
		||||
    ESP_LOGD(TAG, "Range end: %i", range_end);
 | 
			
		||||
    ESP_LOGD(TAG, "Range size: %i", range_size);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  esp_http_client_config_t config = {
 | 
			
		||||
      .url = url.c_str(),
 | 
			
		||||
      .cert_pem = nullptr,
 | 
			
		||||
  };
 | 
			
		||||
  esp_http_client_handle_t client = esp_http_client_init(&config);
 | 
			
		||||
 | 
			
		||||
  char range_header[64];
 | 
			
		||||
  sprintf(range_header, "bytes=%d-%d", range_start, range_end);
 | 
			
		||||
  ESP_LOGV(TAG, "Requesting range: %s", range_header);
 | 
			
		||||
  esp_http_client_set_header(client, "Range", range_header);
 | 
			
		||||
  ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size());
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Opening http connetion");
 | 
			
		||||
  esp_err_t err;
 | 
			
		||||
  if ((err = esp_http_client_open(client, 0)) != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
 | 
			
		||||
    esp_http_client_cleanup(client);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Fetch content length");
 | 
			
		||||
  int content_length = esp_http_client_fetch_headers(client);
 | 
			
		||||
  ESP_LOGV(TAG, "content_length = %d", content_length);
 | 
			
		||||
  if (content_length <= 0) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to get content length: %d", content_length);
 | 
			
		||||
    esp_http_client_cleanup(client);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int total_read_len = 0, read_len;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Allocate buffer");
 | 
			
		||||
  uint8_t *buffer = new uint8_t[4096];
 | 
			
		||||
  std::string recv_string;
 | 
			
		||||
  if (buffer == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to allocate memory for buffer");
 | 
			
		||||
    ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size());
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGV(TAG, "Memory for buffer allocated successfully");
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
      App.feed_wdt();
 | 
			
		||||
      ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size());
 | 
			
		||||
      int read_len = esp_http_client_read(client, reinterpret_cast<char *>(buffer), 4096);
 | 
			
		||||
      ESP_LOGVV(TAG, "Read %d bytes from HTTP client, writing to UART", read_len);
 | 
			
		||||
      if (read_len > 0) {
 | 
			
		||||
        this->write_array(buffer, read_len);
 | 
			
		||||
        ESP_LOGVV(TAG, "Write to UART successful");
 | 
			
		||||
        this->recv_ret_string_(recv_string, 5000, true);
 | 
			
		||||
        this->content_length_ -= read_len;
 | 
			
		||||
        ESP_LOGD(TAG, "Uploaded %0.2f %%, remaining %d bytes",
 | 
			
		||||
                 100.0 * (this->tft_size_ - this->content_length_) / this->tft_size_, this->content_length_);
 | 
			
		||||
        if (recv_string[0] != 0x05) {  // 0x05 == "ok"
 | 
			
		||||
          ESP_LOGD(
 | 
			
		||||
              TAG, "recv_string [%s]",
 | 
			
		||||
              format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
 | 
			
		||||
        }
 | 
			
		||||
        // handle partial upload request
 | 
			
		||||
        if (recv_string[0] == 0x08 && recv_string.size() == 5) {
 | 
			
		||||
          uint32_t result = 0;
 | 
			
		||||
          for (int j = 0; j < 4; ++j) {
 | 
			
		||||
            result += static_cast<uint8_t>(recv_string[j + 1]) << (8 * j);
 | 
			
		||||
          }
 | 
			
		||||
          if (result > 0) {
 | 
			
		||||
            ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result);
 | 
			
		||||
            this->content_length_ = this->tft_size_ - result;
 | 
			
		||||
            // Deallocate the buffer when done
 | 
			
		||||
            delete[] buffer;
 | 
			
		||||
            ESP_LOGVV(TAG, "Memory for buffer deallocated");
 | 
			
		||||
            esp_http_client_cleanup(client);
 | 
			
		||||
            esp_http_client_close(client);
 | 
			
		||||
            return result;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        recv_string.clear();
 | 
			
		||||
      } else if (read_len == 0) {
 | 
			
		||||
        ESP_LOGV(TAG, "End of HTTP response reached");
 | 
			
		||||
        break;  // Exit the loop if there is no more data to read
 | 
			
		||||
      } else {
 | 
			
		||||
        ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %d", read_len);
 | 
			
		||||
        break;  // Exit the loop on error
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Deallocate the buffer when done
 | 
			
		||||
    delete[] buffer;
 | 
			
		||||
    ESP_LOGVV(TAG, "Memory for buffer deallocated");
 | 
			
		||||
  }
 | 
			
		||||
  esp_http_client_cleanup(client);
 | 
			
		||||
  esp_http_client_close(client);
 | 
			
		||||
  return range_end + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Nextion::upload_tft() {
 | 
			
		||||
  ESP_LOGD(TAG, "Nextion TFT upload requested");
 | 
			
		||||
  ESP_LOGD(TAG, "url: %s", this->tft_url_.c_str());
 | 
			
		||||
 | 
			
		||||
  if (this->is_updating_) {
 | 
			
		||||
    ESP_LOGW(TAG, "Currently updating");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!network::is_connected()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Network is not connected");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->is_updating_ = true;
 | 
			
		||||
 | 
			
		||||
  // Define the configuration for the HTTP client
 | 
			
		||||
  ESP_LOGV(TAG, "Establishing connection to HTTP server");
 | 
			
		||||
  ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size());
 | 
			
		||||
  esp_http_client_config_t config = {
 | 
			
		||||
      .url = this->tft_url_.c_str(),
 | 
			
		||||
      .cert_pem = nullptr,
 | 
			
		||||
      .method = HTTP_METHOD_HEAD,
 | 
			
		||||
      .timeout_ms = 15000,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Initialize the HTTP client with the configuration
 | 
			
		||||
  ESP_LOGV(TAG, "Initializing HTTP client");
 | 
			
		||||
  ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size());
 | 
			
		||||
  esp_http_client_handle_t http = esp_http_client_init(&config);
 | 
			
		||||
  if (!http) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to initialize HTTP client.");
 | 
			
		||||
    return this->upload_end(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Perform the HTTP request
 | 
			
		||||
  ESP_LOGV(TAG, "Check if the client could connect");
 | 
			
		||||
  ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size());
 | 
			
		||||
  esp_err_t err = esp_http_client_perform(http);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
 | 
			
		||||
    esp_http_client_cleanup(http);
 | 
			
		||||
    return this->upload_end(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check the HTTP Status Code
 | 
			
		||||
  int status_code = esp_http_client_get_status_code(http);
 | 
			
		||||
  ESP_LOGV(TAG, "HTTP Status Code: %d", status_code);
 | 
			
		||||
  size_t tft_file_size = esp_http_client_get_content_length(http);
 | 
			
		||||
  ESP_LOGD(TAG, "TFT file size: %zu", tft_file_size);
 | 
			
		||||
 | 
			
		||||
  if (tft_file_size < 4096) {
 | 
			
		||||
    ESP_LOGE(TAG, "File size check failed. Size: %zu", tft_file_size);
 | 
			
		||||
    esp_http_client_cleanup(http);
 | 
			
		||||
    return this->upload_end(false);
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGV(TAG, "File size check passed. Proceeding...");
 | 
			
		||||
  }
 | 
			
		||||
  this->content_length_ = tft_file_size;
 | 
			
		||||
  this->tft_size_ = tft_file_size;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Updating Nextion");
 | 
			
		||||
  // The Nextion will ignore the update command if it is sleeping
 | 
			
		||||
 | 
			
		||||
  this->send_command_("sleep=0");
 | 
			
		||||
  this->set_backlight_brightness(1.0);
 | 
			
		||||
  vTaskDelay(pdMS_TO_TICKS(250));  // NOLINT
 | 
			
		||||
 | 
			
		||||
  App.feed_wdt();
 | 
			
		||||
  char command[128];
 | 
			
		||||
  // Tells the Nextion the content length of the tft file and baud rate it will be sent at
 | 
			
		||||
  // Once the Nextion accepts the command it will wait until the file is successfully uploaded
 | 
			
		||||
  // If it fails for any reason a power cycle of the display will be needed
 | 
			
		||||
  sprintf(command, "whmi-wris %d,%" PRIu32 ",1", this->content_length_, this->parent_->get_baud_rate());
 | 
			
		||||
 | 
			
		||||
  // Clear serial receive buffer
 | 
			
		||||
  uint8_t d;
 | 
			
		||||
  while (this->available()) {
 | 
			
		||||
    this->read_byte(&d);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  this->send_command_(command);
 | 
			
		||||
 | 
			
		||||
  std::string response;
 | 
			
		||||
  ESP_LOGV(TAG, "Waiting for upgrade response");
 | 
			
		||||
  this->recv_ret_string_(response, 2048, true);  // This can take some time to return
 | 
			
		||||
 | 
			
		||||
  // The Nextion display will, if it's ready to accept data, send a 0x05 byte.
 | 
			
		||||
  ESP_LOGD(TAG, "Upgrade response is [%s]",
 | 
			
		||||
           format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str());
 | 
			
		||||
 | 
			
		||||
  if (response.find(0x05) != std::string::npos) {
 | 
			
		||||
    ESP_LOGV(TAG, "Preparation for tft update done");
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGE(TAG, "Preparation for tft update failed %d \"%s\"", response[0], response.c_str());
 | 
			
		||||
    esp_http_client_cleanup(http);
 | 
			
		||||
    return this->upload_end(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Updating tft from \"%s\" with a file size of %d, Heap Size %" PRIu32, this->tft_url_.c_str(),
 | 
			
		||||
           content_length_, esp_get_free_heap_size());
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Starting transfer by chunks loop");
 | 
			
		||||
  int result = 0;
 | 
			
		||||
  while (content_length_ > 0) {
 | 
			
		||||
    result = upload_range(this->tft_url_.c_str(), result);
 | 
			
		||||
    if (result < 0) {
 | 
			
		||||
      ESP_LOGE(TAG, "Error updating Nextion!");
 | 
			
		||||
      esp_http_client_cleanup(http);
 | 
			
		||||
      return this->upload_end(false);
 | 
			
		||||
    }
 | 
			
		||||
    App.feed_wdt();
 | 
			
		||||
    ESP_LOGV(TAG, "Heap Size %" PRIu32 ", Bytes left %d", esp_get_free_heap_size(), content_length_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Successfully updated Nextion!");
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Close HTTP connection");
 | 
			
		||||
  esp_http_client_close(http);
 | 
			
		||||
  esp_http_client_cleanup(http);
 | 
			
		||||
  return upload_end(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Nextion::upload_end(bool successful) {
 | 
			
		||||
  this->is_updating_ = false;
 | 
			
		||||
  ESP_LOGD(TAG, "Restarting Nextion");
 | 
			
		||||
  this->soft_reset();
 | 
			
		||||
  vTaskDelay(pdMS_TO_TICKS(1500));  // NOLINT
 | 
			
		||||
  if (successful) {
 | 
			
		||||
    ESP_LOGD(TAG, "Restarting esphome");
 | 
			
		||||
    esp_restart();  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
  }
 | 
			
		||||
  return successful;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace nextion
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
#endif  // USE_ESP_IDF
 | 
			
		||||
@@ -52,9 +52,8 @@ RemoteReceiverTrigger = ns.class_(
 | 
			
		||||
    "RemoteReceiverTrigger", automation.Trigger, RemoteReceiverListener
 | 
			
		||||
)
 | 
			
		||||
RemoteTransmitterDumper = ns.class_("RemoteTransmitterDumper")
 | 
			
		||||
RemoteTransmittable = ns.class_("RemoteTransmittable")
 | 
			
		||||
RemoteTransmitterActionBase = ns.class_(
 | 
			
		||||
    "RemoteTransmitterActionBase", RemoteTransmittable, automation.Action
 | 
			
		||||
    "RemoteTransmitterActionBase", automation.Action
 | 
			
		||||
)
 | 
			
		||||
RemoteReceiverBase = ns.class_("RemoteReceiverBase")
 | 
			
		||||
RemoteTransmitterBase = ns.class_("RemoteTransmitterBase")
 | 
			
		||||
@@ -69,30 +68,11 @@ 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)
 | 
			
		||||
 | 
			
		||||
@@ -149,9 +129,10 @@ 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):
 | 
			
		||||
@@ -162,8 +143,9 @@ 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)
 | 
			
		||||
            await register_transmittable(var, config)
 | 
			
		||||
            cg.add(var.set_parent(transmitter))
 | 
			
		||||
            if CONF_REPEAT in config:
 | 
			
		||||
                conf = config[CONF_REPEAT]
 | 
			
		||||
                template_ = await cg.templatable(conf[CONF_TIMES], args, cg.uint32)
 | 
			
		||||
@@ -1557,7 +1539,7 @@ MIDEA_SCHEMA = cv.Schema(
 | 
			
		||||
 | 
			
		||||
@register_binary_sensor("midea", MideaBinarySensor, MIDEA_SCHEMA)
 | 
			
		||||
def midea_binary_sensor(var, config):
 | 
			
		||||
    cg.add(var.set_data(config[CONF_CODE]))
 | 
			
		||||
    cg.add(var.set_code(config[CONF_CODE]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_trigger("midea", MideaTrigger, MideaData)
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,20 @@ class MideaProtocol : public RemoteProtocol<MideaData> {
 | 
			
		||||
  void dump(const MideaData &data) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DECLARE_REMOTE_PROTOCOL(Midea)
 | 
			
		||||
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>;
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class MideaAction : public RemoteTransmitterActionBase<Ts...> {
 | 
			
		||||
  TEMPLATABLE_VALUE(std::vector<uint8_t>, code)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,6 @@ 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);
 | 
			
		||||
@@ -215,7 +213,7 @@ class RCSwitchDumper : public RemoteReceiverDumperBase {
 | 
			
		||||
  bool dump(RemoteReceiveData src) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using RCSwitchTrigger = RemoteReceiverTrigger<RCSwitchBase>;
 | 
			
		||||
using RCSwitchTrigger = RemoteReceiverTrigger<RCSwitchBase, RCSwitchData>;
 | 
			
		||||
 | 
			
		||||
}  // namespace remote_base
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -127,14 +127,6 @@ 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);
 | 
			
		||||
@@ -192,13 +184,12 @@ class RemoteReceiverBinarySensorBase : public binary_sensor::BinarySensorInitial
 | 
			
		||||
 | 
			
		||||
template<typename T> class RemoteProtocol {
 | 
			
		||||
 public:
 | 
			
		||||
  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;
 | 
			
		||||
  virtual void encode(RemoteTransmitData *dst, const T &data) = 0;
 | 
			
		||||
  virtual optional<T> decode(RemoteReceiveData src) = 0;
 | 
			
		||||
  virtual void dump(const T &data) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename T> class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase {
 | 
			
		||||
template<typename T, typename D> class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase {
 | 
			
		||||
 public:
 | 
			
		||||
  RemoteReceiverBinarySensor() : RemoteReceiverBinarySensorBase() {}
 | 
			
		||||
 | 
			
		||||
@@ -210,14 +201,13 @@ template<typename T> class RemoteReceiverBinarySensor : public RemoteReceiverBin
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  void set_data(typename T::ProtocolData data) { data_ = data; }
 | 
			
		||||
  void set_data(D data) { data_ = data; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  typename T::ProtocolData data_;
 | 
			
		||||
  D data_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
class RemoteReceiverTrigger : public Trigger<typename T::ProtocolData>, public RemoteReceiverListener {
 | 
			
		||||
template<typename T, typename D> class RemoteReceiverTrigger : public Trigger<D>, public RemoteReceiverListener {
 | 
			
		||||
 protected:
 | 
			
		||||
  bool on_receive(RemoteReceiveData src) override {
 | 
			
		||||
    auto proto = T();
 | 
			
		||||
@@ -230,36 +220,28 @@ class RemoteReceiverTrigger : public Trigger<typename T::ProtocolData>, public R
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class RemoteTransmittable {
 | 
			
		||||
template<typename... Ts> class RemoteTransmitterActionBase : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  RemoteTransmittable() {}
 | 
			
		||||
  RemoteTransmittable(RemoteTransmitterBase *transmitter) : transmitter_(transmitter) {}
 | 
			
		||||
  void set_transmitter(RemoteTransmitterBase *transmitter) { this->transmitter_ = transmitter; }
 | 
			
		||||
  void set_parent(RemoteTransmitterBase *parent) { this->parent_ = parent; }
 | 
			
		||||
 | 
			
		||||
 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_;
 | 
			
		||||
};
 | 
			
		||||
  TEMPLATABLE_VALUE(uint32_t, send_times);
 | 
			
		||||
  TEMPLATABLE_VALUE(uint32_t, send_wait);
 | 
			
		||||
 | 
			
		||||
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->transmitter_->transmit();
 | 
			
		||||
    auto call = this->parent_->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> class RemoteReceiverDumper : public RemoteReceiverDumperBase {
 | 
			
		||||
template<typename T, typename D> class RemoteReceiverDumper : public RemoteReceiverDumperBase {
 | 
			
		||||
 public:
 | 
			
		||||
  bool dump(RemoteReceiveData src) override {
 | 
			
		||||
    auto proto = T();
 | 
			
		||||
@@ -272,9 +254,9 @@ template<typename T> class RemoteReceiverDumper : public RemoteReceiverDumperBas
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define DECLARE_REMOTE_PROTOCOL_(prefix) \
 | 
			
		||||
  using prefix##BinarySensor = RemoteReceiverBinarySensor<prefix##Protocol>; \
 | 
			
		||||
  using prefix##Trigger = RemoteReceiverTrigger<prefix##Protocol>; \
 | 
			
		||||
  using prefix##Dumper = RemoteReceiverDumper<prefix##Protocol>;
 | 
			
		||||
  using prefix##BinarySensor = RemoteReceiverBinarySensor<prefix##Protocol, prefix##Data>; \
 | 
			
		||||
  using prefix##Trigger = RemoteReceiverTrigger<prefix##Protocol, prefix##Data>; \
 | 
			
		||||
  using prefix##Dumper = RemoteReceiverDumper<prefix##Protocol, prefix##Data>;
 | 
			
		||||
#define DECLARE_REMOTE_PROTOCOL(prefix) DECLARE_REMOTE_PROTOCOL_(prefix)
 | 
			
		||||
 | 
			
		||||
}  // namespace remote_base
 | 
			
		||||
 
 | 
			
		||||
@@ -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, 6, 0)
 | 
			
		||||
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 4, 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, 10, 0)
 | 
			
		||||
ARDUINO_PLATFORM_VERSION = cv.Version(1, 9, 0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _arduino_check_versions(value):
 | 
			
		||||
 
 | 
			
		||||
@@ -18,20 +18,27 @@ DEPENDENCIES = ["api", "microphone"]
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
 | 
			
		||||
CONF_SILENCE_DETECTION = "silence_detection"
 | 
			
		||||
CONF_ON_LISTENING = "on_listening"
 | 
			
		||||
CONF_ON_START = "on_start"
 | 
			
		||||
CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected"
 | 
			
		||||
CONF_ON_STT_END = "on_stt_end"
 | 
			
		||||
CONF_ON_TTS_START = "on_tts_start"
 | 
			
		||||
CONF_ON_TTS_END = "on_tts_end"
 | 
			
		||||
CONF_ON_END = "on_end"
 | 
			
		||||
CONF_ON_ERROR = "on_error"
 | 
			
		||||
CONF_ON_INTENT_END = "on_intent_end"
 | 
			
		||||
CONF_ON_INTENT_START = "on_intent_start"
 | 
			
		||||
CONF_ON_LISTENING = "on_listening"
 | 
			
		||||
CONF_ON_START = "on_start"
 | 
			
		||||
CONF_ON_STT_END = "on_stt_end"
 | 
			
		||||
CONF_ON_STT_VAD_END = "on_stt_vad_end"
 | 
			
		||||
CONF_ON_STT_VAD_START = "on_stt_vad_start"
 | 
			
		||||
CONF_ON_TTS_END = "on_tts_end"
 | 
			
		||||
CONF_ON_TTS_START = "on_tts_start"
 | 
			
		||||
CONF_ON_TTS_STREAM_START = "on_tts_stream_start"
 | 
			
		||||
CONF_ON_TTS_STREAM_END = "on_tts_stream_end"
 | 
			
		||||
CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected"
 | 
			
		||||
 | 
			
		||||
CONF_SILENCE_DETECTION = "silence_detection"
 | 
			
		||||
CONF_USE_WAKE_WORD = "use_wake_word"
 | 
			
		||||
CONF_VAD_THRESHOLD = "vad_threshold"
 | 
			
		||||
 | 
			
		||||
CONF_NOISE_SUPPRESSION_LEVEL = "noise_suppression_level"
 | 
			
		||||
CONF_AUTO_GAIN = "auto_gain"
 | 
			
		||||
CONF_NOISE_SUPPRESSION_LEVEL = "noise_suppression_level"
 | 
			
		||||
CONF_VOLUME_MULTIPLIER = "volume_multiplier"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -51,6 +58,17 @@ IsRunningCondition = voice_assistant_ns.class_(
 | 
			
		||||
    "IsRunningCondition", automation.Condition, cg.Parented.template(VoiceAssistant)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def tts_stream_validate(config):
 | 
			
		||||
    if CONF_SPEAKER not in config and (
 | 
			
		||||
        CONF_ON_TTS_STREAM_START in config or CONF_ON_TTS_STREAM_END in config
 | 
			
		||||
    ):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"{CONF_SPEAKER} is required when using {CONF_ON_TTS_STREAM_START} and/or {CONF_ON_TTS_STREAM_END}"
 | 
			
		||||
        )
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
@@ -88,8 +106,27 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_ON_CLIENT_DISCONNECTED): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_INTENT_START): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_INTENT_END): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_STT_VAD_START): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_STT_VAD_END): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_TTS_STREAM_START): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_TTS_STREAM_END): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    tts_stream_validate,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -177,6 +214,48 @@ async def to_code(config):
 | 
			
		||||
            config[CONF_ON_CLIENT_DISCONNECTED],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if CONF_ON_INTENT_START in config:
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_intent_start_trigger(),
 | 
			
		||||
            [],
 | 
			
		||||
            config[CONF_ON_INTENT_START],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if CONF_ON_INTENT_END in config:
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_intent_end_trigger(),
 | 
			
		||||
            [],
 | 
			
		||||
            config[CONF_ON_INTENT_END],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if CONF_ON_STT_VAD_START in config:
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_stt_vad_start_trigger(),
 | 
			
		||||
            [],
 | 
			
		||||
            config[CONF_ON_STT_VAD_START],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if CONF_ON_STT_VAD_END in config:
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_stt_vad_end_trigger(),
 | 
			
		||||
            [],
 | 
			
		||||
            config[CONF_ON_STT_VAD_END],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if CONF_ON_TTS_STREAM_START in config:
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_tts_stream_start_trigger(),
 | 
			
		||||
            [],
 | 
			
		||||
            config[CONF_ON_TTS_STREAM_START],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if CONF_ON_TTS_STREAM_END in config:
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_tts_stream_end_trigger(),
 | 
			
		||||
            [],
 | 
			
		||||
            config[CONF_ON_TTS_STREAM_END],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_VOICE_ASSISTANT")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ void VoiceAssistant::setup() {
 | 
			
		||||
 | 
			
		||||
  this->socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 | 
			
		||||
  if (socket_ == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Could not create socket.");
 | 
			
		||||
    ESP_LOGW(TAG, "Could not create socket");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -69,7 +69,7 @@ void VoiceAssistant::setup() {
 | 
			
		||||
    ExternalRAMAllocator<uint8_t> speaker_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
    this->speaker_buffer_ = speaker_allocator.allocate(SPEAKER_BUFFER_SIZE);
 | 
			
		||||
    if (this->speaker_buffer_ == nullptr) {
 | 
			
		||||
      ESP_LOGW(TAG, "Could not allocate speaker buffer.");
 | 
			
		||||
      ESP_LOGW(TAG, "Could not allocate speaker buffer");
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -79,7 +79,7 @@ void VoiceAssistant::setup() {
 | 
			
		||||
  ExternalRAMAllocator<int16_t> allocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->input_buffer_ = allocator.allocate(INPUT_BUFFER_SIZE);
 | 
			
		||||
  if (this->input_buffer_ == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Could not allocate input buffer.");
 | 
			
		||||
    ESP_LOGW(TAG, "Could not allocate input buffer");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -89,7 +89,7 @@ void VoiceAssistant::setup() {
 | 
			
		||||
 | 
			
		||||
  this->ring_buffer_ = rb_create(BUFFER_SIZE, sizeof(int16_t));
 | 
			
		||||
  if (this->ring_buffer_ == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Could not allocate ring buffer.");
 | 
			
		||||
    ESP_LOGW(TAG, "Could not allocate ring buffer");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -98,7 +98,7 @@ void VoiceAssistant::setup() {
 | 
			
		||||
  ExternalRAMAllocator<uint8_t> send_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->send_buffer_ = send_allocator.allocate(SEND_BUFFER_SIZE);
 | 
			
		||||
  if (send_buffer_ == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Could not allocate send buffer.");
 | 
			
		||||
    ESP_LOGW(TAG, "Could not allocate send buffer");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -221,8 +221,8 @@ void VoiceAssistant::loop() {
 | 
			
		||||
      msg.audio_settings = audio_settings;
 | 
			
		||||
 | 
			
		||||
      if (this->api_client_ == nullptr || !this->api_client_->send_voice_assistant_request(msg)) {
 | 
			
		||||
        ESP_LOGW(TAG, "Could not request start.");
 | 
			
		||||
        this->error_trigger_->trigger("not-connected", "Could not request start.");
 | 
			
		||||
        ESP_LOGW(TAG, "Could not request start");
 | 
			
		||||
        this->error_trigger_->trigger("not-connected", "Could not request start");
 | 
			
		||||
        this->continuous_ = false;
 | 
			
		||||
        this->set_state_(State::IDLE, State::IDLE);
 | 
			
		||||
        break;
 | 
			
		||||
@@ -280,7 +280,7 @@ void VoiceAssistant::loop() {
 | 
			
		||||
            this->speaker_buffer_size_ += len;
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          ESP_LOGW(TAG, "Receive buffer full.");
 | 
			
		||||
          ESP_LOGW(TAG, "Receive buffer full");
 | 
			
		||||
        }
 | 
			
		||||
        if (this->speaker_buffer_size_ > 0) {
 | 
			
		||||
          size_t written = this->speaker_->play(this->speaker_buffer_, this->speaker_buffer_size_);
 | 
			
		||||
@@ -290,7 +290,7 @@ void VoiceAssistant::loop() {
 | 
			
		||||
            this->speaker_buffer_index_ -= written;
 | 
			
		||||
            this->set_timeout("speaker-timeout", 2000, [this]() { this->speaker_->stop(); });
 | 
			
		||||
          } else {
 | 
			
		||||
            ESP_LOGW(TAG, "Speaker buffer full.");
 | 
			
		||||
            ESP_LOGW(TAG, "Speaker buffer full");
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (this->wait_for_stream_end_) {
 | 
			
		||||
@@ -513,7 +513,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_STT_START:
 | 
			
		||||
      ESP_LOGD(TAG, "STT Started");
 | 
			
		||||
      ESP_LOGD(TAG, "STT started");
 | 
			
		||||
      this->listening_trigger_->trigger();
 | 
			
		||||
      break;
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_STT_END: {
 | 
			
		||||
@@ -525,19 +525,24 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (text.empty()) {
 | 
			
		||||
        ESP_LOGW(TAG, "No text in STT_END event.");
 | 
			
		||||
        ESP_LOGW(TAG, "No text in STT_END event");
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str());
 | 
			
		||||
      this->stt_end_trigger_->trigger(text);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_INTENT_START:
 | 
			
		||||
      ESP_LOGD(TAG, "Intent started");
 | 
			
		||||
      this->intent_start_trigger_->trigger();
 | 
			
		||||
      break;
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_INTENT_END: {
 | 
			
		||||
      for (auto arg : msg.data) {
 | 
			
		||||
        if (arg.name == "conversation_id") {
 | 
			
		||||
          this->conversation_id_ = std::move(arg.value);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this->intent_end_trigger_->trigger();
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_TTS_START: {
 | 
			
		||||
@@ -548,7 +553,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (text.empty()) {
 | 
			
		||||
        ESP_LOGW(TAG, "No text in TTS_START event.");
 | 
			
		||||
        ESP_LOGW(TAG, "No text in TTS_START event");
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGD(TAG, "Response: \"%s\"", text.c_str());
 | 
			
		||||
@@ -566,7 +571,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (url.empty()) {
 | 
			
		||||
        ESP_LOGW(TAG, "No url in TTS_END event.");
 | 
			
		||||
        ESP_LOGW(TAG, "No url in TTS_END event");
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGD(TAG, "Response URL: \"%s\"", url.c_str());
 | 
			
		||||
@@ -627,13 +632,27 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_TTS_STREAM_START: {
 | 
			
		||||
#ifdef USE_SPEAKER
 | 
			
		||||
      this->wait_for_stream_end_ = true;
 | 
			
		||||
      ESP_LOGD(TAG, "TTS stream start");
 | 
			
		||||
      this->tts_stream_start_trigger_->trigger();
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_TTS_STREAM_END: {
 | 
			
		||||
      this->set_state_(State::RESPONSE_FINISHED, State::IDLE);
 | 
			
		||||
#ifdef USE_SPEAKER
 | 
			
		||||
      ESP_LOGD(TAG, "TTS stream end");
 | 
			
		||||
      this->tts_stream_end_trigger_->trigger();
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_STT_VAD_START:
 | 
			
		||||
      ESP_LOGD(TAG, "Starting STT by VAD");
 | 
			
		||||
      this->stt_vad_start_trigger_->trigger();
 | 
			
		||||
      break;
 | 
			
		||||
    case api::enums::VOICE_ASSISTANT_STT_VAD_END:
 | 
			
		||||
      ESP_LOGD(TAG, "STT by VAD end");
 | 
			
		||||
      this->stt_vad_end_trigger_->trigger();
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      ESP_LOGD(TAG, "Unhandled event type: %d", msg.event_type);
 | 
			
		||||
      break;
 | 
			
		||||
 
 | 
			
		||||
@@ -100,13 +100,21 @@ class VoiceAssistant : public Component {
 | 
			
		||||
  void set_auto_gain(uint8_t auto_gain) { this->auto_gain_ = auto_gain; }
 | 
			
		||||
  void set_volume_multiplier(float volume_multiplier) { this->volume_multiplier_ = volume_multiplier; }
 | 
			
		||||
 | 
			
		||||
  Trigger<> *get_intent_end_trigger() const { return this->intent_end_trigger_; }
 | 
			
		||||
  Trigger<> *get_intent_start_trigger() const { return this->intent_start_trigger_; }
 | 
			
		||||
  Trigger<> *get_listening_trigger() const { return this->listening_trigger_; }
 | 
			
		||||
  Trigger<> *get_end_trigger() const { return this->end_trigger_; }
 | 
			
		||||
  Trigger<> *get_start_trigger() const { return this->start_trigger_; }
 | 
			
		||||
  Trigger<> *get_stt_vad_end_trigger() const { return this->stt_vad_end_trigger_; }
 | 
			
		||||
  Trigger<> *get_stt_vad_start_trigger() const { return this->stt_vad_start_trigger_; }
 | 
			
		||||
#ifdef USE_SPEAKER
 | 
			
		||||
  Trigger<> *get_tts_stream_start_trigger() const { return this->tts_stream_start_trigger_; }
 | 
			
		||||
  Trigger<> *get_tts_stream_end_trigger() const { return this->tts_stream_end_trigger_; }
 | 
			
		||||
#endif
 | 
			
		||||
  Trigger<> *get_wake_word_detected_trigger() const { return this->wake_word_detected_trigger_; }
 | 
			
		||||
  Trigger<std::string> *get_stt_end_trigger() const { return this->stt_end_trigger_; }
 | 
			
		||||
  Trigger<std::string> *get_tts_start_trigger() const { return this->tts_start_trigger_; }
 | 
			
		||||
  Trigger<std::string> *get_tts_end_trigger() const { return this->tts_end_trigger_; }
 | 
			
		||||
  Trigger<> *get_end_trigger() const { return this->end_trigger_; }
 | 
			
		||||
  Trigger<std::string> *get_tts_start_trigger() const { return this->tts_start_trigger_; }
 | 
			
		||||
  Trigger<std::string, std::string> *get_error_trigger() const { return this->error_trigger_; }
 | 
			
		||||
 | 
			
		||||
  Trigger<> *get_client_connected_trigger() const { return this->client_connected_trigger_; }
 | 
			
		||||
@@ -124,13 +132,21 @@ class VoiceAssistant : public Component {
 | 
			
		||||
  std::unique_ptr<socket::Socket> socket_ = nullptr;
 | 
			
		||||
  struct sockaddr_storage dest_addr_;
 | 
			
		||||
 | 
			
		||||
  Trigger<> *intent_end_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<> *intent_start_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<> *listening_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<> *end_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<> *start_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<> *stt_vad_start_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<> *stt_vad_end_trigger_ = new Trigger<>();
 | 
			
		||||
#ifdef USE_SPEAKER
 | 
			
		||||
  Trigger<> *tts_stream_start_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<> *tts_stream_end_trigger_ = new Trigger<>();
 | 
			
		||||
#endif
 | 
			
		||||
  Trigger<> *wake_word_detected_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<std::string> *stt_end_trigger_ = new Trigger<std::string>();
 | 
			
		||||
  Trigger<std::string> *tts_start_trigger_ = new Trigger<std::string>();
 | 
			
		||||
  Trigger<std::string> *tts_end_trigger_ = new Trigger<std::string>();
 | 
			
		||||
  Trigger<> *end_trigger_ = new Trigger<>();
 | 
			
		||||
  Trigger<std::string> *tts_start_trigger_ = new Trigger<std::string>();
 | 
			
		||||
  Trigger<std::string, std::string> *error_trigger_ = new Trigger<std::string, std::string>();
 | 
			
		||||
 | 
			
		||||
  Trigger<> *client_connected_trigger_ = new Trigger<>();
 | 
			
		||||
 
 | 
			
		||||
@@ -389,6 +389,10 @@ void WiFiComponent::print_connect_params_() {
 | 
			
		||||
  bssid_t bssid = wifi_bssid();
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Local MAC: %s", get_mac_address_pretty().c_str());
 | 
			
		||||
  if (this->is_disabled()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  WiFi is disabled!");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  IP Address: %s", wifi_sta_ip().str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3],
 | 
			
		||||
 
 | 
			
		||||
@@ -674,6 +674,11 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (it.number == 0) {
 | 
			
		||||
      // no results
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint16_t number = it.number;
 | 
			
		||||
    std::vector<wifi_ap_record_t> records(number);
 | 
			
		||||
    err = esp_wifi_scan_get_ap_records(&number, records.data());
 | 
			
		||||
 
 | 
			
		||||
@@ -43,17 +43,11 @@ def validate_mode(mode):
 | 
			
		||||
    return mode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_pin(pin):
 | 
			
		||||
    if pin in (8, 9):
 | 
			
		||||
        raise cv.Invalid(f"pin {pin} doesn't exist")
 | 
			
		||||
    return pin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
XL9535_PIN_SCHEMA = cv.All(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(XL9535GPIOPin),
 | 
			
		||||
        cv.Required(CONF_XL9535): cv.use_id(XL9535Component),
 | 
			
		||||
        cv.Required(CONF_NUMBER): cv.All(cv.int_range(min=0, max=17), validate_pin),
 | 
			
		||||
        cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15),
 | 
			
		||||
        cv.Optional(CONF_MODE, default={}): cv.All(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Optional(CONF_INPUT, default=False): cv.boolean,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
"""Constants used by esphome."""
 | 
			
		||||
 | 
			
		||||
__version__ = "2023.12.0-dev"
 | 
			
		||||
__version__ = "2023.11.3"
 | 
			
		||||
 | 
			
		||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
 | 
			
		||||
VALID_SUBSTITUTIONS_CHARACTERS = (
 | 
			
		||||
 
 | 
			
		||||
@@ -973,6 +973,7 @@ class MDNSStatusThread(threading.Thread):
 | 
			
		||||
        self.host_name_to_filename: dict[str, str] = {}
 | 
			
		||||
        # This is a set of host names to track (i.e no_mdns = false)
 | 
			
		||||
        self.host_name_with_mdns_enabled: set[set] = set()
 | 
			
		||||
        self.zc: EsphomeZeroconf | None = None
 | 
			
		||||
        self._refresh_hosts()
 | 
			
		||||
 | 
			
		||||
    def _refresh_hosts(self):
 | 
			
		||||
@@ -996,7 +997,14 @@ class MDNSStatusThread(threading.Thread):
 | 
			
		||||
            # If we just adopted/imported this host, we likely
 | 
			
		||||
            # already have a state for it, so we should make sure
 | 
			
		||||
            # to set it so the dashboard shows it as online
 | 
			
		||||
            if name in host_mdns_state:
 | 
			
		||||
            if self.zc and (
 | 
			
		||||
                entry.loaded_integrations and "api" not in entry.loaded_integrations
 | 
			
		||||
            ):
 | 
			
		||||
                # No api available so we have to poll since
 | 
			
		||||
                # the device won't respond to a request to ._esphomelib._tcp.local.
 | 
			
		||||
                PING_RESULT[filename] = bool(self.zc.resolve_host(entry.name))
 | 
			
		||||
            elif name in host_mdns_state:
 | 
			
		||||
                # We already have a state for this host
 | 
			
		||||
                PING_RESULT[filename] = host_mdns_state[name]
 | 
			
		||||
 | 
			
		||||
            # Make sure the mapping is up to date
 | 
			
		||||
@@ -1007,7 +1015,8 @@ class MDNSStatusThread(threading.Thread):
 | 
			
		||||
    def run(self):
 | 
			
		||||
        global IMPORT_RESULT
 | 
			
		||||
 | 
			
		||||
        zc = EsphomeZeroconf()
 | 
			
		||||
        self.zc = EsphomeZeroconf()
 | 
			
		||||
        zc = self.zc
 | 
			
		||||
        host_mdns_state = self.host_mdns_state
 | 
			
		||||
        host_name_to_filename = self.host_name_to_filename
 | 
			
		||||
        host_name_with_mdns_enabled = self.host_name_with_mdns_enabled
 | 
			
		||||
 
 | 
			
		||||
@@ -23,14 +23,6 @@ from esphome.core import (
 | 
			
		||||
from esphome.helpers import add_class_to_obj
 | 
			
		||||
from esphome.util import OrderedDict, filter_yaml_files
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from yaml import CSafeLoader as FastestAvailableSafeLoader
 | 
			
		||||
except ImportError:
 | 
			
		||||
    from yaml import (  # type: ignore[assignment]
 | 
			
		||||
        SafeLoader as FastestAvailableSafeLoader,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
# Mostly copied from Home Assistant because that code works fine and
 | 
			
		||||
@@ -97,7 +89,7 @@ def _add_data_ref(fn):
 | 
			
		||||
    return wrapped
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ESPHomeLoader(FastestAvailableSafeLoader):
 | 
			
		||||
class ESPHomeLoader(yaml.SafeLoader):
 | 
			
		||||
    """Loader class that keeps track of line numbers."""
 | 
			
		||||
 | 
			
		||||
    @_add_data_ref
 | 
			
		||||
 
 | 
			
		||||
@@ -150,7 +150,11 @@ class EsphomeZeroconf(Zeroconf):
 | 
			
		||||
    def resolve_host(self, host: str, timeout: float = 3.0) -> str | None:
 | 
			
		||||
        """Resolve a host name to an IP address."""
 | 
			
		||||
        name = host.partition(".")[0]
 | 
			
		||||
        info = HostResolver(ESPHOME_SERVICE_TYPE, f"{name}.{ESPHOME_SERVICE_TYPE}")
 | 
			
		||||
        info = HostResolver(
 | 
			
		||||
            ESPHOME_SERVICE_TYPE,
 | 
			
		||||
            f"{name}.{ESPHOME_SERVICE_TYPE}",
 | 
			
		||||
            server=f"{name}.local.",
 | 
			
		||||
        )
 | 
			
		||||
        if (
 | 
			
		||||
            info.load_from_cache(self)
 | 
			
		||||
            or (timeout and info.request(self, timeout * 1000))
 | 
			
		||||
 
 | 
			
		||||
@@ -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.6.0/rp2040-3.6.0.zip
 | 
			
		||||
    earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.4.0/rp2040-3.4.0.zip
 | 
			
		||||
 | 
			
		||||
framework = arduino
 | 
			
		||||
lib_deps =
 | 
			
		||||
 
 | 
			
		||||
@@ -10,8 +10,8 @@ platformio==6.1.11  # When updating platformio, also update Dockerfile
 | 
			
		||||
esptool==4.6.2
 | 
			
		||||
click==8.1.7
 | 
			
		||||
esphome-dashboard==20231107.0
 | 
			
		||||
aioesphomeapi==18.4.0
 | 
			
		||||
zeroconf==0.126.0
 | 
			
		||||
aioesphomeapi==18.5.2
 | 
			
		||||
zeroconf==0.123.0
 | 
			
		||||
 | 
			
		||||
# esp-idf requires this, but doesn't bundle it by default
 | 
			
		||||
# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
pylint==2.17.6
 | 
			
		||||
flake8==6.1.0  # also change in .pre-commit-config.yaml when updating
 | 
			
		||||
black==23.11.0  # also change in .pre-commit-config.yaml when updating
 | 
			
		||||
black==23.10.1  # also change in .pre-commit-config.yaml when updating
 | 
			
		||||
pyupgrade==3.15.0  # also change in .pre-commit-config.yaml when updating
 | 
			
		||||
pre-commit
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3050,9 +3050,6 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -425,15 +425,6 @@ binary_sensor:
 | 
			
		||||
        input: true
 | 
			
		||||
      inverted: false
 | 
			
		||||
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    name: XL9535 Pin 17
 | 
			
		||||
    pin:
 | 
			
		||||
      xl9535: xl9535_hub
 | 
			
		||||
      number: 17
 | 
			
		||||
      mode:
 | 
			
		||||
        input: true
 | 
			
		||||
      inverted: false
 | 
			
		||||
 | 
			
		||||
climate:
 | 
			
		||||
  - platform: tuya
 | 
			
		||||
    id: tuya_climate
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user