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:
		
							
								
								
									
										28
									
								
								.github/workflows/auto-label-pr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/auto-label-pr.yml
									
									
									
									
										vendored
									
									
								
							| @@ -105,7 +105,9 @@ jobs: | |||||||
|  |  | ||||||
|             // Calculate data from PR files |             // Calculate data from PR files | ||||||
|             const changedFiles = prFiles.map(file => file.filename); |             const changedFiles = prFiles.map(file => file.filename); | ||||||
|             const totalChanges = prFiles.reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0); |             const totalAdditions = prFiles.reduce((sum, file) => sum + (file.additions || 0), 0); | ||||||
|  |             const totalDeletions = prFiles.reduce((sum, file) => sum + (file.deletions || 0), 0); | ||||||
|  |             const totalChanges = totalAdditions + totalDeletions; | ||||||
|  |  | ||||||
|             console.log('Current labels:', currentLabels.join(', ')); |             console.log('Current labels:', currentLabels.join(', ')); | ||||||
|             console.log('Changed files:', changedFiles.length); |             console.log('Changed files:', changedFiles.length); | ||||||
| @@ -231,16 +233,21 @@ jobs: | |||||||
|             // Strategy: PR size detection |             // Strategy: PR size detection | ||||||
|             async function detectPRSize() { |             async function detectPRSize() { | ||||||
|               const labels = new Set(); |               const labels = new Set(); | ||||||
|               const testChanges = prFiles |  | ||||||
|                 .filter(file => file.filename.startsWith('tests/')) |  | ||||||
|                 .reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0); |  | ||||||
|  |  | ||||||
|               const nonTestChanges = totalChanges - testChanges; |  | ||||||
|  |  | ||||||
|               if (totalChanges <= SMALL_PR_THRESHOLD) { |               if (totalChanges <= SMALL_PR_THRESHOLD) { | ||||||
|                 labels.add('small-pr'); |                 labels.add('small-pr'); | ||||||
|  |                 return labels; | ||||||
|               } |               } | ||||||
|  |  | ||||||
|  |               const testAdditions = prFiles | ||||||
|  |                 .filter(file => file.filename.startsWith('tests/')) | ||||||
|  |                 .reduce((sum, file) => sum + (file.additions || 0), 0); | ||||||
|  |               const testDeletions = prFiles | ||||||
|  |                 .filter(file => file.filename.startsWith('tests/')) | ||||||
|  |                 .reduce((sum, file) => sum + (file.deletions || 0), 0); | ||||||
|  |  | ||||||
|  |               const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions); | ||||||
|  |  | ||||||
|               // Don't add too-big if mega-pr label is already present |               // Don't add too-big if mega-pr label is already present | ||||||
|               if (nonTestChanges > TOO_BIG_THRESHOLD && !isMegaPR) { |               if (nonTestChanges > TOO_BIG_THRESHOLD && !isMegaPR) { | ||||||
|                 labels.add('too-big'); |                 labels.add('too-big'); | ||||||
| @@ -412,10 +419,13 @@ jobs: | |||||||
|  |  | ||||||
|               // Too big message |               // Too big message | ||||||
|               if (finalLabels.includes('too-big')) { |               if (finalLabels.includes('too-big')) { | ||||||
|                 const testChanges = prFiles |                 const testAdditions = prFiles | ||||||
|                   .filter(file => file.filename.startsWith('tests/')) |                   .filter(file => file.filename.startsWith('tests/')) | ||||||
|                   .reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0); |                   .reduce((sum, file) => sum + (file.additions || 0), 0); | ||||||
|                 const nonTestChanges = totalChanges - testChanges; |                 const testDeletions = prFiles | ||||||
|  |                   .filter(file => file.filename.startsWith('tests/')) | ||||||
|  |                   .reduce((sum, file) => sum + (file.deletions || 0), 0); | ||||||
|  |                 const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions); | ||||||
|  |  | ||||||
|                 const tooManyLabels = finalLabels.length > MAX_LABELS; |                 const tooManyLabels = finalLabels.length > MAX_LABELS; | ||||||
|                 const tooManyChanges = nonTestChanges > TOO_BIG_THRESHOLD; |                 const tooManyChanges = nonTestChanges > TOO_BIG_THRESHOLD; | ||||||
|   | |||||||
| @@ -89,7 +89,7 @@ void AGS10Component::dump_config() { | |||||||
| bool AGS10Component::new_i2c_address(uint8_t newaddress) { | bool AGS10Component::new_i2c_address(uint8_t newaddress) { | ||||||
|   uint8_t rev_newaddress = ~newaddress; |   uint8_t rev_newaddress = ~newaddress; | ||||||
|   std::array<uint8_t, 5> data{newaddress, rev_newaddress, newaddress, rev_newaddress, 0}; |   std::array<uint8_t, 5> data{newaddress, rev_newaddress, newaddress, rev_newaddress, 0}; | ||||||
|   data[4] = calc_crc8_(data, 4); |   data[4] = crc8(data.data(), 4, 0xFF, 0x31, true); | ||||||
|   if (!this->write_bytes(REG_ADDRESS, data)) { |   if (!this->write_bytes(REG_ADDRESS, data)) { | ||||||
|     this->error_code_ = COMMUNICATION_FAILED; |     this->error_code_ = COMMUNICATION_FAILED; | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
| @@ -109,7 +109,7 @@ bool AGS10Component::set_zero_point_with_current_resistance() { return this->set | |||||||
|  |  | ||||||
| bool AGS10Component::set_zero_point_with(uint16_t value) { | bool AGS10Component::set_zero_point_with(uint16_t value) { | ||||||
|   std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0}; |   std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0}; | ||||||
|   data[4] = calc_crc8_(data, 4); |   data[4] = crc8(data.data(), 4, 0xFF, 0x31, true); | ||||||
|   if (!this->write_bytes(REG_CALIBRATION, data)) { |   if (!this->write_bytes(REG_CALIBRATION, data)) { | ||||||
|     this->error_code_ = COMMUNICATION_FAILED; |     this->error_code_ = COMMUNICATION_FAILED; | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
| @@ -184,7 +184,7 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che | |||||||
|   auto res = *data; |   auto res = *data; | ||||||
|   auto crc_byte = res[len]; |   auto crc_byte = res[len]; | ||||||
|  |  | ||||||
|   if (crc_byte != calc_crc8_(res, len)) { |   if (crc_byte != crc8(res.data(), len, 0xFF, 0x31, true)) { | ||||||
|     this->error_code_ = CRC_CHECK_FAILED; |     this->error_code_ = CRC_CHECK_FAILED; | ||||||
|     ESP_LOGE(TAG, "Reading AGS10 version failed: crc error!"); |     ESP_LOGE(TAG, "Reading AGS10 version failed: crc error!"); | ||||||
|     return optional<std::array<uint8_t, N>>(); |     return optional<std::array<uint8_t, N>>(); | ||||||
| @@ -192,20 +192,5 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che | |||||||
|  |  | ||||||
|   return data; |   return data; | ||||||
| } | } | ||||||
|  |  | ||||||
| template<size_t N> uint8_t AGS10Component::calc_crc8_(std::array<uint8_t, N> dat, uint8_t num) { |  | ||||||
|   uint8_t i, byte1, crc = 0xFF; |  | ||||||
|   for (byte1 = 0; byte1 < num; byte1++) { |  | ||||||
|     crc ^= (dat[byte1]); |  | ||||||
|     for (i = 0; i < 8; i++) { |  | ||||||
|       if (crc & 0x80) { |  | ||||||
|         crc = (crc << 1) ^ 0x31; |  | ||||||
|       } else { |  | ||||||
|         crc = (crc << 1); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return crc; |  | ||||||
| } |  | ||||||
| }  // namespace ags10 | }  // namespace ags10 | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
| #include "esphome/core/automation.h" | #include "esphome/core/automation.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/components/sensor/sensor.h" |  | ||||||
| #include "esphome/components/i2c/i2c.h" |  | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace ags10 { | namespace ags10 { | ||||||
| @@ -99,16 +99,6 @@ class AGS10Component : public PollingComponent, public i2c::I2CDevice { | |||||||
|    * Read, checks and returns data from the sensor. |    * Read, checks and returns data from the sensor. | ||||||
|    */ |    */ | ||||||
|   template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register); |   template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register); | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Calculates CRC8 value. |  | ||||||
|    * |  | ||||||
|    * CRC8 calculation, initial value: 0xFF, polynomial: 0x31 (x8+ x5+ x4+1) |  | ||||||
|    * |  | ||||||
|    * @param[in] dat the data buffer |  | ||||||
|    * @param num number of bytes in the buffer |  | ||||||
|    */ |  | ||||||
|   template<size_t N> uint8_t calc_crc8_(std::array<uint8_t, N> dat, uint8_t num); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> { | template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> { | ||||||
|   | |||||||
| @@ -18,6 +18,6 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
| ).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) | ).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     yield esp32_ble_tracker.register_ble_device(var, config) |     await esp32_ble_tracker.register_ble_device(var, config) | ||||||
|   | |||||||
| @@ -29,22 +29,6 @@ namespace am2315c { | |||||||
|  |  | ||||||
| static const char *const TAG = "am2315c"; | static const char *const TAG = "am2315c"; | ||||||
|  |  | ||||||
| uint8_t AM2315C::crc8_(uint8_t *data, uint8_t len) { |  | ||||||
|   uint8_t crc = 0xFF; |  | ||||||
|   while (len--) { |  | ||||||
|     crc ^= *data++; |  | ||||||
|     for (uint8_t i = 0; i < 8; i++) { |  | ||||||
|       if (crc & 0x80) { |  | ||||||
|         crc <<= 1; |  | ||||||
|         crc ^= 0x31; |  | ||||||
|       } else { |  | ||||||
|         crc <<= 1; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return crc; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool AM2315C::reset_register_(uint8_t reg) { | bool AM2315C::reset_register_(uint8_t reg) { | ||||||
|   //  code based on demo code sent by www.aosong.com |   //  code based on demo code sent by www.aosong.com | ||||||
|   //  no further documentation. |   //  no further documentation. | ||||||
| @@ -86,7 +70,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) { | |||||||
|   humidity = raw * 9.5367431640625e-5; |   humidity = raw * 9.5367431640625e-5; | ||||||
|   raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; |   raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; | ||||||
|   temperature = raw * 1.9073486328125e-4 - 50; |   temperature = raw * 1.9073486328125e-4 - 50; | ||||||
|   return this->crc8_(data, 6) == data[6]; |   return crc8(data, 6, 0xFF, 0x31, true) == data[6]; | ||||||
| } | } | ||||||
|  |  | ||||||
| void AM2315C::setup() { | void AM2315C::setup() { | ||||||
|   | |||||||
| @@ -21,9 +21,9 @@ | |||||||
| // SOFTWARE. | // SOFTWARE. | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" |  | ||||||
| #include "esphome/components/sensor/sensor.h" |  | ||||||
| #include "esphome/components/i2c/i2c.h" | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace am2315c { | namespace am2315c { | ||||||
| @@ -39,7 +39,6 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice { | |||||||
|   void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; } |   void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   uint8_t crc8_(uint8_t *data, uint8_t len); |  | ||||||
|   bool convert_(uint8_t *data, float &humidity, float &temperature); |   bool convert_(uint8_t *data, float &humidity, float &temperature); | ||||||
|   bool reset_register_(uint8_t reg); |   bool reset_register_(uint8_t reg); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,11 +10,9 @@ namespace esphome { | |||||||
|  |  | ||||||
| namespace binary_sensor { | namespace binary_sensor { | ||||||
|  |  | ||||||
| // Forward declaration |  | ||||||
| class BinarySensor; | class BinarySensor; | ||||||
| void log_binary_sensor(const char *tag, const char *prefix, const char *type, BinarySensor *obj); | void log_binary_sensor(const char *tag, const char *prefix, const char *type, BinarySensor *obj); | ||||||
|  |  | ||||||
| // Macro that calls the function - kept for backward compatibility |  | ||||||
| #define LOG_BINARY_SENSOR(prefix, type, obj) log_binary_sensor(TAG, prefix, LOG_STR_LITERAL(type), obj) | #define LOG_BINARY_SENSOR(prefix, type, obj) log_binary_sensor(TAG, prefix, LOG_STR_LITERAL(type), obj) | ||||||
|  |  | ||||||
| #define SUB_BINARY_SENSOR(name) \ | #define SUB_BINARY_SENSOR(name) \ | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): |     if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): | ||||||
|         cg.add( |         cg.add( | ||||||
| @@ -63,6 +63,6 @@ def to_code(config): | |||||||
|         ) |         ) | ||||||
|         cg.add(var.set_char_uuid128(uuid128)) |         cg.add(var.set_char_uuid128(uuid128)) | ||||||
|     cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE])) |     cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE])) | ||||||
|     yield output.register_output(var, config) |     await output.register_output(var, config) | ||||||
|     yield ble_client.register_ble_node(var, config) |     await ble_client.register_ble_node(var, config) | ||||||
|     yield cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|   | |||||||
| @@ -7,11 +7,9 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace button { | namespace button { | ||||||
|  |  | ||||||
| // Forward declaration |  | ||||||
| class Button; | class Button; | ||||||
| void log_button(const char *tag, const char *prefix, const char *type, Button *obj); | void log_button(const char *tag, const char *prefix, const char *type, Button *obj); | ||||||
|  |  | ||||||
| // Macro that calls the function - kept for backward compatibility |  | ||||||
| #define LOG_BUTTON(prefix, type, obj) log_button(TAG, prefix, LOG_STR_LITERAL(type), obj) | #define LOG_BUTTON(prefix, type, obj) log_button(TAG, prefix, LOG_STR_LITERAL(type), obj) | ||||||
|  |  | ||||||
| #define SUB_BUTTON(name) \ | #define SUB_BUTTON(name) \ | ||||||
|   | |||||||
| @@ -228,9 +228,9 @@ async def cover_stop_to_code(config, action_id, template_arg, args): | |||||||
|  |  | ||||||
|  |  | ||||||
| @automation.register_action("cover.toggle", ToggleAction, COVER_ACTION_SCHEMA) | @automation.register_action("cover.toggle", ToggleAction, COVER_ACTION_SCHEMA) | ||||||
| def cover_toggle_to_code(config, action_id, template_arg, args): | async def cover_toggle_to_code(config, action_id, template_arg, args): | ||||||
|     paren = yield cg.get_variable(config[CONF_ID]) |     paren = await cg.get_variable(config[CONF_ID]) | ||||||
|     yield cg.new_Pvariable(action_id, template_arg, paren) |     return cg.new_Pvariable(action_id, template_arg, paren) | ||||||
|  |  | ||||||
|  |  | ||||||
| COVER_CONTROL_ACTION_SCHEMA = cv.Schema( | COVER_CONTROL_ACTION_SCHEMA = cv.Schema( | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ void HTE501Component::setup() { | |||||||
|   this->write(address, 2, false); |   this->write(address, 2, false); | ||||||
|   uint8_t identification[9]; |   uint8_t identification[9]; | ||||||
|   this->read(identification, 9); |   this->read(identification, 9); | ||||||
|   if (identification[8] != calc_crc8_(identification, 0, 7)) { |   if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) { | ||||||
|     this->error_code_ = CRC_CHECK_FAILED; |     this->error_code_ = CRC_CHECK_FAILED; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     return; | ||||||
| @@ -46,7 +46,8 @@ void HTE501Component::update() { | |||||||
|   this->set_timeout(50, [this]() { |   this->set_timeout(50, [this]() { | ||||||
|     uint8_t i2c_response[6]; |     uint8_t i2c_response[6]; | ||||||
|     this->read(i2c_response, 6); |     this->read(i2c_response, 6); | ||||||
|     if (i2c_response[2] != calc_crc8_(i2c_response, 0, 1) && i2c_response[5] != calc_crc8_(i2c_response, 3, 4)) { |     if (i2c_response[2] != crc8(i2c_response, 2, 0xFF, 0x31, true) && | ||||||
|  |         i2c_response[5] != crc8(i2c_response + 3, 2, 0xFF, 0x31, true)) { | ||||||
|       this->error_code_ = CRC_CHECK_FAILED; |       this->error_code_ = CRC_CHECK_FAILED; | ||||||
|       this->status_set_warning(); |       this->status_set_warning(); | ||||||
|       return; |       return; | ||||||
| @@ -67,24 +68,5 @@ void HTE501Component::update() { | |||||||
|     this->status_clear_warning(); |     this->status_clear_warning(); | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
| unsigned char HTE501Component::calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to) { |  | ||||||
|   unsigned char crc_val = 0xFF; |  | ||||||
|   unsigned char i = 0; |  | ||||||
|   unsigned char j = 0; |  | ||||||
|   for (i = from; i <= to; i++) { |  | ||||||
|     int cur_val = buf[i]; |  | ||||||
|     for (j = 0; j < 8; j++) { |  | ||||||
|       if (((crc_val ^ cur_val) & 0x80) != 0)  // If MSBs are not equal |  | ||||||
|       { |  | ||||||
|         crc_val = ((crc_val << 1) ^ 0x31); |  | ||||||
|       } else { |  | ||||||
|         crc_val = (crc_val << 1); |  | ||||||
|       } |  | ||||||
|       cur_val = cur_val << 1; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return crc_val; |  | ||||||
| } |  | ||||||
| }  // namespace hte501 | }  // namespace hte501 | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" |  | ||||||
| #include "esphome/components/sensor/sensor.h" |  | ||||||
| #include "esphome/components/i2c/i2c.h" | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace hte501 { | namespace hte501 { | ||||||
| @@ -19,7 +19,6 @@ class HTE501Component : public PollingComponent, public i2c::I2CDevice { | |||||||
|   void update() override; |   void update() override; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   unsigned char calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to); |  | ||||||
|   sensor::Sensor *temperature_sensor_; |   sensor::Sensor *temperature_sensor_; | ||||||
|   sensor::Sensor *humidity_sensor_; |   sensor::Sensor *humidity_sensor_; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "lc709203f.h" | #include "lc709203f.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace lc709203f { | namespace lc709203f { | ||||||
| @@ -189,7 +190,7 @@ uint8_t Lc709203f::get_register_(uint8_t register_to_read, uint16_t *register_va | |||||||
|       // Error on the i2c bus |       // Error on the i2c bus | ||||||
|       this->status_set_warning( |       this->status_set_warning( | ||||||
|           str_sprintf("Error code %d when reading from register 0x%02X", return_code, register_to_read).c_str()); |           str_sprintf("Error code %d when reading from register 0x%02X", return_code, register_to_read).c_str()); | ||||||
|     } else if (this->crc8_(read_buffer, 5) != read_buffer[5]) { |     } else if (crc8(read_buffer, 5, 0x00, 0x07, true) != read_buffer[5]) { | ||||||
|       // I2C indicated OK, but the CRC of the data does not matcth. |       // I2C indicated OK, but the CRC of the data does not matcth. | ||||||
|       this->status_set_warning(str_sprintf("CRC error reading from register 0x%02X", register_to_read).c_str()); |       this->status_set_warning(str_sprintf("CRC error reading from register 0x%02X", register_to_read).c_str()); | ||||||
|     } else { |     } else { | ||||||
| @@ -220,7 +221,7 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set) | |||||||
|   write_buffer[1] = register_to_set; |   write_buffer[1] = register_to_set; | ||||||
|   write_buffer[2] = value_to_set & 0xFF;         // Low byte |   write_buffer[2] = value_to_set & 0xFF;         // Low byte | ||||||
|   write_buffer[3] = (value_to_set >> 8) & 0xFF;  // High byte |   write_buffer[3] = (value_to_set >> 8) & 0xFF;  // High byte | ||||||
|   write_buffer[4] = this->crc8_(write_buffer, 4); |   write_buffer[4] = crc8(write_buffer, 4, 0x00, 0x07, true); | ||||||
|  |  | ||||||
|   for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) { |   for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) { | ||||||
|     // Note: we don't write the first byte of the write buffer to the device. |     // Note: we don't write the first byte of the write buffer to the device. | ||||||
| @@ -239,20 +240,6 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set) | |||||||
|   return return_code; |   return return_code; | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t Lc709203f::crc8_(uint8_t *byte_buffer, uint8_t length_of_crc) { |  | ||||||
|   uint8_t crc = 0x00; |  | ||||||
|   const uint8_t polynomial(0x07); |  | ||||||
|  |  | ||||||
|   for (uint8_t j = length_of_crc; j; --j) { |  | ||||||
|     crc ^= *byte_buffer++; |  | ||||||
|  |  | ||||||
|     for (uint8_t i = 8; i; --i) { |  | ||||||
|       crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return crc; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void Lc709203f::set_pack_size(uint16_t pack_size) { | void Lc709203f::set_pack_size(uint16_t pack_size) { | ||||||
|   static const uint16_t PACK_SIZE_ARRAY[6] = {100, 200, 500, 1000, 2000, 3000}; |   static const uint16_t PACK_SIZE_ARRAY[6] = {100, 200, 500, 1000, 2000, 3000}; | ||||||
|   static const uint16_t APA_ARRAY[6] = {0x08, 0x0B, 0x10, 0x19, 0x2D, 0x36}; |   static const uint16_t APA_ARRAY[6] = {0x08, 0x0B, 0x10, 0x19, 0x2D, 0x36}; | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" |  | ||||||
| #include "esphome/components/sensor/sensor.h" |  | ||||||
| #include "esphome/components/i2c/i2c.h" | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace lc709203f { | namespace lc709203f { | ||||||
| @@ -38,7 +38,6 @@ class Lc709203f : public sensor::Sensor, public PollingComponent, public i2c::I2 | |||||||
|  private: |  private: | ||||||
|   uint8_t get_register_(uint8_t register_to_read, uint16_t *register_value); |   uint8_t get_register_(uint8_t register_to_read, uint16_t *register_value); | ||||||
|   uint8_t set_register_(uint8_t register_to_set, uint16_t value_to_set); |   uint8_t set_register_(uint8_t register_to_set, uint16_t value_to_set); | ||||||
|   uint8_t crc8_(uint8_t *byte_buffer, uint8_t length_of_crc); |  | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   sensor::Sensor *voltage_sensor_{nullptr}; |   sensor::Sensor *voltage_sensor_{nullptr}; | ||||||
|   | |||||||
| @@ -44,6 +44,13 @@ class AddressableLightEffect : public LightEffect { | |||||||
|     this->apply(*this->get_addressable_(), current_color); |     this->apply(*this->get_addressable_(), current_color); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /// Get effect index specifically for addressable effects. | ||||||
|  |   /// Can be used by effects to modify behavior based on their position in the list. | ||||||
|  |   uint32_t get_effect_index() const { return this->get_index(); } | ||||||
|  |  | ||||||
|  |   /// Check if this is the currently running addressable effect. | ||||||
|  |   bool is_current_effect() const { return this->is_active() && this->get_addressable_()->is_effect_active(); } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   AddressableLight *get_addressable_() const { return (AddressableLight *) this->state_->get_output(); } |   AddressableLight *get_addressable_() const { return (AddressableLight *) this->state_->get_output(); } | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -125,6 +125,10 @@ class LambdaLightEffect : public LightEffect { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /// Get the current effect index for use in lambda functions. | ||||||
|  |   /// This can be useful for lambda effects that need to know their own index. | ||||||
|  |   uint32_t get_current_index() const { return this->get_index(); } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   std::function<void(bool initial_run)> f_; |   std::function<void(bool initial_run)> f_; | ||||||
|   uint32_t update_interval_; |   uint32_t update_interval_; | ||||||
| @@ -143,6 +147,10 @@ class AutomationLightEffect : public LightEffect { | |||||||
|   } |   } | ||||||
|   Trigger<> *get_trig() const { return trig_; } |   Trigger<> *get_trig() const { return trig_; } | ||||||
|  |  | ||||||
|  |   /// Get the current effect index for use in automations. | ||||||
|  |   /// Useful for automations that need to know which effect is running. | ||||||
|  |   uint32_t get_current_index() const { return this->get_index(); } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   Trigger<> *trig_{new Trigger<>}; |   Trigger<> *trig_{new Trigger<>}; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								esphome/components/light/light_effect.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								esphome/components/light/light_effect.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | #include "light_effect.h" | ||||||
|  | #include "light_state.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace light { | ||||||
|  |  | ||||||
|  | uint32_t LightEffect::get_index() const { | ||||||
|  |   if (this->state_ == nullptr) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |   return this->get_index_in_parent_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool LightEffect::is_active() const { | ||||||
|  |   if (this->state_ == nullptr) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   return this->get_index() != 0 && this->state_->get_current_effect_index() == this->get_index(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t LightEffect::get_index_in_parent_() const { | ||||||
|  |   if (this->state_ == nullptr) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const auto &effects = this->state_->get_effects(); | ||||||
|  |   for (size_t i = 0; i < effects.size(); i++) { | ||||||
|  |     if (effects[i] == this) { | ||||||
|  |       return i + 1;  // Effects are 1-indexed in the API | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0;  // Not found | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace light | ||||||
|  | }  // namespace esphome | ||||||
| @@ -34,9 +34,23 @@ class LightEffect { | |||||||
|     this->init(); |     this->init(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /// Get the index of this effect in the parent light's effect list. | ||||||
|  |   /// Returns 0 if not found or not initialized. | ||||||
|  |   uint32_t get_index() const; | ||||||
|  |  | ||||||
|  |   /// Check if this effect is currently active. | ||||||
|  |   bool is_active() const; | ||||||
|  |  | ||||||
|  |   /// Get a reference to the parent light state. | ||||||
|  |   /// Returns nullptr if not initialized. | ||||||
|  |   LightState *get_light_state() const { return this->state_; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   LightState *state_{nullptr}; |   LightState *state_{nullptr}; | ||||||
|   std::string name_; |   std::string name_; | ||||||
|  |  | ||||||
|  |   /// Internal method to find this effect's index in the parent light's effect list. | ||||||
|  |   uint32_t get_index_in_parent_() const; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace light | }  // namespace light | ||||||
|   | |||||||
| @@ -36,8 +36,11 @@ static constexpr const char *get_color_mode_json_str(ColorMode mode) { | |||||||
|  |  | ||||||
| void LightJSONSchema::dump_json(LightState &state, JsonObject root) { | void LightJSONSchema::dump_json(LightState &state, JsonObject root) { | ||||||
|   // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson |   // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson | ||||||
|   if (state.supports_effects()) |   if (state.supports_effects()) { | ||||||
|     root["effect"] = state.get_effect_name(); |     root["effect"] = state.get_effect_name(); | ||||||
|  |     root["effect_index"] = state.get_current_effect_index(); | ||||||
|  |     root["effect_count"] = state.get_effect_count(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   auto values = state.remote_values; |   auto values = state.remote_values; | ||||||
|   auto traits = state.get_output()->get_traits(); |   auto traits = state.get_output()->get_traits(); | ||||||
| @@ -160,6 +163,11 @@ void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject | |||||||
|     const char *effect = root["effect"]; |     const char *effect = root["effect"]; | ||||||
|     call.set_effect(effect); |     call.set_effect(effect); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (root["effect_index"].is<uint32_t>()) { | ||||||
|  |     uint32_t effect_index = root["effect_index"]; | ||||||
|  |     call.set_effect(effect_index); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace light | }  // namespace light | ||||||
|   | |||||||
| @@ -163,6 +163,44 @@ class LightState : public EntityBase, public Component { | |||||||
|   /// Add effects for this light state. |   /// Add effects for this light state. | ||||||
|   void add_effects(const std::vector<LightEffect *> &effects); |   void add_effects(const std::vector<LightEffect *> &effects); | ||||||
|  |  | ||||||
|  |   /// Get the total number of effects available for this light. | ||||||
|  |   size_t get_effect_count() const { return this->effects_.size(); } | ||||||
|  |  | ||||||
|  |   /// Get the currently active effect index (0 = no effect, 1+ = effect index). | ||||||
|  |   uint32_t get_current_effect_index() const { return this->active_effect_index_; } | ||||||
|  |  | ||||||
|  |   /// Get effect index by name. Returns 0 if effect not found. | ||||||
|  |   uint32_t get_effect_index(const std::string &effect_name) const { | ||||||
|  |     if (strcasecmp(effect_name.c_str(), "none") == 0) { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |     for (size_t i = 0; i < this->effects_.size(); i++) { | ||||||
|  |       if (strcasecmp(effect_name.c_str(), this->effects_[i]->get_name().c_str()) == 0) { | ||||||
|  |         return i + 1;  // Effects are 1-indexed in active_effect_index_ | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return 0;  // Effect not found | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Get effect by index. Returns nullptr if index is invalid. | ||||||
|  |   LightEffect *get_effect_by_index(uint32_t index) const { | ||||||
|  |     if (index == 0 || index > this->effects_.size()) { | ||||||
|  |       return nullptr; | ||||||
|  |     } | ||||||
|  |     return this->effects_[index - 1];  // Effects are 1-indexed in active_effect_index_ | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Get effect name by index. Returns "None" for index 0, empty string for invalid index. | ||||||
|  |   std::string get_effect_name_by_index(uint32_t index) const { | ||||||
|  |     if (index == 0) { | ||||||
|  |       return "None"; | ||||||
|  |     } | ||||||
|  |     if (index > this->effects_.size()) { | ||||||
|  |       return "";  // Invalid index | ||||||
|  |     } | ||||||
|  |     return this->effects_[index - 1]->get_name(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /// The result of all the current_values_as_* methods have gamma correction applied. |   /// The result of all the current_values_as_* methods have gamma correction applied. | ||||||
|   void current_values_as_binary(bool *binary); |   void current_values_as_binary(bool *binary); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -50,28 +50,13 @@ bool MLX90614Component::write_emissivity_() { | |||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t MLX90614Component::crc8_pec_(const uint8_t *data, uint8_t len) { |  | ||||||
|   uint8_t crc = 0; |  | ||||||
|   for (uint8_t i = 0; i < len; i++) { |  | ||||||
|     uint8_t in = data[i]; |  | ||||||
|     for (uint8_t j = 0; j < 8; j++) { |  | ||||||
|       bool carry = (crc ^ in) & 0x80; |  | ||||||
|       crc <<= 1; |  | ||||||
|       if (carry) |  | ||||||
|         crc ^= 0x07; |  | ||||||
|       in <<= 1; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return crc; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool MLX90614Component::write_bytes_(uint8_t reg, uint16_t data) { | bool MLX90614Component::write_bytes_(uint8_t reg, uint16_t data) { | ||||||
|   uint8_t buf[5]; |   uint8_t buf[5]; | ||||||
|   buf[0] = this->address_ << 1; |   buf[0] = this->address_ << 1; | ||||||
|   buf[1] = reg; |   buf[1] = reg; | ||||||
|   buf[2] = data & 0xFF; |   buf[2] = data & 0xFF; | ||||||
|   buf[3] = data >> 8; |   buf[3] = data >> 8; | ||||||
|   buf[4] = this->crc8_pec_(buf, 4); |   buf[4] = crc8(buf, 4, 0x00, 0x07, true); | ||||||
|   return this->write_bytes(reg, buf + 2, 3); |   return this->write_bytes(reg, buf + 2, 3); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ class MLX90614Component : public PollingComponent, public i2c::I2CDevice { | |||||||
|  protected: |  protected: | ||||||
|   bool write_emissivity_(); |   bool write_emissivity_(); | ||||||
|  |  | ||||||
|   uint8_t crc8_pec_(const uint8_t *data, uint8_t len); |  | ||||||
|   bool write_bytes_(uint8_t reg, uint16_t data); |   bool write_bytes_(uint8_t reg, uint16_t data); | ||||||
|  |  | ||||||
|   sensor::Sensor *ambient_sensor_{nullptr}; |   sensor::Sensor *ambient_sensor_{nullptr}; | ||||||
|   | |||||||
| @@ -9,11 +9,9 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace number { | namespace number { | ||||||
|  |  | ||||||
| // Forward declaration |  | ||||||
| class Number; | class Number; | ||||||
| void log_number(const char *tag, const char *prefix, const char *type, Number *obj); | void log_number(const char *tag, const char *prefix, const char *type, Number *obj); | ||||||
|  |  | ||||||
| // Macro that calls the function - kept for backward compatibility |  | ||||||
| #define LOG_NUMBER(prefix, type, obj) log_number(TAG, prefix, LOG_STR_LITERAL(type), obj) | #define LOG_NUMBER(prefix, type, obj) log_number(TAG, prefix, LOG_STR_LITERAL(type), obj) | ||||||
|  |  | ||||||
| #define SUB_NUMBER(name) \ | #define SUB_NUMBER(name) \ | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     yield cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|     yield uart.register_uart_device(var, config) |     await uart.register_uart_device(var, config) | ||||||
|   | |||||||
| @@ -99,9 +99,9 @@ async def to_code(config): | |||||||
|         } |         } | ||||||
|     ), |     ), | ||||||
| ) | ) | ||||||
| def output_pipsolar_set_level_to_code(config, action_id, template_arg, args): | async def output_pipsolar_set_level_to_code(config, action_id, template_arg, args): | ||||||
|     paren = yield cg.get_variable(config[CONF_ID]) |     paren = await cg.get_variable(config[CONF_ID]) | ||||||
|     var = cg.new_Pvariable(action_id, template_arg, paren) |     var = cg.new_Pvariable(action_id, template_arg, paren) | ||||||
|     template_ = yield cg.templatable(config[CONF_VALUE], args, float) |     template_ = await cg.templatable(config[CONF_VALUE], args, float) | ||||||
|     cg.add(var.set_level(template_)) |     cg.add(var.set_level(template_)) | ||||||
|     yield var |     return var | ||||||
|   | |||||||
| @@ -18,6 +18,6 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
| ).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) | ).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     yield esp32_ble_tracker.register_ble_device(var, config) |     await esp32_ble_tracker.register_ble_device(var, config) | ||||||
|   | |||||||
| @@ -1782,14 +1782,12 @@ def nexa_dumper(var, config): | |||||||
|  |  | ||||||
|  |  | ||||||
| @register_action("nexa", NexaAction, NEXA_SCHEMA) | @register_action("nexa", NexaAction, NEXA_SCHEMA) | ||||||
| def nexa_action(var, config, args): | async def nexa_action(var, config, args): | ||||||
|     cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.uint32)))) |     cg.add(var.set_device(await cg.templatable(config[CONF_DEVICE], args, cg.uint32))) | ||||||
|     cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.uint8)))) |     cg.add(var.set_group(await cg.templatable(config[CONF_GROUP], args, cg.uint8))) | ||||||
|     cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, cg.uint8)))) |     cg.add(var.set_state(await cg.templatable(config[CONF_STATE], args, cg.uint8))) | ||||||
|     cg.add( |     cg.add(var.set_channel(await cg.templatable(config[CONF_CHANNEL], args, cg.uint8))) | ||||||
|         var.set_channel((yield cg.templatable(config[CONF_CHANNEL], args, cg.uint8))) |     cg.add(var.set_level(await cg.templatable(config[CONF_LEVEL], args, cg.uint8))) | ||||||
|     ) |  | ||||||
|     cg.add(var.set_level((yield cg.templatable(config[CONF_LEVEL], args, cg.uint8)))) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # Midea | # Midea | ||||||
|   | |||||||
| @@ -183,7 +183,7 @@ CONFIG_SCHEMA = ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_code(config): | async def to_code(config): | ||||||
|     fw_hex = get_firmware(config[CONF_FIRMWARE]) |     fw_hex = get_firmware(config[CONF_FIRMWARE]) | ||||||
|     fw_major, fw_minor = parse_firmware_version(config[CONF_FIRMWARE][CONF_VERSION]) |     fw_major, fw_minor = parse_firmware_version(config[CONF_FIRMWARE][CONF_VERSION]) | ||||||
|  |  | ||||||
| @@ -193,17 +193,17 @@ def to_code(config): | |||||||
|     cg.add_define("USE_SHD_FIRMWARE_MINOR_VERSION", fw_minor) |     cg.add_define("USE_SHD_FIRMWARE_MINOR_VERSION", fw_minor) | ||||||
|  |  | ||||||
|     var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) |     var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) | ||||||
|     yield cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|     config.pop( |     config.pop( | ||||||
|         CONF_UPDATE_INTERVAL |         CONF_UPDATE_INTERVAL | ||||||
|     )  # drop UPDATE_INTERVAL as it does not apply to the light component |     )  # drop UPDATE_INTERVAL as it does not apply to the light component | ||||||
|  |  | ||||||
|     yield light.register_light(var, config) |     await light.register_light(var, config) | ||||||
|     yield uart.register_uart_device(var, config) |     await uart.register_uart_device(var, config) | ||||||
|  |  | ||||||
|     nrst_pin = yield cg.gpio_pin_expression(config[CONF_NRST_PIN]) |     nrst_pin = await cg.gpio_pin_expression(config[CONF_NRST_PIN]) | ||||||
|     cg.add(var.set_nrst_pin(nrst_pin)) |     cg.add(var.set_nrst_pin(nrst_pin)) | ||||||
|     boot0_pin = yield cg.gpio_pin_expression(config[CONF_BOOT0_PIN]) |     boot0_pin = await cg.gpio_pin_expression(config[CONF_BOOT0_PIN]) | ||||||
|     cg.add(var.set_boot0_pin(boot0_pin)) |     cg.add(var.set_boot0_pin(boot0_pin)) | ||||||
|  |  | ||||||
|     cg.add(var.set_leading_edge(config[CONF_LEADING_EDGE])) |     cg.add(var.set_leading_edge(config[CONF_LEADING_EDGE])) | ||||||
| @@ -217,5 +217,5 @@ def to_code(config): | |||||||
|             continue |             continue | ||||||
|  |  | ||||||
|         conf = config[key] |         conf = config[key] | ||||||
|         sens = yield sensor.new_sensor(conf) |         sens = await sensor.new_sensor(conf) | ||||||
|         cg.add(getattr(var, f"set_{key}_sensor")(sens)) |         cg.add(getattr(var, f"set_{key}_sensor")(sens)) | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ void TEE501Component::setup() { | |||||||
|   this->write(address, 2, false); |   this->write(address, 2, false); | ||||||
|   uint8_t identification[9]; |   uint8_t identification[9]; | ||||||
|   this->read(identification, 9); |   this->read(identification, 9); | ||||||
|   if (identification[8] != calc_crc8_(identification, 0, 7)) { |   if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) { | ||||||
|     this->error_code_ = CRC_CHECK_FAILED; |     this->error_code_ = CRC_CHECK_FAILED; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     return; | ||||||
| @@ -45,7 +45,7 @@ void TEE501Component::update() { | |||||||
|   this->set_timeout(50, [this]() { |   this->set_timeout(50, [this]() { | ||||||
|     uint8_t i2c_response[3]; |     uint8_t i2c_response[3]; | ||||||
|     this->read(i2c_response, 3); |     this->read(i2c_response, 3); | ||||||
|     if (i2c_response[2] != calc_crc8_(i2c_response, 0, 1)) { |     if (i2c_response[2] != crc8(i2c_response, 2, 0xFF, 0x31, true)) { | ||||||
|       this->error_code_ = CRC_CHECK_FAILED; |       this->error_code_ = CRC_CHECK_FAILED; | ||||||
|       this->status_set_warning(); |       this->status_set_warning(); | ||||||
|       return; |       return; | ||||||
| @@ -62,24 +62,5 @@ void TEE501Component::update() { | |||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
| unsigned char TEE501Component::calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to) { |  | ||||||
|   unsigned char crc_val = 0xFF; |  | ||||||
|   unsigned char i = 0; |  | ||||||
|   unsigned char j = 0; |  | ||||||
|   for (i = from; i <= to; i++) { |  | ||||||
|     int cur_val = buf[i]; |  | ||||||
|     for (j = 0; j < 8; j++) { |  | ||||||
|       if (((crc_val ^ cur_val) & 0x80) != 0)  // If MSBs are not equal |  | ||||||
|       { |  | ||||||
|         crc_val = ((crc_val << 1) ^ 0x31); |  | ||||||
|       } else { |  | ||||||
|         crc_val = (crc_val << 1); |  | ||||||
|       } |  | ||||||
|       cur_val = cur_val << 1; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return crc_val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| }  // namespace tee501 | }  // namespace tee501 | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" |  | ||||||
| #include "esphome/components/sensor/sensor.h" |  | ||||||
| #include "esphome/components/i2c/i2c.h" | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace tee501 { | namespace tee501 { | ||||||
| @@ -16,8 +16,6 @@ class TEE501Component : public sensor::Sensor, public PollingComponent, public i | |||||||
|   void update() override; |   void update() override; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   unsigned char calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to); |  | ||||||
|  |  | ||||||
|   enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE}; |   enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -202,9 +202,9 @@ async def valve_stop_to_code(config, action_id, template_arg, args): | |||||||
|  |  | ||||||
|  |  | ||||||
| @automation.register_action("valve.toggle", ToggleAction, VALVE_ACTION_SCHEMA) | @automation.register_action("valve.toggle", ToggleAction, VALVE_ACTION_SCHEMA) | ||||||
| def valve_toggle_to_code(config, action_id, template_arg, args): | async def valve_toggle_to_code(config, action_id, template_arg, args): | ||||||
|     paren = yield cg.get_variable(config[CONF_ID]) |     paren = await cg.get_variable(config[CONF_ID]) | ||||||
|     yield cg.new_Pvariable(action_id, template_arg, paren) |     return cg.new_Pvariable(action_id, template_arg, paren) | ||||||
|  |  | ||||||
|  |  | ||||||
| VALVE_CONTROL_ACTION_SCHEMA = cv.Schema( | VALVE_CONTROL_ACTION_SCHEMA = cv.Schema( | ||||||
|   | |||||||
| @@ -41,17 +41,28 @@ static const uint16_t CRC16_1021_BE_LUT_H[] = {0x0000, 0x1231, 0x2462, 0x3653, 0 | |||||||
|  |  | ||||||
| // Mathematics | // Mathematics | ||||||
|  |  | ||||||
| uint8_t crc8(const uint8_t *data, uint8_t len) { | uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc, uint8_t poly, bool msb_first) { | ||||||
|   uint8_t crc = 0; |  | ||||||
|  |  | ||||||
|   while ((len--) != 0u) { |   while ((len--) != 0u) { | ||||||
|     uint8_t inbyte = *data++; |     uint8_t inbyte = *data++; | ||||||
|     for (uint8_t i = 8; i != 0u; i--) { |     if (msb_first) { | ||||||
|       bool mix = (crc ^ inbyte) & 0x01; |       // MSB first processing (for polynomials like 0x31, 0x07) | ||||||
|       crc >>= 1; |       crc ^= inbyte; | ||||||
|       if (mix) |       for (uint8_t i = 8; i != 0u; i--) { | ||||||
|         crc ^= 0x8C; |         if (crc & 0x80) { | ||||||
|       inbyte >>= 1; |           crc = (crc << 1) ^ poly; | ||||||
|  |         } else { | ||||||
|  |           crc <<= 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       // LSB first processing (default for Dallas/Maxim 0x8C) | ||||||
|  |       for (uint8_t i = 8; i != 0u; i--) { | ||||||
|  |         bool mix = (crc ^ inbyte) & 0x01; | ||||||
|  |         crc >>= 1; | ||||||
|  |         if (mix) | ||||||
|  |           crc ^= poly; | ||||||
|  |         inbyte >>= 1; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   return crc; |   return crc; | ||||||
|   | |||||||
| @@ -145,8 +145,8 @@ template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max | |||||||
|   return (value - min) * (max_out - min_out) / (max - min) + min_out; |   return (value - min) * (max_out - min_out) / (max - min) + min_out; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Calculate a CRC-8 checksum of \p data with size \p len using the CRC-8-Dallas/Maxim polynomial. | /// Calculate a CRC-8 checksum of \p data with size \p len. | ||||||
| uint8_t crc8(const uint8_t *data, uint8_t len); | uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc = 0x00, uint8_t poly = 0x8C, bool msb_first = false); | ||||||
|  |  | ||||||
| /// Calculate a CRC-16 checksum of \p data with size \p len. | /// Calculate a CRC-16 checksum of \p data with size \p len. | ||||||
| uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001, | uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001, | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								tests/integration/fixtures/crc8_helper.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/integration/fixtures/crc8_helper.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | esphome: | ||||||
|  |   name: crc8-helper-test | ||||||
|  |  | ||||||
|  | host: | ||||||
|  |  | ||||||
|  | api: | ||||||
|  |  | ||||||
|  | logger: | ||||||
|  |   level: INFO | ||||||
|  |  | ||||||
|  | external_components: | ||||||
|  |   - source: | ||||||
|  |       type: local | ||||||
|  |       path: EXTERNAL_COMPONENT_PATH | ||||||
|  |     components: [crc8_test_component] | ||||||
|  |  | ||||||
|  | crc8_test_component: | ||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  | crc8_test_component_ns = cg.esphome_ns.namespace("crc8_test_component") | ||||||
|  | CRC8TestComponent = crc8_test_component_ns.class_("CRC8TestComponent", cg.Component) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(): cv.declare_id(CRC8TestComponent), | ||||||
|  |     } | ||||||
|  | ).extend(cv.COMPONENT_SCHEMA) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
| @@ -0,0 +1,170 @@ | |||||||
|  | #include "crc8_test_component.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace crc8_test_component { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "crc8_test"; | ||||||
|  |  | ||||||
|  | void CRC8TestComponent::setup() { | ||||||
|  |   ESP_LOGI(TAG, "CRC8 Helper Function Integration Test Starting"); | ||||||
|  |  | ||||||
|  |   // Run all test suites | ||||||
|  |   test_crc8_dallas_maxim(); | ||||||
|  |   test_crc8_sensirion_style(); | ||||||
|  |   test_crc8_pec_style(); | ||||||
|  |   test_crc8_parameter_equivalence(); | ||||||
|  |   test_crc8_edge_cases(); | ||||||
|  |   test_component_compatibility(); | ||||||
|  |  | ||||||
|  |   ESP_LOGI(TAG, "CRC8 Integration Test Complete"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CRC8TestComponent::test_crc8_dallas_maxim() { | ||||||
|  |   ESP_LOGI(TAG, "Testing Dallas/Maxim CRC8 (default parameters)"); | ||||||
|  |  | ||||||
|  |   // Test vectors for Dallas/Maxim CRC8 (polynomial 0x8C, LSB-first, init 0x00) | ||||||
|  |   const uint8_t test1[] = {0x01}; | ||||||
|  |   const uint8_t test2[] = {0xFF}; | ||||||
|  |   const uint8_t test3[] = {0x12, 0x34}; | ||||||
|  |   const uint8_t test4[] = {0xAA, 0xBB, 0xCC}; | ||||||
|  |   const uint8_t test5[] = {0x01, 0x02, 0x03, 0x04, 0x05}; | ||||||
|  |  | ||||||
|  |   bool all_passed = true; | ||||||
|  |   all_passed &= verify_crc8("Dallas [0x01]", test1, sizeof(test1), 0x5E); | ||||||
|  |   all_passed &= verify_crc8("Dallas [0xFF]", test2, sizeof(test2), 0x35); | ||||||
|  |   all_passed &= verify_crc8("Dallas [0x12, 0x34]", test3, sizeof(test3), 0xA2); | ||||||
|  |   all_passed &= verify_crc8("Dallas [0xAA, 0xBB, 0xCC]", test4, sizeof(test4), 0xD4); | ||||||
|  |   all_passed &= verify_crc8("Dallas [0x01...0x05]", test5, sizeof(test5), 0x2A); | ||||||
|  |  | ||||||
|  |   log_test_result("Dallas/Maxim CRC8", all_passed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CRC8TestComponent::test_crc8_sensirion_style() { | ||||||
|  |   ESP_LOGI(TAG, "Testing Sensirion CRC8 (0x31 poly, MSB-first, init 0xFF)"); | ||||||
|  |  | ||||||
|  |   const uint8_t test1[] = {0x00}; | ||||||
|  |   const uint8_t test2[] = {0x01}; | ||||||
|  |   const uint8_t test3[] = {0xFF}; | ||||||
|  |   const uint8_t test4[] = {0x12, 0x34}; | ||||||
|  |   const uint8_t test5[] = {0xBE, 0xEF}; | ||||||
|  |  | ||||||
|  |   bool all_passed = true; | ||||||
|  |   all_passed &= verify_crc8("Sensirion [0x00]", test1, sizeof(test1), 0xAC, 0xFF, 0x31, true); | ||||||
|  |   all_passed &= verify_crc8("Sensirion [0x01]", test2, sizeof(test2), 0x9D, 0xFF, 0x31, true); | ||||||
|  |   all_passed &= verify_crc8("Sensirion [0xFF]", test3, sizeof(test3), 0x00, 0xFF, 0x31, true); | ||||||
|  |   all_passed &= verify_crc8("Sensirion [0x12, 0x34]", test4, sizeof(test4), 0x37, 0xFF, 0x31, true); | ||||||
|  |   all_passed &= verify_crc8("Sensirion [0xBE, 0xEF]", test5, sizeof(test5), 0x92, 0xFF, 0x31, true); | ||||||
|  |  | ||||||
|  |   log_test_result("Sensirion CRC8", all_passed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CRC8TestComponent::test_crc8_pec_style() { | ||||||
|  |   ESP_LOGI(TAG, "Testing PEC CRC8 (0x07 poly, MSB-first, init 0x00)"); | ||||||
|  |  | ||||||
|  |   const uint8_t test1[] = {0x00}; | ||||||
|  |   const uint8_t test2[] = {0x01}; | ||||||
|  |   const uint8_t test3[] = {0xFF}; | ||||||
|  |   const uint8_t test4[] = {0x12, 0x34}; | ||||||
|  |   const uint8_t test5[] = {0xAA, 0xBB}; | ||||||
|  |  | ||||||
|  |   bool all_passed = true; | ||||||
|  |   all_passed &= verify_crc8("PEC [0x00]", test1, sizeof(test1), 0x00, 0x00, 0x07, true); | ||||||
|  |   all_passed &= verify_crc8("PEC [0x01]", test2, sizeof(test2), 0x07, 0x00, 0x07, true); | ||||||
|  |   all_passed &= verify_crc8("PEC [0xFF]", test3, sizeof(test3), 0xF3, 0x00, 0x07, true); | ||||||
|  |   all_passed &= verify_crc8("PEC [0x12, 0x34]", test4, sizeof(test4), 0xF1, 0x00, 0x07, true); | ||||||
|  |   all_passed &= verify_crc8("PEC [0xAA, 0xBB]", test5, sizeof(test5), 0xB2, 0x00, 0x07, true); | ||||||
|  |  | ||||||
|  |   log_test_result("PEC CRC8", all_passed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CRC8TestComponent::test_crc8_parameter_equivalence() { | ||||||
|  |   ESP_LOGI(TAG, "Testing parameter equivalence"); | ||||||
|  |  | ||||||
|  |   const uint8_t test_data[] = {0x12, 0x34, 0x56, 0x78}; | ||||||
|  |  | ||||||
|  |   // Test that default parameters work as expected | ||||||
|  |   uint8_t default_result = crc8(test_data, sizeof(test_data)); | ||||||
|  |   uint8_t explicit_result = crc8(test_data, sizeof(test_data), 0x00, 0x8C, false); | ||||||
|  |  | ||||||
|  |   bool passed = (default_result == explicit_result); | ||||||
|  |   if (!passed) { | ||||||
|  |     ESP_LOGE(TAG, "Parameter equivalence FAILED: default=0x%02X, explicit=0x%02X", default_result, explicit_result); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   log_test_result("Parameter equivalence", passed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CRC8TestComponent::test_crc8_edge_cases() { | ||||||
|  |   ESP_LOGI(TAG, "Testing edge cases"); | ||||||
|  |  | ||||||
|  |   bool all_passed = true; | ||||||
|  |  | ||||||
|  |   // Empty array test | ||||||
|  |   const uint8_t empty[] = {}; | ||||||
|  |   uint8_t empty_result = crc8(empty, 0); | ||||||
|  |   bool empty_passed = (empty_result == 0x00);  // Should return init value | ||||||
|  |   if (!empty_passed) { | ||||||
|  |     ESP_LOGE(TAG, "Empty array test FAILED: expected 0x00, got 0x%02X", empty_result); | ||||||
|  |   } | ||||||
|  |   all_passed &= empty_passed; | ||||||
|  |  | ||||||
|  |   // Single byte tests | ||||||
|  |   const uint8_t single_zero[] = {0x00}; | ||||||
|  |   const uint8_t single_ff[] = {0xFF}; | ||||||
|  |   all_passed &= verify_crc8("Single [0x00]", single_zero, 1, 0x00); | ||||||
|  |   all_passed &= verify_crc8("Single [0xFF]", single_ff, 1, 0x35); | ||||||
|  |  | ||||||
|  |   log_test_result("Edge cases", all_passed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CRC8TestComponent::test_component_compatibility() { | ||||||
|  |   ESP_LOGI(TAG, "Testing component compatibility"); | ||||||
|  |  | ||||||
|  |   // Test specific component use cases | ||||||
|  |   bool all_passed = true; | ||||||
|  |  | ||||||
|  |   // AGS10-style data (Sensirion CRC8) | ||||||
|  |   const uint8_t ags10_data[] = {0x12, 0x34, 0x56}; | ||||||
|  |   uint8_t ags10_result = crc8(ags10_data, sizeof(ags10_data), 0xFF, 0x31, true); | ||||||
|  |   ESP_LOGI(TAG, "AGS10-style CRC8: 0x%02X", ags10_result); | ||||||
|  |  | ||||||
|  |   // LC709203F-style data (PEC CRC8) | ||||||
|  |   const uint8_t lc_data[] = {0xAA, 0xBB}; | ||||||
|  |   uint8_t lc_result = crc8(lc_data, sizeof(lc_data), 0x00, 0x07, true); | ||||||
|  |   ESP_LOGI(TAG, "LC709203F-style CRC8: 0x%02X", lc_result); | ||||||
|  |  | ||||||
|  |   // DallasTemperature-style data (Dallas CRC8) | ||||||
|  |   const uint8_t dallas_data[] = {0x28, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}; | ||||||
|  |   uint8_t dallas_result = crc8(dallas_data, sizeof(dallas_data)); | ||||||
|  |   ESP_LOGI(TAG, "Dallas-style CRC8: 0x%02X", dallas_result); | ||||||
|  |  | ||||||
|  |   all_passed = true;  // These are just demonstration tests | ||||||
|  |   log_test_result("Component compatibility", all_passed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool CRC8TestComponent::verify_crc8(const char *test_name, const uint8_t *data, uint8_t len, uint8_t expected, | ||||||
|  |                                     uint8_t crc, uint8_t poly, bool msb_first) { | ||||||
|  |   uint8_t result = esphome::crc8(data, len, crc, poly, msb_first); | ||||||
|  |   bool passed = (result == expected); | ||||||
|  |  | ||||||
|  |   if (passed) { | ||||||
|  |     ESP_LOGI(TAG, "%s: PASS (0x%02X)", test_name, result); | ||||||
|  |   } else { | ||||||
|  |     ESP_LOGE(TAG, "%s: FAIL - expected 0x%02X, got 0x%02X", test_name, expected, result); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return passed; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CRC8TestComponent::log_test_result(const char *test_name, bool passed) { | ||||||
|  |   if (passed) { | ||||||
|  |     ESP_LOGI(TAG, "%s: ALL TESTS PASSED", test_name); | ||||||
|  |   } else { | ||||||
|  |     ESP_LOGE(TAG, "%s: SOME TESTS FAILED", test_name); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace crc8_test_component | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace crc8_test_component { | ||||||
|  |  | ||||||
|  | class CRC8TestComponent : public Component { | ||||||
|  |  public: | ||||||
|  |   void setup() override; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   void test_crc8_dallas_maxim(); | ||||||
|  |   void test_crc8_sensirion_style(); | ||||||
|  |   void test_crc8_pec_style(); | ||||||
|  |   void test_crc8_parameter_equivalence(); | ||||||
|  |   void test_crc8_edge_cases(); | ||||||
|  |   void test_component_compatibility(); | ||||||
|  |   void test_old_vs_new_implementations(); | ||||||
|  |  | ||||||
|  |   void log_test_result(const char *test_name, bool passed); | ||||||
|  |   bool verify_crc8(const char *test_name, const uint8_t *data, uint8_t len, uint8_t expected, uint8_t crc = 0x00, | ||||||
|  |                    uint8_t poly = 0x8C, bool msb_first = false); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace crc8_test_component | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										100
									
								
								tests/integration/test_crc8_helper.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								tests/integration/test_crc8_helper.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | """Integration test for CRC8 helper function.""" | ||||||
|  |  | ||||||
|  | from __future__ import annotations | ||||||
|  |  | ||||||
|  | import asyncio | ||||||
|  | from pathlib import Path | ||||||
|  |  | ||||||
|  | import pytest | ||||||
|  |  | ||||||
|  | from .types import APIClientConnectedFactory, RunCompiledFunction | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pytest.mark.asyncio | ||||||
|  | async def test_crc8_helper( | ||||||
|  |     yaml_config: str, | ||||||
|  |     run_compiled: RunCompiledFunction, | ||||||
|  |     api_client_connected: APIClientConnectedFactory, | ||||||
|  | ) -> None: | ||||||
|  |     """Test the CRC8 helper function through integration testing.""" | ||||||
|  |     # Get the path to the external components directory | ||||||
|  |     external_components_path = str( | ||||||
|  |         Path(__file__).parent / "fixtures" / "external_components" | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     # Replace the placeholder in the YAML config with the actual path | ||||||
|  |     yaml_config = yaml_config.replace( | ||||||
|  |         "EXTERNAL_COMPONENT_PATH", external_components_path | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     # Track test completion with asyncio.Event | ||||||
|  |     test_complete = asyncio.Event() | ||||||
|  |  | ||||||
|  |     # Track test results | ||||||
|  |     test_results = { | ||||||
|  |         "dallas_maxim": False, | ||||||
|  |         "sensirion": False, | ||||||
|  |         "pec": False, | ||||||
|  |         "parameter_equivalence": False, | ||||||
|  |         "edge_cases": False, | ||||||
|  |         "component_compatibility": False, | ||||||
|  |         "setup_started": False, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     def on_log_line(line): | ||||||
|  |         """Process log lines to track test progress and results.""" | ||||||
|  |         # Track test start | ||||||
|  |         if "CRC8 Helper Function Integration Test Starting" in line: | ||||||
|  |             test_results["setup_started"] = True | ||||||
|  |  | ||||||
|  |         # Track test completion | ||||||
|  |         elif "CRC8 Integration Test Complete" in line: | ||||||
|  |             test_complete.set() | ||||||
|  |  | ||||||
|  |         # Track individual test results | ||||||
|  |         elif "ALL TESTS PASSED" in line: | ||||||
|  |             if "Dallas/Maxim CRC8" in line: | ||||||
|  |                 test_results["dallas_maxim"] = True | ||||||
|  |             elif "Sensirion CRC8" in line: | ||||||
|  |                 test_results["sensirion"] = True | ||||||
|  |             elif "PEC CRC8" in line: | ||||||
|  |                 test_results["pec"] = True | ||||||
|  |             elif "Parameter equivalence" in line: | ||||||
|  |                 test_results["parameter_equivalence"] = True | ||||||
|  |             elif "Edge cases" in line: | ||||||
|  |                 test_results["edge_cases"] = True | ||||||
|  |             elif "Component compatibility" in line: | ||||||
|  |                 test_results["component_compatibility"] = True | ||||||
|  |  | ||||||
|  |         # Log failures for debugging | ||||||
|  |         elif "TEST FAILED:" in line or "SUBTEST FAILED:" in line: | ||||||
|  |             print(f"CRC8 Test Failure: {line}") | ||||||
|  |  | ||||||
|  |     # Compile and run the test | ||||||
|  |     async with ( | ||||||
|  |         run_compiled(yaml_config, line_callback=on_log_line), | ||||||
|  |         api_client_connected() as client, | ||||||
|  |     ): | ||||||
|  |         # Verify device info | ||||||
|  |         device_info = await client.device_info() | ||||||
|  |         assert device_info is not None | ||||||
|  |         assert device_info.name == "crc8-helper-test" | ||||||
|  |  | ||||||
|  |         # Wait for tests to complete with timeout | ||||||
|  |         try: | ||||||
|  |             await asyncio.wait_for(test_complete.wait(), timeout=5.0) | ||||||
|  |         except TimeoutError: | ||||||
|  |             pytest.fail("CRC8 integration test timed out after 5 seconds") | ||||||
|  |  | ||||||
|  |         # Verify all tests passed | ||||||
|  |         assert test_results["setup_started"], "CRC8 test setup never started" | ||||||
|  |         assert test_results["dallas_maxim"], "Dallas/Maxim CRC8 test failed" | ||||||
|  |         assert test_results["sensirion"], "Sensirion CRC8 test failed" | ||||||
|  |         assert test_results["pec"], "PEC CRC8 test failed" | ||||||
|  |         assert test_results["parameter_equivalence"], ( | ||||||
|  |             "Parameter equivalence test failed" | ||||||
|  |         ) | ||||||
|  |         assert test_results["edge_cases"], "Edge cases test failed" | ||||||
|  |         assert test_results["component_compatibility"], ( | ||||||
|  |             "Component compatibility test failed" | ||||||
|  |         ) | ||||||
		Reference in New Issue
	
	Block a user