mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Merge branch 'integration' into memory_api
This commit is contained in:
		| @@ -96,8 +96,11 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ | ||||
|   BLEClientWriteAction(BLEClient *ble_client) { | ||||
|     ble_client->register_ble_node(this); | ||||
|     ble_client_ = ble_client; | ||||
|     this->construct_simple_value_(); | ||||
|   } | ||||
|  | ||||
|   ~BLEClientWriteAction() { this->destroy_simple_value_(); } | ||||
|  | ||||
|   void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } | ||||
|   void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } | ||||
|   void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } | ||||
| @@ -106,14 +109,18 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ | ||||
|   void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } | ||||
|   void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } | ||||
|  | ||||
|   void set_value_template(std::function<std::vector<uint8_t>(Ts...)> func) { | ||||
|     this->value_template_ = std::move(func); | ||||
|     has_simple_value_ = false; | ||||
|   void set_value_template(std::vector<uint8_t> (*func)(Ts...)) { | ||||
|     this->destroy_simple_value_(); | ||||
|     this->value_.template_func = func; | ||||
|     this->has_simple_value_ = false; | ||||
|   } | ||||
|  | ||||
|   void set_value_simple(const std::vector<uint8_t> &value) { | ||||
|     this->value_simple_ = value; | ||||
|     has_simple_value_ = true; | ||||
|     if (!this->has_simple_value_) { | ||||
|       this->construct_simple_value_(); | ||||
|     } | ||||
|     this->value_.simple = value; | ||||
|     this->has_simple_value_ = true; | ||||
|   } | ||||
|  | ||||
|   void play(Ts... x) override {} | ||||
| @@ -121,7 +128,7 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ | ||||
|   void play_complex(Ts... x) override { | ||||
|     this->num_running_++; | ||||
|     this->var_ = std::make_tuple(x...); | ||||
|     auto value = this->has_simple_value_ ? this->value_simple_ : this->value_template_(x...); | ||||
|     auto value = this->has_simple_value_ ? this->value_.simple : this->value_.template_func(x...); | ||||
|     // on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work. | ||||
|     if (!write(value)) | ||||
|       this->play_next_(x...); | ||||
| @@ -194,10 +201,22 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   void construct_simple_value_() { new (&this->value_.simple) std::vector<uint8_t>(); } | ||||
|  | ||||
|   void destroy_simple_value_() { | ||||
|     if (this->has_simple_value_) { | ||||
|       this->value_.simple.~vector(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   BLEClient *ble_client_; | ||||
|   bool has_simple_value_ = true; | ||||
|   std::vector<uint8_t> value_simple_; | ||||
|   std::function<std::vector<uint8_t>(Ts...)> value_template_{}; | ||||
|   union Value { | ||||
|     std::vector<uint8_t> simple; | ||||
|     std::vector<uint8_t> (*template_func)(Ts...); | ||||
|     Value() {}   // trivial constructor | ||||
|     ~Value() {}  // trivial destructor - we manage lifetime via discriminator | ||||
|   } value_; | ||||
|   espbt::ESPBTUUID service_uuid_; | ||||
|   espbt::ESPBTUUID char_uuid_; | ||||
|   std::tuple<Ts...> var_{}; | ||||
| @@ -213,9 +232,9 @@ template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts... | ||||
|   void play(Ts... x) override { | ||||
|     uint32_t passkey; | ||||
|     if (has_simple_value_) { | ||||
|       passkey = this->value_simple_; | ||||
|       passkey = this->value_.simple; | ||||
|     } else { | ||||
|       passkey = this->value_template_(x...); | ||||
|       passkey = this->value_.template_func(x...); | ||||
|     } | ||||
|     if (passkey > 999999) | ||||
|       return; | ||||
| @@ -224,21 +243,23 @@ template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts... | ||||
|     esp_ble_passkey_reply(remote_bda, true, passkey); | ||||
|   } | ||||
|  | ||||
|   void set_value_template(std::function<uint32_t(Ts...)> func) { | ||||
|     this->value_template_ = std::move(func); | ||||
|     has_simple_value_ = false; | ||||
|   void set_value_template(uint32_t (*func)(Ts...)) { | ||||
|     this->value_.template_func = func; | ||||
|     this->has_simple_value_ = false; | ||||
|   } | ||||
|  | ||||
|   void set_value_simple(const uint32_t &value) { | ||||
|     this->value_simple_ = value; | ||||
|     has_simple_value_ = true; | ||||
|     this->value_.simple = value; | ||||
|     this->has_simple_value_ = true; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   BLEClient *parent_{nullptr}; | ||||
|   bool has_simple_value_ = true; | ||||
|   uint32_t value_simple_{0}; | ||||
|   std::function<uint32_t(Ts...)> value_template_{}; | ||||
|   union { | ||||
|     uint32_t simple; | ||||
|     uint32_t (*template_func)(Ts...); | ||||
|   } value_{.simple = 0}; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Action<Ts...> { | ||||
| @@ -249,27 +270,29 @@ template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Ac | ||||
|     esp_bd_addr_t remote_bda; | ||||
|     memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t)); | ||||
|     if (has_simple_value_) { | ||||
|       esp_ble_confirm_reply(remote_bda, this->value_simple_); | ||||
|       esp_ble_confirm_reply(remote_bda, this->value_.simple); | ||||
|     } else { | ||||
|       esp_ble_confirm_reply(remote_bda, this->value_template_(x...)); | ||||
|       esp_ble_confirm_reply(remote_bda, this->value_.template_func(x...)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void set_value_template(std::function<bool(Ts...)> func) { | ||||
|     this->value_template_ = std::move(func); | ||||
|     has_simple_value_ = false; | ||||
|   void set_value_template(bool (*func)(Ts...)) { | ||||
|     this->value_.template_func = func; | ||||
|     this->has_simple_value_ = false; | ||||
|   } | ||||
|  | ||||
|   void set_value_simple(const bool &value) { | ||||
|     this->value_simple_ = value; | ||||
|     has_simple_value_ = true; | ||||
|     this->value_.simple = value; | ||||
|     this->has_simple_value_ = true; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   BLEClient *parent_{nullptr}; | ||||
|   bool has_simple_value_ = true; | ||||
|   bool value_simple_{false}; | ||||
|   std::function<bool(Ts...)> value_template_{}; | ||||
|   union { | ||||
|     bool simple; | ||||
|     bool (*template_func)(Ts...); | ||||
|   } value_{.simple = false}; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...> { | ||||
|   | ||||
| @@ -117,9 +117,9 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga | ||||
| } | ||||
|  | ||||
| float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) { | ||||
|   if (this->data_to_value_func_.has_value()) { | ||||
|   if (this->has_data_to_value_) { | ||||
|     std::vector<uint8_t> data(value, value + value_len); | ||||
|     return (*this->data_to_value_func_)(data); | ||||
|     return this->data_to_value_func_(data); | ||||
|   } else { | ||||
|     return value[0]; | ||||
|   } | ||||
|   | ||||
| @@ -15,8 +15,6 @@ namespace ble_client { | ||||
|  | ||||
| namespace espbt = esphome::esp32_ble_tracker; | ||||
|  | ||||
| using data_to_value_t = std::function<float(std::vector<uint8_t>)>; | ||||
|  | ||||
| class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode { | ||||
|  public: | ||||
|   void loop() override; | ||||
| @@ -33,13 +31,17 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie | ||||
|   void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } | ||||
|   void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } | ||||
|   void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } | ||||
|   void set_data_to_value(data_to_value_t &&lambda) { this->data_to_value_func_ = lambda; } | ||||
|   void set_data_to_value(float (*lambda)(const std::vector<uint8_t> &)) { | ||||
|     this->data_to_value_func_ = lambda; | ||||
|     this->has_data_to_value_ = true; | ||||
|   } | ||||
|   void set_enable_notify(bool notify) { this->notify_ = notify; } | ||||
|   uint16_t handle; | ||||
|  | ||||
|  protected: | ||||
|   float parse_data_(uint8_t *value, uint16_t value_len); | ||||
|   optional<data_to_value_t> data_to_value_func_{}; | ||||
|   bool has_data_to_value_{false}; | ||||
|   float (*data_to_value_func_)(const std::vector<uint8_t> &){}; | ||||
|   bool notify_; | ||||
|   espbt::ESPBTUUID service_uuid_; | ||||
|   espbt::ESPBTUUID char_uuid_; | ||||
|   | ||||
| @@ -124,7 +124,7 @@ class HttpRequestComponent : public Component { | ||||
|   float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } | ||||
|  | ||||
|   void set_useragent(const char *useragent) { this->useragent_ = useragent; } | ||||
|   void set_timeout(uint16_t timeout) { this->timeout_ = timeout; } | ||||
|   void set_timeout(uint32_t timeout) { this->timeout_ = timeout; } | ||||
|   void set_watchdog_timeout(uint32_t watchdog_timeout) { this->watchdog_timeout_ = watchdog_timeout; } | ||||
|   uint32_t get_watchdog_timeout() const { return this->watchdog_timeout_; } | ||||
|   void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; } | ||||
| @@ -173,7 +173,7 @@ class HttpRequestComponent : public Component { | ||||
|   const char *useragent_{nullptr}; | ||||
|   bool follow_redirects_{}; | ||||
|   uint16_t redirect_limit_{}; | ||||
|   uint16_t timeout_{4500}; | ||||
|   uint32_t timeout_{4500}; | ||||
|   uint32_t watchdog_timeout_{0}; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ from esphome.const import ( | ||||
|     CONF_FAMILY, | ||||
|     CONF_GROUP, | ||||
|     CONF_ID, | ||||
|     CONF_INDEX, | ||||
|     CONF_INVERTED, | ||||
|     CONF_LEVEL, | ||||
|     CONF_MAGNITUDE, | ||||
| @@ -616,6 +617,49 @@ async def dooya_action(var, config, args): | ||||
|     cg.add(var.set_check(template_)) | ||||
|  | ||||
|  | ||||
| # Dyson | ||||
| DysonData, DysonBinarySensor, DysonTrigger, DysonAction, DysonDumper = declare_protocol( | ||||
|     "Dyson" | ||||
| ) | ||||
| DYSON_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_CODE): cv.hex_uint16_t, | ||||
|         cv.Optional(CONF_INDEX, default=0xFF): cv.hex_uint8_t, | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @register_binary_sensor("dyson", DysonBinarySensor, DYSON_SCHEMA) | ||||
| def dyson_binary_sensor(var, config): | ||||
|     cg.add( | ||||
|         var.set_data( | ||||
|             cg.StructInitializer( | ||||
|                 DysonData, | ||||
|                 ("code", config[CONF_CODE]), | ||||
|                 ("index", config[CONF_INDEX]), | ||||
|             ) | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @register_trigger("dyson", DysonTrigger, DysonData) | ||||
| def dyson_trigger(var, config): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @register_dumper("dyson", DysonDumper) | ||||
| def dyson_dumper(var, config): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @register_action("dyson", DysonAction, DYSON_SCHEMA) | ||||
| async def dyson_action(var, config, args): | ||||
|     template_ = await cg.templatable(config[CONF_CODE], args, cg.uint16) | ||||
|     cg.add(var.set_code(template_)) | ||||
|     template_ = await cg.templatable(config[CONF_INDEX], args, cg.uint8) | ||||
|     cg.add(var.set_index(template_)) | ||||
|  | ||||
|  | ||||
| # JVC | ||||
| JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol("JVC") | ||||
| JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t}) | ||||
|   | ||||
							
								
								
									
										71
									
								
								esphome/components/remote_base/dyson_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								esphome/components/remote_base/dyson_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| #include "dyson_protocol.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #include <cinttypes> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace remote_base { | ||||
|  | ||||
| static const char *const TAG = "remote.dyson"; | ||||
|  | ||||
| // pulsewidth [µs] | ||||
| constexpr uint32_t PW_MARK_US = 780; | ||||
| constexpr uint32_t PW_SHORT_US = 720; | ||||
| constexpr uint32_t PW_LONG_US = 1500; | ||||
| constexpr uint32_t PW_START_US = 2280; | ||||
|  | ||||
| // MSB of 15 bit dyson code | ||||
| constexpr uint16_t MSB_DYSON = (1 << 14); | ||||
|  | ||||
| // required symbols in transmit buffer = (start_symbol + 15 data_symbols) | ||||
| constexpr uint32_t N_SYMBOLS_REQ = 2u * (1 + 15); | ||||
|  | ||||
| void DysonProtocol::encode(RemoteTransmitData *dst, const DysonData &data) { | ||||
|   uint32_t raw_code = (data.code << 2) + (data.index & 3); | ||||
|   dst->set_carrier_frequency(36000); | ||||
|   dst->reserve(N_SYMBOLS_REQ + 1); | ||||
|   dst->item(PW_START_US, PW_SHORT_US); | ||||
|   for (uint16_t mask = MSB_DYSON; mask != 0; mask >>= 1) { | ||||
|     if (mask == (mask & raw_code)) { | ||||
|       dst->item(PW_MARK_US, PW_LONG_US); | ||||
|     } else { | ||||
|       dst->item(PW_MARK_US, PW_SHORT_US); | ||||
|     } | ||||
|   } | ||||
|   dst->mark(PW_MARK_US);  // final carrier pulse | ||||
| } | ||||
|  | ||||
| optional<DysonData> DysonProtocol::decode(RemoteReceiveData src) { | ||||
|   uint32_t n_received = static_cast<uint32_t>(src.size()); | ||||
|   uint16_t raw_code = 0; | ||||
|   DysonData data{ | ||||
|       .code = 0, | ||||
|       .index = 0, | ||||
|   }; | ||||
|   if (n_received < N_SYMBOLS_REQ) | ||||
|     return {};  // invalid frame length | ||||
|   if (!src.expect_item(PW_START_US, PW_SHORT_US)) | ||||
|     return {};  // start not found | ||||
|   for (uint16_t mask = MSB_DYSON; mask != 0; mask >>= 1) { | ||||
|     if (src.expect_item(PW_MARK_US, PW_SHORT_US)) { | ||||
|       raw_code &= ~mask;  // zero detected | ||||
|     } else if (src.expect_item(PW_MARK_US, PW_LONG_US)) { | ||||
|       raw_code |= mask;  // one detected | ||||
|     } else { | ||||
|       return {};  // invalid data item | ||||
|     } | ||||
|   } | ||||
|   data.code = raw_code >> 2;          // extract button code | ||||
|   data.index = raw_code & 3;          // extract rolling index | ||||
|   if (src.expect_mark(PW_MARK_US)) {  // check total length | ||||
|     return data; | ||||
|   } | ||||
|   return {};  // frame not complete | ||||
| } | ||||
|  | ||||
| void DysonProtocol::dump(const DysonData &data) { | ||||
|   ESP_LOGI(TAG, "Dyson: code=0x%x rolling index=%d", data.code, data.index); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
							
								
								
									
										46
									
								
								esphome/components/remote_base/dyson_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								esphome/components/remote_base/dyson_protocol.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "remote_base.h" | ||||
|  | ||||
| #include <cinttypes> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace remote_base { | ||||
|  | ||||
| static constexpr uint8_t IGNORE_INDEX = 0xFF; | ||||
|  | ||||
| struct DysonData { | ||||
|   uint16_t code;  // the button, e.g. power, swing, fan++, ... | ||||
|   uint8_t index;  // the rolling index counter | ||||
|   bool operator==(const DysonData &rhs) const { | ||||
|     if (IGNORE_INDEX == index || IGNORE_INDEX == rhs.index) { | ||||
|       return code == rhs.code; | ||||
|     } | ||||
|     return code == rhs.code && index == rhs.index; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class DysonProtocol : public RemoteProtocol<DysonData> { | ||||
|  public: | ||||
|   void encode(RemoteTransmitData *dst, const DysonData &data) override; | ||||
|   optional<DysonData> decode(RemoteReceiveData src) override; | ||||
|   void dump(const DysonData &data) override; | ||||
| }; | ||||
|  | ||||
| DECLARE_REMOTE_PROTOCOL(Dyson) | ||||
|  | ||||
| template<typename... Ts> class DysonAction : public RemoteTransmitterActionBase<Ts...> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(uint16_t, code) | ||||
|   TEMPLATABLE_VALUE(uint8_t, index) | ||||
|  | ||||
|   void encode(RemoteTransmitData *dst, Ts... x) override { | ||||
|     DysonData data{}; | ||||
|     data.code = this->code_.value(x...); | ||||
|     data.index = this->index_.value(x...); | ||||
|     DysonProtocol().encode(dst, data); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
| @@ -48,7 +48,6 @@ import sys | ||||
| from typing import Any | ||||
|  | ||||
| from helpers import ( | ||||
|     BASE_BUS_COMPONENTS, | ||||
|     CPP_FILE_EXTENSIONS, | ||||
|     PYTHON_FILE_EXTENSIONS, | ||||
|     changed_files, | ||||
| @@ -453,7 +452,7 @@ def detect_memory_impact_config( | ||||
|     # Get actually changed files (not dependencies) | ||||
|     files = changed_files(branch) | ||||
|  | ||||
|     # Find all changed components (excluding core and base bus components) | ||||
|     # Find all changed components (excluding core) | ||||
|     # Also collect platform hints from platform-specific filenames | ||||
|     changed_component_set: set[str] = set() | ||||
|     has_core_cpp_changes = False | ||||
| @@ -462,13 +461,13 @@ def detect_memory_impact_config( | ||||
|     for file in files: | ||||
|         component = get_component_from_path(file) | ||||
|         if component: | ||||
|             # Skip base bus components as they're used across many builds | ||||
|             if component not in BASE_BUS_COMPONENTS: | ||||
|                 changed_component_set.add(component) | ||||
|                 # Check if this is a platform-specific file | ||||
|                 platform_hint = _detect_platform_hint_from_filename(file) | ||||
|                 if platform_hint: | ||||
|                     platform_hints.append(platform_hint) | ||||
|             # Add all changed components, including base bus components | ||||
|             # Base bus components (uart, i2c, spi, etc.) should still be analyzed | ||||
|             # when directly changed, even though they're also used as dependencies | ||||
|             changed_component_set.add(component) | ||||
|             # Check if this is a platform-specific file | ||||
|             if platform_hint := _detect_platform_hint_from_filename(file): | ||||
|                 platform_hints.append(platform_hint) | ||||
|         elif file.startswith("esphome/") and file.endswith(CPP_FILE_EXTENSIONS): | ||||
|             # Core ESPHome C++ files changed (not component-specific) | ||||
|             # Only C++ files affect memory usage | ||||
|   | ||||
| @@ -3,3 +3,52 @@ esp32_ble_tracker: | ||||
| ble_client: | ||||
|   - mac_address: 01:02:03:04:05:06 | ||||
|     id: test_blec | ||||
|     on_connect: | ||||
|       - ble_client.ble_write: | ||||
|           id: test_blec | ||||
|           service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" | ||||
|           characteristic_uuid: "abcd1235-abcd-1234-abcd-abcd12345678" | ||||
|           value: !lambda |- | ||||
|             return std::vector<uint8_t>{0x01, 0x02, 0x03}; | ||||
|       - ble_client.ble_write: | ||||
|           id: test_blec | ||||
|           service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" | ||||
|           characteristic_uuid: "abcd1235-abcd-1234-abcd-abcd12345678" | ||||
|           value: [0x04, 0x05, 0x06] | ||||
|     on_passkey_request: | ||||
|       - ble_client.passkey_reply: | ||||
|           id: test_blec | ||||
|           passkey: !lambda |- | ||||
|             return 123456; | ||||
|       - ble_client.passkey_reply: | ||||
|           id: test_blec | ||||
|           passkey: 654321 | ||||
|     on_numeric_comparison_request: | ||||
|       - ble_client.numeric_comparison_reply: | ||||
|           id: test_blec | ||||
|           accept: !lambda |- | ||||
|             return true; | ||||
|       - ble_client.numeric_comparison_reply: | ||||
|           id: test_blec | ||||
|           accept: false | ||||
|  | ||||
| sensor: | ||||
|   - platform: ble_client | ||||
|     ble_client_id: test_blec | ||||
|     type: characteristic | ||||
|     id: test_sensor_lambda | ||||
|     name: "BLE Sensor with Lambda" | ||||
|     service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" | ||||
|     characteristic_uuid: "abcd1236-abcd-1234-abcd-abcd12345678" | ||||
|     lambda: |- | ||||
|       if (x.size() >= 2) { | ||||
|         return (float)(x[0] | (x[1] << 8)) / 100.0; | ||||
|       } | ||||
|       return NAN; | ||||
|   - platform: ble_client | ||||
|     ble_client_id: test_blec | ||||
|     type: characteristic | ||||
|     id: test_sensor_no_lambda | ||||
|     name: "BLE Sensor without Lambda" | ||||
|     service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" | ||||
|     characteristic_uuid: "abcd1237-abcd-1234-abcd-abcd12345678" | ||||
|   | ||||
| @@ -48,6 +48,11 @@ on_drayton: | ||||
|     - logger.log: | ||||
|         format: "on_drayton: %u %u %u" | ||||
|         args: ["x.address", "x.channel", "x.command"] | ||||
| on_dyson: | ||||
|   then: | ||||
|     - logger.log: | ||||
|         format: "on_dyson: %u %u" | ||||
|         args: ["x.code", "x.index"] | ||||
| on_gobox: | ||||
|   then: | ||||
|     - logger.log: | ||||
|   | ||||
| @@ -6,6 +6,13 @@ button: | ||||
|       remote_transmitter.transmit_beo4: | ||||
|         source: 0x01 | ||||
|         command: 0x0C | ||||
|   - platform: template | ||||
|     name: Dyson fan up | ||||
|     id: dyson_fan_up | ||||
|     on_press: | ||||
|       remote_transmitter.transmit_dyson: | ||||
|         code: 0x1215 | ||||
|         index: 0x0 | ||||
|   - platform: template | ||||
|     name: JVC Off | ||||
|     id: living_room_lights_on | ||||
|   | ||||
| @@ -19,3 +19,41 @@ uart: | ||||
|  | ||||
| packet_transport: | ||||
|   - platform: uart | ||||
|  | ||||
| switch: | ||||
|   # Test uart switch with single state (array) | ||||
|   - platform: uart | ||||
|     name: "UART Switch Single Array" | ||||
|     uart_id: uart_uart | ||||
|     data: [0x01, 0x02, 0x03] | ||||
|   # Test uart switch with single state (string) | ||||
|   - platform: uart | ||||
|     name: "UART Switch Single String" | ||||
|     uart_id: uart_uart | ||||
|     data: "ON" | ||||
|   # Test uart switch with turn_on/turn_off (arrays) | ||||
|   - platform: uart | ||||
|     name: "UART Switch Dual Array" | ||||
|     uart_id: uart_uart | ||||
|     data: | ||||
|       turn_on: [0xA0, 0xA1, 0xA2] | ||||
|       turn_off: [0xB0, 0xB1, 0xB2] | ||||
|   # Test uart switch with turn_on/turn_off (strings) | ||||
|   - platform: uart | ||||
|     name: "UART Switch Dual String" | ||||
|     uart_id: uart_uart | ||||
|     data: | ||||
|       turn_on: "TURN_ON" | ||||
|       turn_off: "TURN_OFF" | ||||
|  | ||||
| button: | ||||
|   # Test uart button with array data | ||||
|   - platform: uart | ||||
|     name: "UART Button Array" | ||||
|     uart_id: uart_uart | ||||
|     data: [0xFF, 0xEE, 0xDD] | ||||
|   # Test uart button with string data | ||||
|   - platform: uart | ||||
|     name: "UART Button String" | ||||
|     uart_id: uart_uart | ||||
|     data: "BUTTON_PRESS" | ||||
|   | ||||
| @@ -13,3 +13,21 @@ uart: | ||||
|     rx_buffer_size: 512 | ||||
|     parity: EVEN | ||||
|     stop_bits: 2 | ||||
|  | ||||
| switch: | ||||
|   - platform: uart | ||||
|     name: "UART Switch Array" | ||||
|     uart_id: uart_uart | ||||
|     data: [0x01, 0x02, 0x03] | ||||
|   - platform: uart | ||||
|     name: "UART Switch Dual" | ||||
|     uart_id: uart_uart | ||||
|     data: | ||||
|       turn_on: [0xA0, 0xA1] | ||||
|       turn_off: [0xB0, 0xB1] | ||||
|  | ||||
| button: | ||||
|   - platform: uart | ||||
|     name: "UART Button" | ||||
|     uart_id: uart_uart | ||||
|     data: [0xFF, 0xEE] | ||||
|   | ||||
| @@ -849,39 +849,47 @@ def test_detect_memory_impact_config_no_components_with_tests(tmp_path: Path) -> | ||||
|     assert result["should_run"] == "false" | ||||
|  | ||||
|  | ||||
| def test_detect_memory_impact_config_skips_base_bus_components(tmp_path: Path) -> None: | ||||
|     """Test that base bus components (i2c, spi, uart) are skipped.""" | ||||
| def test_detect_memory_impact_config_includes_base_bus_components( | ||||
|     tmp_path: Path, | ||||
| ) -> None: | ||||
|     """Test that base bus components (i2c, spi, uart) are included when directly changed. | ||||
|  | ||||
|     Base bus components should be analyzed for memory impact when they are directly | ||||
|     changed, even though they are often used as dependencies. This ensures that | ||||
|     optimizations to base components (like using move semantics or initializer_list) | ||||
|     are properly measured. | ||||
|     """ | ||||
|     # Create test directory structure | ||||
|     tests_dir = tmp_path / "tests" / "components" | ||||
|  | ||||
|     # i2c component (should be skipped as it's a base bus component) | ||||
|     i2c_dir = tests_dir / "i2c" | ||||
|     i2c_dir.mkdir(parents=True) | ||||
|     (i2c_dir / "test.esp32-idf.yaml").write_text("test: i2c") | ||||
|     # uart component (base bus component that should be included) | ||||
|     uart_dir = tests_dir / "uart" | ||||
|     uart_dir.mkdir(parents=True) | ||||
|     (uart_dir / "test.esp32-idf.yaml").write_text("test: uart") | ||||
|  | ||||
|     # wifi component (should not be skipped) | ||||
|     # wifi component (regular component) | ||||
|     wifi_dir = tests_dir / "wifi" | ||||
|     wifi_dir.mkdir(parents=True) | ||||
|     (wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi") | ||||
|  | ||||
|     # Mock changed_files to return both i2c and wifi | ||||
|     # Mock changed_files to return both uart and wifi | ||||
|     with ( | ||||
|         patch.object(determine_jobs, "root_path", str(tmp_path)), | ||||
|         patch.object(helpers, "root_path", str(tmp_path)), | ||||
|         patch.object(determine_jobs, "changed_files") as mock_changed_files, | ||||
|     ): | ||||
|         mock_changed_files.return_value = [ | ||||
|             "esphome/components/i2c/i2c.cpp", | ||||
|             "esphome/components/uart/automation.h",  # Header file with inline code | ||||
|             "esphome/components/wifi/wifi.cpp", | ||||
|         ] | ||||
|         determine_jobs._component_has_tests.cache_clear() | ||||
|  | ||||
|         result = determine_jobs.detect_memory_impact_config() | ||||
|  | ||||
|     # Should only include wifi, not i2c | ||||
|     # Should include both uart and wifi | ||||
|     assert result["should_run"] == "true" | ||||
|     assert result["components"] == ["wifi"] | ||||
|     assert "i2c" not in result["components"] | ||||
|     assert set(result["components"]) == {"uart", "wifi"} | ||||
|     assert result["platform"] == "esp32-idf"  # Common platform | ||||
|  | ||||
|  | ||||
| def test_detect_memory_impact_config_with_variant_tests(tmp_path: Path) -> None: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user