mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/dev' into zwave_proxy
This commit is contained in:
		@@ -11,7 +11,7 @@ ci:
 | 
				
			|||||||
repos:
 | 
					repos:
 | 
				
			||||||
  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
					  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
				
			||||||
    # Ruff version.
 | 
					    # Ruff version.
 | 
				
			||||||
    rev: v0.12.10
 | 
					    rev: v0.12.11
 | 
				
			||||||
    hooks:
 | 
					    hooks:
 | 
				
			||||||
      # Run the linter.
 | 
					      # Run the linter.
 | 
				
			||||||
      - id: ruff
 | 
					      - id: ruff
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,14 +132,17 @@ def choose_upload_log_host(
 | 
				
			|||||||
                ]
 | 
					                ]
 | 
				
			||||||
                resolved.append(choose_prompt(options, purpose=purpose))
 | 
					                resolved.append(choose_prompt(options, purpose=purpose))
 | 
				
			||||||
            elif device == "OTA":
 | 
					            elif device == "OTA":
 | 
				
			||||||
                if (show_ota and "ota" in CORE.config) or (
 | 
					                if CORE.address and (
 | 
				
			||||||
                    show_api and "api" in CORE.config
 | 
					                    (show_ota and "ota" in CORE.config)
 | 
				
			||||||
 | 
					                    or (show_api and "api" in CORE.config)
 | 
				
			||||||
                ):
 | 
					                ):
 | 
				
			||||||
                    resolved.append(CORE.address)
 | 
					                    resolved.append(CORE.address)
 | 
				
			||||||
                elif show_mqtt and has_mqtt_logging():
 | 
					                elif show_mqtt and has_mqtt_logging():
 | 
				
			||||||
                    resolved.append("MQTT")
 | 
					                    resolved.append("MQTT")
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                resolved.append(device)
 | 
					                resolved.append(device)
 | 
				
			||||||
 | 
					        if not resolved:
 | 
				
			||||||
 | 
					            _LOGGER.error("All specified devices: %s could not be resolved.", defaults)
 | 
				
			||||||
        return resolved
 | 
					        return resolved
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # No devices specified, show interactive chooser
 | 
					    # No devices specified, show interactive chooser
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,11 +61,10 @@ void AbsoluteHumidityComponent::loop() {
 | 
				
			|||||||
      ESP_LOGW(TAG, "No valid state from temperature sensor!");
 | 
					      ESP_LOGW(TAG, "No valid state from temperature sensor!");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (no_humidity) {
 | 
					    if (no_humidity) {
 | 
				
			||||||
      ESP_LOGW(TAG, "No valid state from temperature sensor!");
 | 
					      ESP_LOGW(TAG, "No valid state from humidity sensor!");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ESP_LOGW(TAG, "Unable to calculate absolute humidity.");
 | 
					 | 
				
			||||||
    this->publish_state(NAN);
 | 
					    this->publish_state(NAN);
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning("Unable to calculate absolute humidity.");
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -87,9 +86,8 @@ void AbsoluteHumidityComponent::loop() {
 | 
				
			|||||||
      es = es_wobus(temperature_c);
 | 
					      es = es_wobus(temperature_c);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      ESP_LOGE(TAG, "Invalid saturation vapor pressure equation selection!");
 | 
					 | 
				
			||||||
      this->publish_state(NAN);
 | 
					      this->publish_state(NAN);
 | 
				
			||||||
      this->status_set_error();
 | 
					      this->status_set_error("Invalid saturation vapor pressure equation selection!");
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
 | 
					  ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -309,11 +309,13 @@ class APIConnection final : public APIServerConnection {
 | 
				
			|||||||
    msg.key = entity->get_object_id_hash();
 | 
					    msg.key = entity->get_object_id_hash();
 | 
				
			||||||
    // Try to use static reference first to avoid allocation
 | 
					    // Try to use static reference first to avoid allocation
 | 
				
			||||||
    StringRef static_ref = entity->get_object_id_ref_for_api_();
 | 
					    StringRef static_ref = entity->get_object_id_ref_for_api_();
 | 
				
			||||||
 | 
					    // Store dynamic string outside the if-else to maintain lifetime
 | 
				
			||||||
 | 
					    std::string object_id;
 | 
				
			||||||
    if (!static_ref.empty()) {
 | 
					    if (!static_ref.empty()) {
 | 
				
			||||||
      msg.set_object_id(static_ref);
 | 
					      msg.set_object_id(static_ref);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // Dynamic case - need to allocate
 | 
					      // Dynamic case - need to allocate
 | 
				
			||||||
      std::string object_id = entity->get_object_id();
 | 
					      object_id = entity->get_object_id();
 | 
				
			||||||
      msg.set_object_id(StringRef(object_id));
 | 
					      msg.set_object_id(StringRef(object_id));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ void AXS15231Touchscreen::update_touches() {
 | 
				
			|||||||
  i2c::ErrorCode err;
 | 
					  i2c::ErrorCode err;
 | 
				
			||||||
  uint8_t data[8]{};
 | 
					  uint8_t data[8]{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  err = this->write(AXS_READ_TOUCHPAD, sizeof(AXS_READ_TOUCHPAD), false);
 | 
					  err = this->write(AXS_READ_TOUCHPAD, sizeof(AXS_READ_TOUCHPAD));
 | 
				
			||||||
  ERROR_CHECK(err);
 | 
					  ERROR_CHECK(err);
 | 
				
			||||||
  err = this->read(data, sizeof(data));
 | 
					  err = this->read(data, sizeof(data));
 | 
				
			||||||
  ERROR_CHECK(err);
 | 
					  ERROR_CHECK(err);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -183,6 +183,12 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
 | 
				
			|||||||
        this->send_device_connection(msg.address, false);
 | 
					        this->send_device_connection(msg.address, false);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      if (!msg.has_address_type) {
 | 
				
			||||||
 | 
					        ESP_LOGE(TAG, "[%d] [%s] Missing address type in connect request", connection->get_connection_index(),
 | 
				
			||||||
 | 
					                 connection->address_str().c_str());
 | 
				
			||||||
 | 
					        this->send_device_connection(msg.address, false);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      if (connection->state() == espbt::ClientState::CONNECTED ||
 | 
					      if (connection->state() == espbt::ClientState::CONNECTED ||
 | 
				
			||||||
          connection->state() == espbt::ClientState::ESTABLISHED) {
 | 
					          connection->state() == espbt::ClientState::ESTABLISHED) {
 | 
				
			||||||
        this->log_connection_request_ignored_(connection, connection->state());
 | 
					        this->log_connection_request_ignored_(connection, connection->state());
 | 
				
			||||||
@@ -209,13 +215,9 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
 | 
				
			|||||||
        connection->set_connection_type(espbt::ConnectionType::V3_WITHOUT_CACHE);
 | 
					        connection->set_connection_type(espbt::ConnectionType::V3_WITHOUT_CACHE);
 | 
				
			||||||
        this->log_connection_info_(connection, "v3 without cache");
 | 
					        this->log_connection_info_(connection, "v3 without cache");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (msg.has_address_type) {
 | 
					      uint64_to_bd_addr(msg.address, connection->remote_bda_);
 | 
				
			||||||
        uint64_to_bd_addr(msg.address, connection->remote_bda_);
 | 
					      connection->set_remote_addr_type(static_cast<esp_ble_addr_type_t>(msg.address_type));
 | 
				
			||||||
        connection->set_remote_addr_type(static_cast<esp_ble_addr_type_t>(msg.address_type));
 | 
					      connection->set_state(espbt::ClientState::DISCOVERED);
 | 
				
			||||||
        connection->set_state(espbt::ClientState::DISCOVERED);
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        connection->set_state(espbt::ClientState::SEARCHING);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      this->send_connections_free();
 | 
					      this->send_connections_free();
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -203,7 +203,7 @@ void BMI160Component::dump_config() {
 | 
				
			|||||||
i2c::ErrorCode BMI160Component::read_le_int16_(uint8_t reg, int16_t *value, uint8_t len) {
 | 
					i2c::ErrorCode BMI160Component::read_le_int16_(uint8_t reg, int16_t *value, uint8_t len) {
 | 
				
			||||||
  uint8_t raw_data[len * 2];
 | 
					  uint8_t raw_data[len * 2];
 | 
				
			||||||
  // read using read_register because we have little-endian data, and read_bytes_16 will swap it
 | 
					  // read using read_register because we have little-endian data, and read_bytes_16 will swap it
 | 
				
			||||||
  i2c::ErrorCode err = this->read_register(reg, raw_data, len * 2, true);
 | 
					  i2c::ErrorCode err = this->read_register(reg, raw_data, len * 2);
 | 
				
			||||||
  if (err != i2c::ERROR_OK) {
 | 
					  if (err != i2c::ERROR_OK) {
 | 
				
			||||||
    return err;
 | 
					    return err;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,12 +63,12 @@ void BMP280Component::setup() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Read the chip id twice, to work around a bug where the first read is 0.
 | 
					  // Read the chip id twice, to work around a bug where the first read is 0.
 | 
				
			||||||
  // https://community.st.com/t5/stm32-mcus-products/issue-with-reading-bmp280-chip-id-using-spi/td-p/691855
 | 
					  // https://community.st.com/t5/stm32-mcus-products/issue-with-reading-bmp280-chip-id-using-spi/td-p/691855
 | 
				
			||||||
  if (!this->read_byte(0xD0, &chip_id)) {
 | 
					  if (!this->bmp_read_byte(0xD0, &chip_id)) {
 | 
				
			||||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
					    this->error_code_ = COMMUNICATION_FAILED;
 | 
				
			||||||
    this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
 | 
					    this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (!this->read_byte(0xD0, &chip_id)) {
 | 
					  if (!this->bmp_read_byte(0xD0, &chip_id)) {
 | 
				
			||||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
					    this->error_code_ = COMMUNICATION_FAILED;
 | 
				
			||||||
    this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
 | 
					    this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
@@ -80,7 +80,7 @@ void BMP280Component::setup() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Send a soft reset.
 | 
					  // Send a soft reset.
 | 
				
			||||||
  if (!this->write_byte(BMP280_REGISTER_RESET, BMP280_SOFT_RESET)) {
 | 
					  if (!this->bmp_write_byte(BMP280_REGISTER_RESET, BMP280_SOFT_RESET)) {
 | 
				
			||||||
    this->mark_failed("Reset failed");
 | 
					    this->mark_failed("Reset failed");
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -89,7 +89,7 @@ void BMP280Component::setup() {
 | 
				
			|||||||
  uint8_t retry = 5;
 | 
					  uint8_t retry = 5;
 | 
				
			||||||
  do {
 | 
					  do {
 | 
				
			||||||
    delay(2);
 | 
					    delay(2);
 | 
				
			||||||
    if (!this->read_byte(BMP280_REGISTER_STATUS, &status)) {
 | 
					    if (!this->bmp_read_byte(BMP280_REGISTER_STATUS, &status)) {
 | 
				
			||||||
      this->mark_failed("Error reading status register");
 | 
					      this->mark_failed("Error reading status register");
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -115,14 +115,14 @@ void BMP280Component::setup() {
 | 
				
			|||||||
  this->calibration_.p9 = this->read_s16_le_(0x9E);
 | 
					  this->calibration_.p9 = this->read_s16_le_(0x9E);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint8_t config_register = 0;
 | 
					  uint8_t config_register = 0;
 | 
				
			||||||
  if (!this->read_byte(BMP280_REGISTER_CONFIG, &config_register)) {
 | 
					  if (!this->bmp_read_byte(BMP280_REGISTER_CONFIG, &config_register)) {
 | 
				
			||||||
    this->mark_failed("Read config");
 | 
					    this->mark_failed("Read config");
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  config_register &= ~0b11111100;
 | 
					  config_register &= ~0b11111100;
 | 
				
			||||||
  config_register |= 0b000 << 5;  // 0.5 ms standby time
 | 
					  config_register |= 0b000 << 5;  // 0.5 ms standby time
 | 
				
			||||||
  config_register |= (this->iir_filter_ & 0b111) << 2;
 | 
					  config_register |= (this->iir_filter_ & 0b111) << 2;
 | 
				
			||||||
  if (!this->write_byte(BMP280_REGISTER_CONFIG, config_register)) {
 | 
					  if (!this->bmp_write_byte(BMP280_REGISTER_CONFIG, config_register)) {
 | 
				
			||||||
    this->mark_failed("Write config");
 | 
					    this->mark_failed("Write config");
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -159,7 +159,7 @@ void BMP280Component::update() {
 | 
				
			|||||||
  meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
 | 
					  meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
 | 
				
			||||||
  meas_value |= (this->pressure_oversampling_ & 0b111) << 2;
 | 
					  meas_value |= (this->pressure_oversampling_ & 0b111) << 2;
 | 
				
			||||||
  meas_value |= 0b01;  // Forced mode
 | 
					  meas_value |= 0b01;  // Forced mode
 | 
				
			||||||
  if (!this->write_byte(BMP280_REGISTER_CONTROL, meas_value)) {
 | 
					  if (!this->bmp_write_byte(BMP280_REGISTER_CONTROL, meas_value)) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -188,9 +188,10 @@ void BMP280Component::update() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
float BMP280Component::read_temperature_(int32_t *t_fine) {
 | 
					float BMP280Component::read_temperature_(int32_t *t_fine) {
 | 
				
			||||||
  uint8_t data[3];
 | 
					  uint8_t data[3]{};
 | 
				
			||||||
  if (!this->read_bytes(BMP280_REGISTER_TEMPDATA, data, 3))
 | 
					  if (!this->bmp_read_bytes(BMP280_REGISTER_TEMPDATA, data, 3))
 | 
				
			||||||
    return NAN;
 | 
					    return NAN;
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Read temperature data, raw: %02X %02X %02X", data[0], data[1], data[2]);
 | 
				
			||||||
  int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
 | 
					  int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
 | 
				
			||||||
  adc >>= 4;
 | 
					  adc >>= 4;
 | 
				
			||||||
  if (adc == 0x80000) {
 | 
					  if (adc == 0x80000) {
 | 
				
			||||||
@@ -212,7 +213,7 @@ float BMP280Component::read_temperature_(int32_t *t_fine) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
float BMP280Component::read_pressure_(int32_t t_fine) {
 | 
					float BMP280Component::read_pressure_(int32_t t_fine) {
 | 
				
			||||||
  uint8_t data[3];
 | 
					  uint8_t data[3];
 | 
				
			||||||
  if (!this->read_bytes(BMP280_REGISTER_PRESSUREDATA, data, 3))
 | 
					  if (!this->bmp_read_bytes(BMP280_REGISTER_PRESSUREDATA, data, 3))
 | 
				
			||||||
    return NAN;
 | 
					    return NAN;
 | 
				
			||||||
  int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
 | 
					  int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
 | 
				
			||||||
  adc >>= 4;
 | 
					  adc >>= 4;
 | 
				
			||||||
@@ -258,12 +259,12 @@ void BMP280Component::set_pressure_oversampling(BMP280Oversampling pressure_over
 | 
				
			|||||||
void BMP280Component::set_iir_filter(BMP280IIRFilter iir_filter) { this->iir_filter_ = iir_filter; }
 | 
					void BMP280Component::set_iir_filter(BMP280IIRFilter iir_filter) { this->iir_filter_ = iir_filter; }
 | 
				
			||||||
uint8_t BMP280Component::read_u8_(uint8_t a_register) {
 | 
					uint8_t BMP280Component::read_u8_(uint8_t a_register) {
 | 
				
			||||||
  uint8_t data = 0;
 | 
					  uint8_t data = 0;
 | 
				
			||||||
  this->read_byte(a_register, &data);
 | 
					  this->bmp_read_byte(a_register, &data);
 | 
				
			||||||
  return data;
 | 
					  return data;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
uint16_t BMP280Component::read_u16_le_(uint8_t a_register) {
 | 
					uint16_t BMP280Component::read_u16_le_(uint8_t a_register) {
 | 
				
			||||||
  uint16_t data = 0;
 | 
					  uint16_t data = 0;
 | 
				
			||||||
  this->read_byte_16(a_register, &data);
 | 
					  this->bmp_read_byte_16(a_register, &data);
 | 
				
			||||||
  return (data >> 8) | (data << 8);
 | 
					  return (data >> 8) | (data << 8);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
int16_t BMP280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); }
 | 
					int16_t BMP280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,12 +67,12 @@ class BMP280Component : public PollingComponent {
 | 
				
			|||||||
  float get_setup_priority() const override;
 | 
					  float get_setup_priority() const override;
 | 
				
			||||||
  void update() override;
 | 
					  void update() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  virtual bool read_byte(uint8_t a_register, uint8_t *data) = 0;
 | 
					 | 
				
			||||||
  virtual bool write_byte(uint8_t a_register, uint8_t data) = 0;
 | 
					 | 
				
			||||||
  virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
 | 
					 | 
				
			||||||
  virtual bool read_byte_16(uint8_t a_register, uint16_t *data) = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
 | 
					  virtual bool bmp_read_byte(uint8_t a_register, uint8_t *data) = 0;
 | 
				
			||||||
 | 
					  virtual bool bmp_write_byte(uint8_t a_register, uint8_t data) = 0;
 | 
				
			||||||
 | 
					  virtual bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
 | 
				
			||||||
 | 
					  virtual bool bmp_read_byte_16(uint8_t a_register, uint16_t *data) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Read the temperature value and store the calculated ambient temperature in t_fine.
 | 
					  /// Read the temperature value and store the calculated ambient temperature in t_fine.
 | 
				
			||||||
  float read_temperature_(int32_t *t_fine);
 | 
					  float read_temperature_(int32_t *t_fine);
 | 
				
			||||||
  /// Read the pressure value in hPa using the provided t_fine value.
 | 
					  /// Read the pressure value in hPa using the provided t_fine value.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,19 +5,6 @@
 | 
				
			|||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace bmp280_i2c {
 | 
					namespace bmp280_i2c {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BMP280I2CComponent::read_byte(uint8_t a_register, uint8_t *data) {
 | 
					 | 
				
			||||||
  return I2CDevice::read_byte(a_register, data);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
bool BMP280I2CComponent::write_byte(uint8_t a_register, uint8_t data) {
 | 
					 | 
				
			||||||
  return I2CDevice::write_byte(a_register, data);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
bool BMP280I2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
 | 
					 | 
				
			||||||
  return I2CDevice::read_bytes(a_register, data, len);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
bool BMP280I2CComponent::read_byte_16(uint8_t a_register, uint16_t *data) {
 | 
					 | 
				
			||||||
  return I2CDevice::read_byte_16(a_register, data);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void BMP280I2CComponent::dump_config() {
 | 
					void BMP280I2CComponent::dump_config() {
 | 
				
			||||||
  LOG_I2C_DEVICE(this);
 | 
					  LOG_I2C_DEVICE(this);
 | 
				
			||||||
  BMP280Component::dump_config();
 | 
					  BMP280Component::dump_config();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,10 +11,12 @@ static const char *const TAG = "bmp280_i2c.sensor";
 | 
				
			|||||||
/// This class implements support for the BMP280 Temperature+Pressure i2c sensor.
 | 
					/// This class implements support for the BMP280 Temperature+Pressure i2c sensor.
 | 
				
			||||||
class BMP280I2CComponent : public esphome::bmp280_base::BMP280Component, public i2c::I2CDevice {
 | 
					class BMP280I2CComponent : public esphome::bmp280_base::BMP280Component, public i2c::I2CDevice {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  bool read_byte(uint8_t a_register, uint8_t *data) override;
 | 
					  bool bmp_read_byte(uint8_t a_register, uint8_t *data) override { return read_byte(a_register, data); }
 | 
				
			||||||
  bool write_byte(uint8_t a_register, uint8_t data) override;
 | 
					  bool bmp_write_byte(uint8_t a_register, uint8_t data) override { return write_byte(a_register, data); }
 | 
				
			||||||
  bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
 | 
					  bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) override {
 | 
				
			||||||
  bool read_byte_16(uint8_t a_register, uint16_t *data) override;
 | 
					    return read_bytes(a_register, data, len);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  bool bmp_read_byte_16(uint8_t a_register, uint16_t *data) override { return read_byte_16(a_register, data); }
 | 
				
			||||||
  void dump_config() override;
 | 
					  void dump_config() override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ void BMP280SPIComponent::setup() {
 | 
				
			|||||||
// 0x77 is transferred, for read access, the byte 0xF7 is transferred.
 | 
					// 0x77 is transferred, for read access, the byte 0xF7 is transferred.
 | 
				
			||||||
// https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf
 | 
					// https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BMP280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) {
 | 
					bool BMP280SPIComponent::bmp_read_byte(uint8_t a_register, uint8_t *data) {
 | 
				
			||||||
  this->enable();
 | 
					  this->enable();
 | 
				
			||||||
  this->transfer_byte(set_bit(a_register, 7));
 | 
					  this->transfer_byte(set_bit(a_register, 7));
 | 
				
			||||||
  *data = this->transfer_byte(0);
 | 
					  *data = this->transfer_byte(0);
 | 
				
			||||||
@@ -36,7 +36,7 @@ bool BMP280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) {
 | 
				
			|||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BMP280SPIComponent::write_byte(uint8_t a_register, uint8_t data) {
 | 
					bool BMP280SPIComponent::bmp_write_byte(uint8_t a_register, uint8_t data) {
 | 
				
			||||||
  this->enable();
 | 
					  this->enable();
 | 
				
			||||||
  this->transfer_byte(clear_bit(a_register, 7));
 | 
					  this->transfer_byte(clear_bit(a_register, 7));
 | 
				
			||||||
  this->transfer_byte(data);
 | 
					  this->transfer_byte(data);
 | 
				
			||||||
@@ -44,7 +44,7 @@ bool BMP280SPIComponent::write_byte(uint8_t a_register, uint8_t data) {
 | 
				
			|||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BMP280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
 | 
					bool BMP280SPIComponent::bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
 | 
				
			||||||
  this->enable();
 | 
					  this->enable();
 | 
				
			||||||
  this->transfer_byte(set_bit(a_register, 7));
 | 
					  this->transfer_byte(set_bit(a_register, 7));
 | 
				
			||||||
  this->read_array(data, len);
 | 
					  this->read_array(data, len);
 | 
				
			||||||
@@ -52,7 +52,7 @@ bool BMP280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t le
 | 
				
			|||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool BMP280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) {
 | 
					bool BMP280SPIComponent::bmp_read_byte_16(uint8_t a_register, uint16_t *data) {
 | 
				
			||||||
  this->enable();
 | 
					  this->enable();
 | 
				
			||||||
  this->transfer_byte(set_bit(a_register, 7));
 | 
					  this->transfer_byte(set_bit(a_register, 7));
 | 
				
			||||||
  ((uint8_t *) data)[1] = this->transfer_byte(0);
 | 
					  ((uint8_t *) data)[1] = this->transfer_byte(0);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,10 +10,10 @@ class BMP280SPIComponent : public esphome::bmp280_base::BMP280Component,
 | 
				
			|||||||
                           public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
 | 
					                           public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
 | 
				
			||||||
                                                 spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> {
 | 
					                                                 spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> {
 | 
				
			||||||
  void setup() override;
 | 
					  void setup() override;
 | 
				
			||||||
  bool read_byte(uint8_t a_register, uint8_t *data) override;
 | 
					  bool bmp_read_byte(uint8_t a_register, uint8_t *data) override;
 | 
				
			||||||
  bool write_byte(uint8_t a_register, uint8_t data) override;
 | 
					  bool bmp_write_byte(uint8_t a_register, uint8_t data) override;
 | 
				
			||||||
  bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
 | 
					  bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
 | 
				
			||||||
  bool read_byte_16(uint8_t a_register, uint16_t *data) override;
 | 
					  bool bmp_read_byte_16(uint8_t a_register, uint16_t *data) override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace bmp280_spi
 | 
					}  // namespace bmp280_spi
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,7 +91,7 @@ bool CH422GComponent::read_inputs_() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Write a register. Can't use the standard write_byte() method because there is no single pre-configured i2c address.
 | 
					// Write a register. Can't use the standard write_byte() method because there is no single pre-configured i2c address.
 | 
				
			||||||
bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) {
 | 
					bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) {
 | 
				
			||||||
  auto err = this->bus_->write(reg, &value, 1);
 | 
					  auto err = this->bus_->write_readv(reg, &value, 1, nullptr, 0);
 | 
				
			||||||
  if (err != i2c::ERROR_OK) {
 | 
					  if (err != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str());
 | 
					    this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str());
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
@@ -102,7 +102,7 @@ bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
uint8_t CH422GComponent::read_reg_(uint8_t reg) {
 | 
					uint8_t CH422GComponent::read_reg_(uint8_t reg) {
 | 
				
			||||||
  uint8_t value;
 | 
					  uint8_t value;
 | 
				
			||||||
  auto err = this->bus_->read(reg, &value, 1);
 | 
					  auto err = this->bus_->write_readv(reg, nullptr, 0, &value, 1);
 | 
				
			||||||
  if (err != i2c::ERROR_OK) {
 | 
					  if (err != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str());
 | 
					    this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str());
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,7 @@ void EE895Component::write_command_(uint16_t addr, uint16_t reg_cnt) {
 | 
				
			|||||||
  crc16 = calc_crc16_(address, 6);
 | 
					  crc16 = calc_crc16_(address, 6);
 | 
				
			||||||
  address[5] = crc16 & 0xFF;
 | 
					  address[5] = crc16 & 0xFF;
 | 
				
			||||||
  address[6] = (crc16 >> 8) & 0xFF;
 | 
					  address[6] = (crc16 >> 8) & 0xFF;
 | 
				
			||||||
  this->write(address, 7, true);
 | 
					  this->write(address, 7);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
float EE895Component::read_float_() {
 | 
					float EE895Component::read_float_() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,7 +93,7 @@ bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  if (this->address_ == 0 || device.address_uint64() != this->address_)
 | 
					  if (this->address_ == 0 || device.address_uint64() != this->address_)
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  if (this->state_ != espbt::ClientState::IDLE && this->state_ != espbt::ClientState::SEARCHING)
 | 
					  if (this->state_ != espbt::ClientState::IDLE)
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->log_event_("Found device");
 | 
					  this->log_event_("Found device");
 | 
				
			||||||
@@ -168,8 +168,7 @@ void BLEClientBase::unconditional_disconnect() {
 | 
				
			|||||||
    this->log_gattc_warning_("esp_ble_gattc_close", err);
 | 
					    this->log_gattc_warning_("esp_ble_gattc_close", err);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (this->state_ == espbt::ClientState::SEARCHING || this->state_ == espbt::ClientState::READY_TO_CONNECT ||
 | 
					  if (this->state_ == espbt::ClientState::READY_TO_CONNECT || this->state_ == espbt::ClientState::DISCOVERED) {
 | 
				
			||||||
      this->state_ == espbt::ClientState::DISCOVERED) {
 | 
					 | 
				
			||||||
    this->set_address(0);
 | 
					    this->set_address(0);
 | 
				
			||||||
    this->set_state(espbt::ClientState::IDLE);
 | 
					    this->set_state(espbt::ClientState::IDLE);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,8 +49,6 @@ const char *client_state_to_string(ClientState state) {
 | 
				
			|||||||
      return "DISCONNECTING";
 | 
					      return "DISCONNECTING";
 | 
				
			||||||
    case ClientState::IDLE:
 | 
					    case ClientState::IDLE:
 | 
				
			||||||
      return "IDLE";
 | 
					      return "IDLE";
 | 
				
			||||||
    case ClientState::SEARCHING:
 | 
					 | 
				
			||||||
      return "SEARCHING";
 | 
					 | 
				
			||||||
    case ClientState::DISCOVERED:
 | 
					    case ClientState::DISCOVERED:
 | 
				
			||||||
      return "DISCOVERED";
 | 
					      return "DISCOVERED";
 | 
				
			||||||
    case ClientState::READY_TO_CONNECT:
 | 
					    case ClientState::READY_TO_CONNECT:
 | 
				
			||||||
@@ -136,9 +134,8 @@ void ESP32BLETracker::loop() {
 | 
				
			|||||||
  ClientStateCounts counts = this->count_client_states_();
 | 
					  ClientStateCounts counts = this->count_client_states_();
 | 
				
			||||||
  if (counts != this->client_state_counts_) {
 | 
					  if (counts != this->client_state_counts_) {
 | 
				
			||||||
    this->client_state_counts_ = counts;
 | 
					    this->client_state_counts_ = counts;
 | 
				
			||||||
    ESP_LOGD(TAG, "connecting: %d, discovered: %d, searching: %d, disconnecting: %d",
 | 
					    ESP_LOGD(TAG, "connecting: %d, discovered: %d, disconnecting: %d", this->client_state_counts_.connecting,
 | 
				
			||||||
             this->client_state_counts_.connecting, this->client_state_counts_.discovered,
 | 
					             this->client_state_counts_.discovered, this->client_state_counts_.disconnecting);
 | 
				
			||||||
             this->client_state_counts_.searching, this->client_state_counts_.disconnecting);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (this->scanner_state_ == ScannerState::FAILED ||
 | 
					  if (this->scanner_state_ == ScannerState::FAILED ||
 | 
				
			||||||
@@ -158,10 +155,8 @@ void ESP32BLETracker::loop() {
 | 
				
			|||||||
    https://github.com/espressif/esp-idf/issues/6688
 | 
					    https://github.com/espressif/esp-idf/issues/6688
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
  bool promote_to_connecting = counts.discovered && !counts.searching && !counts.connecting;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (this->scanner_state_ == ScannerState::IDLE && !counts.connecting && !counts.disconnecting &&
 | 
					  if (this->scanner_state_ == ScannerState::IDLE && !counts.connecting && !counts.disconnecting && !counts.discovered) {
 | 
				
			||||||
      !promote_to_connecting) {
 | 
					 | 
				
			||||||
#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
 | 
					#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
 | 
				
			||||||
    this->update_coex_preference_(false);
 | 
					    this->update_coex_preference_(false);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -170,12 +165,11 @@ void ESP32BLETracker::loop() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // If there is a discovered client and no connecting
 | 
					  // If there is a discovered client and no connecting
 | 
				
			||||||
  // clients and no clients using the scanner to search for
 | 
					  // clients, then promote the discovered client to ready to connect.
 | 
				
			||||||
  // devices, then promote the discovered client to ready to connect.
 | 
					 | 
				
			||||||
  // We check both RUNNING and IDLE states because:
 | 
					  // We check both RUNNING and IDLE states because:
 | 
				
			||||||
  // - RUNNING: gap_scan_event_handler initiates stop_scan_() but promotion can happen immediately
 | 
					  // - RUNNING: gap_scan_event_handler initiates stop_scan_() but promotion can happen immediately
 | 
				
			||||||
  // - IDLE: Scanner has already stopped (naturally or by gap_scan_event_handler)
 | 
					  // - IDLE: Scanner has already stopped (naturally or by gap_scan_event_handler)
 | 
				
			||||||
  if (promote_to_connecting &&
 | 
					  if (counts.discovered && !counts.connecting &&
 | 
				
			||||||
      (this->scanner_state_ == ScannerState::RUNNING || this->scanner_state_ == ScannerState::IDLE)) {
 | 
					      (this->scanner_state_ == ScannerState::RUNNING || this->scanner_state_ == ScannerState::IDLE)) {
 | 
				
			||||||
    this->try_promote_discovered_clients_();
 | 
					    this->try_promote_discovered_clients_();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -307,14 +301,7 @@ void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
 | 
					  if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
 | 
				
			||||||
    // Process the scan result immediately
 | 
					    // Process the scan result immediately
 | 
				
			||||||
    bool found_discovered_client = this->process_scan_result_(scan_result);
 | 
					    this->process_scan_result_(scan_result);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // If we found a discovered client that needs promotion, stop scanning
 | 
					 | 
				
			||||||
    // This replaces the promote_to_connecting logic from loop()
 | 
					 | 
				
			||||||
    if (found_discovered_client && this->scanner_state_ == ScannerState::RUNNING) {
 | 
					 | 
				
			||||||
      ESP_LOGD(TAG, "Found discovered client, stopping scan for connection");
 | 
					 | 
				
			||||||
      this->stop_scan_();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  } else if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) {
 | 
					  } else if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) {
 | 
				
			||||||
    // Scan finished on its own
 | 
					    // Scan finished on its own
 | 
				
			||||||
    if (this->scanner_state_ != ScannerState::RUNNING) {
 | 
					    if (this->scanner_state_ != ScannerState::RUNNING) {
 | 
				
			||||||
@@ -640,9 +627,8 @@ void ESP32BLETracker::dump_config() {
 | 
				
			|||||||
                this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f,
 | 
					                this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f,
 | 
				
			||||||
                this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_));
 | 
					                this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_));
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "  Scanner State: %s", this->scanner_state_to_string_(this->scanner_state_));
 | 
					  ESP_LOGCONFIG(TAG, "  Scanner State: %s", this->scanner_state_to_string_(this->scanner_state_));
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "  Connecting: %d, discovered: %d, searching: %d, disconnecting: %d",
 | 
					  ESP_LOGCONFIG(TAG, "  Connecting: %d, discovered: %d, disconnecting: %d", this->client_state_counts_.connecting,
 | 
				
			||||||
                this->client_state_counts_.connecting, this->client_state_counts_.discovered,
 | 
					                this->client_state_counts_.discovered, this->client_state_counts_.disconnecting);
 | 
				
			||||||
                this->client_state_counts_.searching, this->client_state_counts_.disconnecting);
 | 
					 | 
				
			||||||
  if (this->scan_start_fail_count_) {
 | 
					  if (this->scan_start_fail_count_) {
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "  Scan Start Fail Count: %d", this->scan_start_fail_count_);
 | 
					    ESP_LOGCONFIG(TAG, "  Scan Start Fail Count: %d", this->scan_start_fail_count_);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -720,20 +706,9 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
 | 
				
			|||||||
         ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
 | 
					         ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool ESP32BLETracker::has_connecting_clients_() const {
 | 
					 | 
				
			||||||
  for (auto *client : this->clients_) {
 | 
					 | 
				
			||||||
    auto state = client->state();
 | 
					 | 
				
			||||||
    if (state == ClientState::CONNECTING || state == ClientState::READY_TO_CONNECT) {
 | 
					 | 
				
			||||||
      return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif  // USE_ESP32_BLE_DEVICE
 | 
					#endif  // USE_ESP32_BLE_DEVICE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
 | 
					void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
 | 
				
			||||||
  bool found_discovered_client = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Process raw advertisements
 | 
					  // Process raw advertisements
 | 
				
			||||||
  if (this->raw_advertisements_) {
 | 
					  if (this->raw_advertisements_) {
 | 
				
			||||||
    for (auto *listener : this->listeners_) {
 | 
					    for (auto *listener : this->listeners_) {
 | 
				
			||||||
@@ -759,14 +734,6 @@ bool ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
 | 
				
			|||||||
    for (auto *client : this->clients_) {
 | 
					    for (auto *client : this->clients_) {
 | 
				
			||||||
      if (client->parse_device(device)) {
 | 
					      if (client->parse_device(device)) {
 | 
				
			||||||
        found = true;
 | 
					        found = true;
 | 
				
			||||||
        // Check if this client is discovered and needs promotion
 | 
					 | 
				
			||||||
        if (client->state() == ClientState::DISCOVERED) {
 | 
					 | 
				
			||||||
          // Only check for connecting clients if we found a discovered client
 | 
					 | 
				
			||||||
          // This matches the original logic: !connecting && client->state() == DISCOVERED
 | 
					 | 
				
			||||||
          if (!this->has_connecting_clients_()) {
 | 
					 | 
				
			||||||
            found_discovered_client = true;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -775,8 +742,6 @@ bool ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
#endif  // USE_ESP32_BLE_DEVICE
 | 
					#endif  // USE_ESP32_BLE_DEVICE
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  return found_discovered_client;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) {
 | 
					void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,12 +141,10 @@ class ESPBTDeviceListener {
 | 
				
			|||||||
struct ClientStateCounts {
 | 
					struct ClientStateCounts {
 | 
				
			||||||
  uint8_t connecting = 0;
 | 
					  uint8_t connecting = 0;
 | 
				
			||||||
  uint8_t discovered = 0;
 | 
					  uint8_t discovered = 0;
 | 
				
			||||||
  uint8_t searching = 0;
 | 
					 | 
				
			||||||
  uint8_t disconnecting = 0;
 | 
					  uint8_t disconnecting = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool operator==(const ClientStateCounts &other) const {
 | 
					  bool operator==(const ClientStateCounts &other) const {
 | 
				
			||||||
    return connecting == other.connecting && discovered == other.discovered && searching == other.searching &&
 | 
					    return connecting == other.connecting && discovered == other.discovered && disconnecting == other.disconnecting;
 | 
				
			||||||
           disconnecting == other.disconnecting;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool operator!=(const ClientStateCounts &other) const { return !(*this == other); }
 | 
					  bool operator!=(const ClientStateCounts &other) const { return !(*this == other); }
 | 
				
			||||||
@@ -159,8 +157,6 @@ enum class ClientState : uint8_t {
 | 
				
			|||||||
  DISCONNECTING,
 | 
					  DISCONNECTING,
 | 
				
			||||||
  // Connection is idle, no device detected.
 | 
					  // Connection is idle, no device detected.
 | 
				
			||||||
  IDLE,
 | 
					  IDLE,
 | 
				
			||||||
  // Searching for device.
 | 
					 | 
				
			||||||
  SEARCHING,
 | 
					 | 
				
			||||||
  // Device advertisement found.
 | 
					  // Device advertisement found.
 | 
				
			||||||
  DISCOVERED,
 | 
					  DISCOVERED,
 | 
				
			||||||
  // Device is discovered and the scanner is stopped
 | 
					  // Device is discovered and the scanner is stopped
 | 
				
			||||||
@@ -292,12 +288,7 @@ class ESP32BLETracker : public Component,
 | 
				
			|||||||
  /// Common cleanup logic when transitioning scanner to IDLE state
 | 
					  /// Common cleanup logic when transitioning scanner to IDLE state
 | 
				
			||||||
  void cleanup_scan_state_(bool is_stop_complete);
 | 
					  void cleanup_scan_state_(bool is_stop_complete);
 | 
				
			||||||
  /// Process a single scan result immediately
 | 
					  /// Process a single scan result immediately
 | 
				
			||||||
  /// Returns true if a discovered client needs promotion to READY_TO_CONNECT
 | 
					  void process_scan_result_(const BLEScanResult &scan_result);
 | 
				
			||||||
  bool process_scan_result_(const BLEScanResult &scan_result);
 | 
					 | 
				
			||||||
#ifdef USE_ESP32_BLE_DEVICE
 | 
					 | 
				
			||||||
  /// Check if any clients are in connecting or ready to connect state
 | 
					 | 
				
			||||||
  bool has_connecting_clients_() const;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
  /// Handle scanner failure states
 | 
					  /// Handle scanner failure states
 | 
				
			||||||
  void handle_scanner_failure_();
 | 
					  void handle_scanner_failure_();
 | 
				
			||||||
  /// Try to promote discovered clients to ready to connect
 | 
					  /// Try to promote discovered clients to ready to connect
 | 
				
			||||||
@@ -321,9 +312,6 @@ class ESP32BLETracker : public Component,
 | 
				
			|||||||
        case ClientState::DISCOVERED:
 | 
					        case ClientState::DISCOVERED:
 | 
				
			||||||
          counts.discovered++;
 | 
					          counts.discovered++;
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        case ClientState::SEARCHING:
 | 
					 | 
				
			||||||
          counts.searching++;
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case ClientState::CONNECTING:
 | 
					        case ClientState::CONNECTING:
 | 
				
			||||||
        case ClientState::READY_TO_CONNECT:
 | 
					        case ClientState::READY_TO_CONNECT:
 | 
				
			||||||
          counts.connecting++;
 | 
					          counts.connecting++;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,8 +100,8 @@ void ESPHomeOTAComponent::handle_handshake_() {
 | 
				
			|||||||
  /// Handle the initial OTA handshake.
 | 
					  /// Handle the initial OTA handshake.
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// This method is non-blocking and will return immediately if no data is available.
 | 
					  /// This method is non-blocking and will return immediately if no data is available.
 | 
				
			||||||
  /// It waits for the first magic byte (0x6C) before proceeding to handle_data_().
 | 
					  /// It reads all 5 magic bytes (0x6C, 0x26, 0xF7, 0x5C, 0x45) non-blocking
 | 
				
			||||||
  /// A 10-second timeout is enforced from initial connection.
 | 
					  /// before proceeding to handle_data_(). A 10-second timeout is enforced from initial connection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (this->client_ == nullptr) {
 | 
					  if (this->client_ == nullptr) {
 | 
				
			||||||
    // We already checked server_->ready() in loop(), so we can accept directly
 | 
					    // We already checked server_->ready() in loop(), so we can accept directly
 | 
				
			||||||
@@ -126,6 +126,7 @@ void ESPHomeOTAComponent::handle_handshake_() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    this->log_start_("handshake");
 | 
					    this->log_start_("handshake");
 | 
				
			||||||
    this->client_connect_time_ = App.get_loop_component_start_time();
 | 
					    this->client_connect_time_ = App.get_loop_component_start_time();
 | 
				
			||||||
 | 
					    this->magic_buf_pos_ = 0;  // Reset magic buffer position
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Check for handshake timeout
 | 
					  // Check for handshake timeout
 | 
				
			||||||
@@ -136,34 +137,47 @@ void ESPHomeOTAComponent::handle_handshake_() {
 | 
				
			|||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Try to read first byte of magic bytes
 | 
					  // Try to read remaining magic bytes
 | 
				
			||||||
  uint8_t first_byte;
 | 
					  if (this->magic_buf_pos_ < 5) {
 | 
				
			||||||
  ssize_t read = this->client_->read(&first_byte, 1);
 | 
					    // Read as many bytes as available
 | 
				
			||||||
 | 
					    uint8_t bytes_to_read = 5 - this->magic_buf_pos_;
 | 
				
			||||||
 | 
					    ssize_t read = this->client_->read(this->magic_buf_ + this->magic_buf_pos_, bytes_to_read);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
 | 
					    if (read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
 | 
				
			||||||
    return;  // No data yet, try again next loop
 | 
					      return;  // No data yet, try again next loop
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (read <= 0) {
 | 
					 | 
				
			||||||
    // Error or connection closed
 | 
					 | 
				
			||||||
    if (read == -1) {
 | 
					 | 
				
			||||||
      this->log_socket_error_("reading first byte");
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      ESP_LOGW(TAG, "Remote closed during handshake");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this->cleanup_connection_();
 | 
					
 | 
				
			||||||
    return;
 | 
					    if (read <= 0) {
 | 
				
			||||||
 | 
					      // Error or connection closed
 | 
				
			||||||
 | 
					      if (read == -1) {
 | 
				
			||||||
 | 
					        this->log_socket_error_("reading magic bytes");
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        ESP_LOGW(TAG, "Remote closed during handshake");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this->cleanup_connection_();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this->magic_buf_pos_ += read;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Got first byte, check if it's the magic byte
 | 
					  // Check if we have all 5 magic bytes
 | 
				
			||||||
  if (first_byte != 0x6C) {
 | 
					  if (this->magic_buf_pos_ == 5) {
 | 
				
			||||||
    ESP_LOGW(TAG, "Invalid initial byte: 0x%02X", first_byte);
 | 
					    // Validate magic bytes
 | 
				
			||||||
    this->cleanup_connection_();
 | 
					    static const uint8_t MAGIC_BYTES[5] = {0x6C, 0x26, 0xF7, 0x5C, 0x45};
 | 
				
			||||||
    return;
 | 
					    if (memcmp(this->magic_buf_, MAGIC_BYTES, 5) != 0) {
 | 
				
			||||||
  }
 | 
					      ESP_LOGW(TAG, "Magic bytes mismatch! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", this->magic_buf_[0],
 | 
				
			||||||
 | 
					               this->magic_buf_[1], this->magic_buf_[2], this->magic_buf_[3], this->magic_buf_[4]);
 | 
				
			||||||
 | 
					      // Send error response (non-blocking, best effort)
 | 
				
			||||||
 | 
					      uint8_t error = static_cast<uint8_t>(ota::OTA_RESPONSE_ERROR_MAGIC);
 | 
				
			||||||
 | 
					      this->client_->write(&error, 1);
 | 
				
			||||||
 | 
					      this->cleanup_connection_();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // First byte is valid, continue with data handling
 | 
					    // All 5 magic bytes are valid, continue with data handling
 | 
				
			||||||
  this->handle_data_();
 | 
					    this->handle_data_();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ESPHomeOTAComponent::handle_data_() {
 | 
					void ESPHomeOTAComponent::handle_data_() {
 | 
				
			||||||
@@ -186,18 +200,6 @@ void ESPHomeOTAComponent::handle_data_() {
 | 
				
			|||||||
  size_t size_acknowledged = 0;
 | 
					  size_t size_acknowledged = 0;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Read remaining 4 bytes of magic (we already read the first byte 0x6C in handle_handshake_)
 | 
					 | 
				
			||||||
  if (!this->readall_(buf, 4)) {
 | 
					 | 
				
			||||||
    this->log_read_error_("magic bytes");
 | 
					 | 
				
			||||||
    goto error;  // NOLINT(cppcoreguidelines-avoid-goto)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  // Check remaining magic bytes: 0x26, 0xF7, 0x5C, 0x45
 | 
					 | 
				
			||||||
  if (buf[0] != 0x26 || buf[1] != 0xF7 || buf[2] != 0x5C || buf[3] != 0x45) {
 | 
					 | 
				
			||||||
    ESP_LOGW(TAG, "Magic bytes mismatch! 0x6C-0x%02X-0x%02X-0x%02X-0x%02X", buf[0], buf[1], buf[2], buf[3]);
 | 
					 | 
				
			||||||
    error_code = ota::OTA_RESPONSE_ERROR_MAGIC;
 | 
					 | 
				
			||||||
    goto error;  // NOLINT(cppcoreguidelines-avoid-goto)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Send OK and version - 2 bytes
 | 
					  // Send OK and version - 2 bytes
 | 
				
			||||||
  buf[0] = ota::OTA_RESPONSE_OK;
 | 
					  buf[0] = ota::OTA_RESPONSE_OK;
 | 
				
			||||||
  buf[1] = USE_OTA_VERSION;
 | 
					  buf[1] = USE_OTA_VERSION;
 | 
				
			||||||
@@ -487,6 +489,7 @@ void ESPHomeOTAComponent::cleanup_connection_() {
 | 
				
			|||||||
  this->client_->close();
 | 
					  this->client_->close();
 | 
				
			||||||
  this->client_ = nullptr;
 | 
					  this->client_ = nullptr;
 | 
				
			||||||
  this->client_connect_time_ = 0;
 | 
					  this->client_connect_time_ = 0;
 | 
				
			||||||
 | 
					  this->magic_buf_pos_ = 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ESPHomeOTAComponent::yield_and_feed_watchdog_() {
 | 
					void ESPHomeOTAComponent::yield_and_feed_watchdog_() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,11 +41,13 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
 | 
				
			|||||||
  std::string password_;
 | 
					  std::string password_;
 | 
				
			||||||
#endif  // USE_OTA_PASSWORD
 | 
					#endif  // USE_OTA_PASSWORD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint16_t port_;
 | 
					 | 
				
			||||||
  uint32_t client_connect_time_{0};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::unique_ptr<socket::Socket> server_;
 | 
					  std::unique_ptr<socket::Socket> server_;
 | 
				
			||||||
  std::unique_ptr<socket::Socket> client_;
 | 
					  std::unique_ptr<socket::Socket> client_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t client_connect_time_{0};
 | 
				
			||||||
 | 
					  uint16_t port_;
 | 
				
			||||||
 | 
					  uint8_t magic_buf_[5];
 | 
				
			||||||
 | 
					  uint8_t magic_buf_pos_{0};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,9 +9,8 @@ static const char *const TAG = "hte501";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void HTE501Component::setup() {
 | 
					void HTE501Component::setup() {
 | 
				
			||||||
  uint8_t address[] = {0x70, 0x29};
 | 
					  uint8_t address[] = {0x70, 0x29};
 | 
				
			||||||
  this->write(address, 2, false);
 | 
					 | 
				
			||||||
  uint8_t identification[9];
 | 
					  uint8_t identification[9];
 | 
				
			||||||
  this->read(identification, 9);
 | 
					  this->write_read(address, sizeof address, identification, sizeof identification);
 | 
				
			||||||
  if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) {
 | 
					  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();
 | 
				
			||||||
@@ -42,7 +41,7 @@ void HTE501Component::dump_config() {
 | 
				
			|||||||
float HTE501Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
					float HTE501Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
				
			||||||
void HTE501Component::update() {
 | 
					void HTE501Component::update() {
 | 
				
			||||||
  uint8_t address_1[] = {0x2C, 0x1B};
 | 
					  uint8_t address_1[] = {0x2C, 0x1B};
 | 
				
			||||||
  this->write(address_1, 2, true);
 | 
					  this->write(address_1, 2);
 | 
				
			||||||
  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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,6 @@ import logging
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from esphome import pins
 | 
					from esphome import pins
 | 
				
			||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
from esphome.components import esp32
 | 
					 | 
				
			||||||
from esphome.config_helpers import filter_source_files_from_platform
 | 
					from esphome.config_helpers import filter_source_files_from_platform
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
@@ -14,8 +13,6 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_SCL,
 | 
					    CONF_SCL,
 | 
				
			||||||
    CONF_SDA,
 | 
					    CONF_SDA,
 | 
				
			||||||
    CONF_TIMEOUT,
 | 
					    CONF_TIMEOUT,
 | 
				
			||||||
    KEY_CORE,
 | 
					 | 
				
			||||||
    KEY_FRAMEWORK_VERSION,
 | 
					 | 
				
			||||||
    PLATFORM_ESP32,
 | 
					    PLATFORM_ESP32,
 | 
				
			||||||
    PLATFORM_ESP8266,
 | 
					    PLATFORM_ESP8266,
 | 
				
			||||||
    PLATFORM_RP2040,
 | 
					    PLATFORM_RP2040,
 | 
				
			||||||
@@ -48,28 +45,8 @@ def _bus_declare_type(value):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def validate_config(config):
 | 
					def validate_config(config):
 | 
				
			||||||
    if (
 | 
					    if CORE.using_esp_idf:
 | 
				
			||||||
        config[CONF_SCAN]
 | 
					        return cv.require_framework_version(esp_idf=cv.Version(5, 4, 2))(config)
 | 
				
			||||||
        and CORE.is_esp32
 | 
					 | 
				
			||||||
        and CORE.using_esp_idf
 | 
					 | 
				
			||||||
        and esp32.get_esp32_variant()
 | 
					 | 
				
			||||||
        in [
 | 
					 | 
				
			||||||
            esp32.const.VARIANT_ESP32C5,
 | 
					 | 
				
			||||||
            esp32.const.VARIANT_ESP32C6,
 | 
					 | 
				
			||||||
            esp32.const.VARIANT_ESP32P4,
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
    ):
 | 
					 | 
				
			||||||
        version: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
 | 
					 | 
				
			||||||
        if version.major == 5 and (
 | 
					 | 
				
			||||||
            (version.minor == 3 and version.patch <= 3)
 | 
					 | 
				
			||||||
            or (version.minor == 4 and version.patch <= 1)
 | 
					 | 
				
			||||||
        ):
 | 
					 | 
				
			||||||
            LOGGER.warning(
 | 
					 | 
				
			||||||
                "There is a bug in esp-idf version %s that breaks I2C scan, I2C scan "
 | 
					 | 
				
			||||||
                "has been disabled, see https://github.com/esphome/issues/issues/7128",
 | 
					 | 
				
			||||||
                str(version),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            config[CONF_SCAN] = False
 | 
					 | 
				
			||||||
    return config
 | 
					    return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
#include "i2c.h"
 | 
					#include "i2c.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/defines.h"
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -7,38 +9,48 @@ namespace i2c {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static const char *const TAG = "i2c";
 | 
					static const char *const TAG = "i2c";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ErrorCode I2CDevice::read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop) {
 | 
					void I2CBus::i2c_scan_() {
 | 
				
			||||||
  ErrorCode err = this->write(&a_register, 1, stop);
 | 
					  // suppress logs from the IDF I2C library during the scan
 | 
				
			||||||
  if (err != ERROR_OK)
 | 
					#if defined(USE_ESP32) && defined(USE_LOGGER)
 | 
				
			||||||
    return err;
 | 
					  auto previous = esp_log_level_get("*");
 | 
				
			||||||
  return bus_->read(address_, data, len);
 | 
					  esp_log_level_set("*", ESP_LOG_NONE);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (uint8_t address = 8; address != 120; address++) {
 | 
				
			||||||
 | 
					    auto err = write_readv(address, nullptr, 0, nullptr, 0);
 | 
				
			||||||
 | 
					    if (err == ERROR_OK) {
 | 
				
			||||||
 | 
					      scan_results_.emplace_back(address, true);
 | 
				
			||||||
 | 
					    } else if (err == ERROR_UNKNOWN) {
 | 
				
			||||||
 | 
					      scan_results_.emplace_back(address, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#if defined(USE_ESP32) && defined(USE_LOGGER)
 | 
				
			||||||
 | 
					  esp_log_level_set("*", previous);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ErrorCode I2CDevice::read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop) {
 | 
					ErrorCode I2CDevice::read_register(uint8_t a_register, uint8_t *data, size_t len) {
 | 
				
			||||||
 | 
					  return bus_->write_readv(this->address_, &a_register, 1, data, len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ErrorCode I2CDevice::read_register16(uint16_t a_register, uint8_t *data, size_t len) {
 | 
				
			||||||
  a_register = convert_big_endian(a_register);
 | 
					  a_register = convert_big_endian(a_register);
 | 
				
			||||||
  ErrorCode const err = this->write(reinterpret_cast<const uint8_t *>(&a_register), 2, stop);
 | 
					  return bus_->write_readv(this->address_, reinterpret_cast<const uint8_t *>(&a_register), 2, data, len);
 | 
				
			||||||
  if (err != ERROR_OK)
 | 
					 | 
				
			||||||
    return err;
 | 
					 | 
				
			||||||
  return bus_->read(address_, data, len);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop) {
 | 
					ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len) const {
 | 
				
			||||||
  WriteBuffer buffers[2];
 | 
					  std::vector<uint8_t> v{};
 | 
				
			||||||
  buffers[0].data = &a_register;
 | 
					  v.push_back(a_register);
 | 
				
			||||||
  buffers[0].len = 1;
 | 
					  v.insert(v.end(), data, data + len);
 | 
				
			||||||
  buffers[1].data = data;
 | 
					  return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0);
 | 
				
			||||||
  buffers[1].len = len;
 | 
					 | 
				
			||||||
  return bus_->writev(address_, buffers, 2, stop);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop) {
 | 
					ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len) const {
 | 
				
			||||||
  a_register = convert_big_endian(a_register);
 | 
					  std::vector<uint8_t> v(len + 2);
 | 
				
			||||||
  WriteBuffer buffers[2];
 | 
					  v.push_back(a_register >> 8);
 | 
				
			||||||
  buffers[0].data = reinterpret_cast<const uint8_t *>(&a_register);
 | 
					  v.push_back(a_register);
 | 
				
			||||||
  buffers[0].len = 2;
 | 
					  v.insert(v.end(), data, data + len);
 | 
				
			||||||
  buffers[1].data = data;
 | 
					  return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0);
 | 
				
			||||||
  buffers[1].len = len;
 | 
					 | 
				
			||||||
  return bus_->writev(address_, buffers, 2, stop);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) {
 | 
					bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) {
 | 
				
			||||||
@@ -49,7 +61,7 @@ bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) {
 | 
				
			|||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) {
 | 
					bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) const {
 | 
				
			||||||
  // we have to copy in order to be able to change byte order
 | 
					  // we have to copy in order to be able to change byte order
 | 
				
			||||||
  std::unique_ptr<uint16_t[]> temp{new uint16_t[len]};
 | 
					  std::unique_ptr<uint16_t[]> temp{new uint16_t[len]};
 | 
				
			||||||
  for (size_t i = 0; i < len; i++)
 | 
					  for (size_t i = 0; i < len; i++)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "i2c_bus.h"
 | 
					 | 
				
			||||||
#include "esphome/core/helpers.h"
 | 
					 | 
				
			||||||
#include "esphome/core/optional.h"
 | 
					 | 
				
			||||||
#include <array>
 | 
					#include <array>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/optional.h"
 | 
				
			||||||
 | 
					#include "i2c_bus.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace i2c {
 | 
					namespace i2c {
 | 
				
			||||||
@@ -161,51 +161,53 @@ class I2CDevice {
 | 
				
			|||||||
  /// @param data pointer to an array to store the bytes
 | 
					  /// @param data pointer to an array to store the bytes
 | 
				
			||||||
  /// @param len length of the buffer = number of bytes to read
 | 
					  /// @param len length of the buffer = number of bytes to read
 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					  /// @return an i2c::ErrorCode
 | 
				
			||||||
  ErrorCode read(uint8_t *data, size_t len) { return bus_->read(address_, data, len); }
 | 
					  ErrorCode read(uint8_t *data, size_t len) const { return bus_->write_readv(this->address_, nullptr, 0, data, len); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// @brief reads an array of bytes from a specific register in the I²C device
 | 
					  /// @brief reads an array of bytes from a specific register in the I²C device
 | 
				
			||||||
  /// @param a_register an 8 bits internal address of the I²C register to read from
 | 
					  /// @param a_register an 8 bits internal address of the I²C register to read from
 | 
				
			||||||
  /// @param data pointer to an array to store the bytes
 | 
					  /// @param data pointer to an array to store the bytes
 | 
				
			||||||
  /// @param len length of the buffer = number of bytes to read
 | 
					  /// @param len length of the buffer = number of bytes to read
 | 
				
			||||||
  /// @param stop (true/false): True will send a stop message, releasing the bus after
 | 
					 | 
				
			||||||
  /// transmission. False will send a restart, keeping the connection active.
 | 
					 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					  /// @return an i2c::ErrorCode
 | 
				
			||||||
  ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop = true);
 | 
					  ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// @brief reads an array of bytes from a specific register in the I²C device
 | 
					  /// @brief reads an array of bytes from a specific register in the I²C device
 | 
				
			||||||
  /// @param a_register the 16 bits internal address of the I²C register to read from
 | 
					  /// @param a_register the 16 bits internal address of the I²C register to read from
 | 
				
			||||||
  /// @param data pointer to an array of bytes to store the information
 | 
					  /// @param data pointer to an array of bytes to store the information
 | 
				
			||||||
  /// @param len length of the buffer = number of bytes to read
 | 
					  /// @param len length of the buffer = number of bytes to read
 | 
				
			||||||
  /// @param stop (true/false): True will send a stop message, releasing the bus after
 | 
					 | 
				
			||||||
  /// transmission. False will send a restart, keeping the connection active.
 | 
					 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					  /// @return an i2c::ErrorCode
 | 
				
			||||||
  ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop = true);
 | 
					  ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// @brief writes an array of bytes to a device using an I2CBus
 | 
					  /// @brief writes an array of bytes to a device using an I2CBus
 | 
				
			||||||
  /// @param data pointer to an array that contains the bytes to send
 | 
					  /// @param data pointer to an array that contains the bytes to send
 | 
				
			||||||
  /// @param len length of the buffer = number of bytes to write
 | 
					  /// @param len length of the buffer = number of bytes to write
 | 
				
			||||||
  /// @param stop (true/false): True will send a stop message, releasing the bus after
 | 
					 | 
				
			||||||
  /// transmission. False will send a restart, keeping the connection active.
 | 
					 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					  /// @return an i2c::ErrorCode
 | 
				
			||||||
  ErrorCode write(const uint8_t *data, size_t len, bool stop = true) { return bus_->write(address_, data, len, stop); }
 | 
					  ErrorCode write(const uint8_t *data, size_t len) const {
 | 
				
			||||||
 | 
					    return bus_->write_readv(this->address_, data, len, nullptr, 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// @brief writes an array of bytes to a device, then reads an array, as a single transaction
 | 
				
			||||||
 | 
					  /// @param write_data pointer to an array that contains the bytes to send
 | 
				
			||||||
 | 
					  /// @param write_len length of the buffer = number of bytes to write
 | 
				
			||||||
 | 
					  /// @param read_data pointer to an array to store the bytes read
 | 
				
			||||||
 | 
					  /// @param read_len length of the buffer = number of bytes to read
 | 
				
			||||||
 | 
					  /// @return an i2c::ErrorCode
 | 
				
			||||||
 | 
					  ErrorCode write_read(const uint8_t *write_data, size_t write_len, uint8_t *read_data, size_t read_len) const {
 | 
				
			||||||
 | 
					    return bus_->write_readv(this->address_, write_data, write_len, read_data, read_len);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// @brief writes an array of bytes to a specific register in the I²C device
 | 
					  /// @brief writes an array of bytes to a specific register in the I²C device
 | 
				
			||||||
  /// @param a_register the internal address of the register to read from
 | 
					  /// @param a_register the internal address of the register to read from
 | 
				
			||||||
  /// @param data pointer to an array to store the bytes
 | 
					  /// @param data pointer to an array to store the bytes
 | 
				
			||||||
  /// @param len length of the buffer = number of bytes to read
 | 
					  /// @param len length of the buffer = number of bytes to read
 | 
				
			||||||
  /// @param stop (true/false): True will send a stop message, releasing the bus after
 | 
					 | 
				
			||||||
  /// transmission. False will send a restart, keeping the connection active.
 | 
					 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					  /// @return an i2c::ErrorCode
 | 
				
			||||||
  ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop = true);
 | 
					  ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// @brief write an array of bytes to a specific register in the I²C device
 | 
					  /// @brief write an array of bytes to a specific register in the I²C device
 | 
				
			||||||
  /// @param a_register the 16 bits internal address of the register to read from
 | 
					  /// @param a_register the 16 bits internal address of the register to read from
 | 
				
			||||||
  /// @param data pointer to an array to store the bytes
 | 
					  /// @param data pointer to an array to store the bytes
 | 
				
			||||||
  /// @param len length of the buffer = number of bytes to read
 | 
					  /// @param len length of the buffer = number of bytes to read
 | 
				
			||||||
  /// @param stop (true/false): True will send a stop message, releasing the bus after
 | 
					 | 
				
			||||||
  /// transmission. False will send a restart, keeping the connection active.
 | 
					 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					  /// @return an i2c::ErrorCode
 | 
				
			||||||
  ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop = true);
 | 
					  ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Compat APIs
 | 
					  /// Compat APIs
 | 
				
			||||||
@@ -217,7 +219,7 @@ class I2CDevice {
 | 
				
			|||||||
    return read_register(a_register, data, len) == ERROR_OK;
 | 
					    return read_register(a_register, data, len) == ERROR_OK;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool read_bytes_raw(uint8_t *data, uint8_t len) { return read(data, len) == ERROR_OK; }
 | 
					  bool read_bytes_raw(uint8_t *data, uint8_t len) const { return read(data, len) == ERROR_OK; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) {
 | 
					  template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) {
 | 
				
			||||||
    std::array<uint8_t, N> res;
 | 
					    std::array<uint8_t, N> res;
 | 
				
			||||||
@@ -236,9 +238,7 @@ class I2CDevice {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len);
 | 
					  bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool read_byte(uint8_t a_register, uint8_t *data, bool stop = true) {
 | 
					  bool read_byte(uint8_t a_register, uint8_t *data) { return read_register(a_register, data, 1) == ERROR_OK; }
 | 
				
			||||||
    return read_register(a_register, data, 1, stop) == ERROR_OK;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  optional<uint8_t> read_byte(uint8_t a_register) {
 | 
					  optional<uint8_t> read_byte(uint8_t a_register) {
 | 
				
			||||||
    uint8_t data;
 | 
					    uint8_t data;
 | 
				
			||||||
@@ -249,11 +249,11 @@ class I2CDevice {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  bool read_byte_16(uint8_t a_register, uint16_t *data) { return read_bytes_16(a_register, data, 1); }
 | 
					  bool read_byte_16(uint8_t a_register, uint16_t *data) { return read_bytes_16(a_register, data, 1); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len, bool stop = true) {
 | 
					  bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const {
 | 
				
			||||||
    return write_register(a_register, data, len, stop) == ERROR_OK;
 | 
					    return write_register(a_register, data, len) == ERROR_OK;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool write_bytes(uint8_t a_register, const std::vector<uint8_t> &data) {
 | 
					  bool write_bytes(uint8_t a_register, const std::vector<uint8_t> &data) const {
 | 
				
			||||||
    return write_bytes(a_register, data.data(), data.size());
 | 
					    return write_bytes(a_register, data.data(), data.size());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -261,13 +261,42 @@ class I2CDevice {
 | 
				
			|||||||
    return write_bytes(a_register, data.data(), data.size());
 | 
					    return write_bytes(a_register, data.data(), data.size());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len);
 | 
					  bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool write_byte(uint8_t a_register, uint8_t data, bool stop = true) {
 | 
					  bool write_byte(uint8_t a_register, uint8_t data) const { return write_bytes(a_register, &data, 1); }
 | 
				
			||||||
    return write_bytes(a_register, &data, 1, stop);
 | 
					
 | 
				
			||||||
 | 
					  bool write_byte_16(uint8_t a_register, uint16_t data) const { return write_bytes_16(a_register, &data, 1); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Deprecated functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESPDEPRECATED("The stop argument is no longer used. This will be removed from ESPHome 2026.3.0", "2025.9.0")
 | 
				
			||||||
 | 
					  ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop) {
 | 
				
			||||||
 | 
					    return this->read_register(a_register, data, len);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool write_byte_16(uint8_t a_register, uint16_t data) { return write_bytes_16(a_register, &data, 1); }
 | 
					  ESPDEPRECATED("The stop argument is no longer used. This will be removed from ESPHome 2026.3.0", "2025.9.0")
 | 
				
			||||||
 | 
					  ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop) {
 | 
				
			||||||
 | 
					    return this->read_register16(a_register, data, len);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be "
 | 
				
			||||||
 | 
					                "removed from ESPHome 2026.3.0",
 | 
				
			||||||
 | 
					                "2025.9.0")
 | 
				
			||||||
 | 
					  ErrorCode write(const uint8_t *data, size_t len, bool stop) const { return this->write(data, len); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be "
 | 
				
			||||||
 | 
					                "removed from ESPHome 2026.3.0",
 | 
				
			||||||
 | 
					                "2025.9.0")
 | 
				
			||||||
 | 
					  ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop) const {
 | 
				
			||||||
 | 
					    return this->write_register(a_register, data, len);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be "
 | 
				
			||||||
 | 
					                "removed from ESPHome 2026.3.0",
 | 
				
			||||||
 | 
					                "2025.9.0")
 | 
				
			||||||
 | 
					  ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop) const {
 | 
				
			||||||
 | 
					    return this->write_register16(a_register, data, len);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  uint8_t address_{0x00};  ///< store the address of the device on the bus
 | 
					  uint8_t address_{0x00};  ///< store the address of the device on the bus
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
#include <cstddef>
 | 
					#include <cstddef>
 | 
				
			||||||
#include <cstdint>
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					#include <cstring>
 | 
				
			||||||
#include <utility>
 | 
					#include <utility>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace i2c {
 | 
					namespace i2c {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,71 +42,66 @@ struct WriteBuffer {
 | 
				
			|||||||
/// note https://www.nxp.com/docs/en/application-note/AN10216.pdf
 | 
					/// note https://www.nxp.com/docs/en/application-note/AN10216.pdf
 | 
				
			||||||
class I2CBus {
 | 
					class I2CBus {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  /// @brief Creates a ReadBuffer and calls the virtual readv() method to read bytes into this buffer
 | 
					  virtual ~I2CBus() = default;
 | 
				
			||||||
  /// @param address address of the I²C component on the i2c bus
 | 
					 | 
				
			||||||
  /// @param buffer pointer to an array of bytes that will be used to store the data received
 | 
					 | 
				
			||||||
  /// @param len length of the buffer = number of bytes to read
 | 
					 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					 | 
				
			||||||
  virtual ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) {
 | 
					 | 
				
			||||||
    ReadBuffer buf;
 | 
					 | 
				
			||||||
    buf.data = buffer;
 | 
					 | 
				
			||||||
    buf.len = len;
 | 
					 | 
				
			||||||
    return readv(address, &buf, 1);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// @brief This virtual method reads bytes from an I2CBus into an array of ReadBuffer.
 | 
					  /// @brief This virtual method writes bytes to an I2CBus from an array,
 | 
				
			||||||
  /// @param address address of the I²C component on the i2c bus
 | 
					  /// then reads bytes into an array of ReadBuffer.
 | 
				
			||||||
  /// @param buffers pointer to an array of ReadBuffer
 | 
					  /// @param address address of the I²C device on the i2c bus
 | 
				
			||||||
  /// @param count number of ReadBuffer to read
 | 
					  /// @param write_buffer pointer to data
 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					  /// @param write_count number of bytes to write
 | 
				
			||||||
  /// @details This is a pure virtual method that must be implemented in a subclass.
 | 
					  /// @param read_buffer pointer to an array to receive data
 | 
				
			||||||
  virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t count) = 0;
 | 
					  /// @param read_count number of bytes to read
 | 
				
			||||||
 | 
					 | 
				
			||||||
  virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len) {
 | 
					 | 
				
			||||||
    return write(address, buffer, len, true);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// @brief Creates a WriteBuffer and calls the writev() method to send the bytes from this buffer
 | 
					 | 
				
			||||||
  /// @param address address of the I²C component on the i2c bus
 | 
					 | 
				
			||||||
  /// @param buffer pointer to an array of bytes that contains the data to be sent
 | 
					 | 
				
			||||||
  /// @param len length of the buffer = number of bytes to write
 | 
					 | 
				
			||||||
  /// @param stop true or false: True will send a stop message, releasing the bus after
 | 
					 | 
				
			||||||
  /// transmission. False will send a restart, keeping the connection active.
 | 
					 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					 | 
				
			||||||
  virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop) {
 | 
					 | 
				
			||||||
    WriteBuffer buf;
 | 
					 | 
				
			||||||
    buf.data = buffer;
 | 
					 | 
				
			||||||
    buf.len = len;
 | 
					 | 
				
			||||||
    return writev(address, &buf, 1, stop);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) {
 | 
					 | 
				
			||||||
    return writev(address, buffers, cnt, true);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// @brief This virtual method writes bytes to an I2CBus from an array of WriteBuffer.
 | 
					 | 
				
			||||||
  /// @param address address of the I²C component on the i2c bus
 | 
					 | 
				
			||||||
  /// @param buffers pointer to an array of WriteBuffer
 | 
					 | 
				
			||||||
  /// @param count number of WriteBuffer to write
 | 
					 | 
				
			||||||
  /// @param stop true or false: True will send a stop message, releasing the bus after
 | 
					 | 
				
			||||||
  /// transmission. False will send a restart, keeping the connection active.
 | 
					  /// transmission. False will send a restart, keeping the connection active.
 | 
				
			||||||
  /// @return an i2c::ErrorCode
 | 
					  /// @return an i2c::ErrorCode
 | 
				
			||||||
  /// @details This is a pure virtual method that must be implemented in the subclass.
 | 
					  /// @details This is a pure virtual method that must be implemented in the subclass.
 | 
				
			||||||
  virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t count, bool stop) = 0;
 | 
					  virtual ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer,
 | 
				
			||||||
 | 
					                                size_t read_count) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Legacy functions for compatibility
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) {
 | 
				
			||||||
 | 
					    return this->write_readv(address, nullptr, 0, buffer, len);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop = true) {
 | 
				
			||||||
 | 
					    return this->write_readv(address, buffer, len, nullptr, 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESPDEPRECATED("This method is deprecated and will be removed in ESPHome 2026.3.0. Use write_readv() instead.",
 | 
				
			||||||
 | 
					                "2025.9.0")
 | 
				
			||||||
 | 
					  ErrorCode readv(uint8_t address, ReadBuffer *read_buffers, size_t count) {
 | 
				
			||||||
 | 
					    size_t total_len = 0;
 | 
				
			||||||
 | 
					    for (size_t i = 0; i != count; i++) {
 | 
				
			||||||
 | 
					      total_len += read_buffers[i].len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::vector<uint8_t> buffer(total_len);
 | 
				
			||||||
 | 
					    auto err = this->write_readv(address, nullptr, 0, buffer.data(), total_len);
 | 
				
			||||||
 | 
					    if (err != ERROR_OK)
 | 
				
			||||||
 | 
					      return err;
 | 
				
			||||||
 | 
					    size_t pos = 0;
 | 
				
			||||||
 | 
					    for (size_t i = 0; i != count; i++) {
 | 
				
			||||||
 | 
					      if (read_buffers[i].len != 0) {
 | 
				
			||||||
 | 
					        std::memcpy(read_buffers[i].data, buffer.data() + pos, read_buffers[i].len);
 | 
				
			||||||
 | 
					        pos += read_buffers[i].len;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ERROR_OK;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESPDEPRECATED("This method is deprecated and will be removed in ESPHome 2026.3.0. Use write_readv() instead.",
 | 
				
			||||||
 | 
					                "2025.9.0")
 | 
				
			||||||
 | 
					  ErrorCode writev(uint8_t address, const WriteBuffer *write_buffers, size_t count, bool stop = true) {
 | 
				
			||||||
 | 
					    std::vector<uint8_t> buffer{};
 | 
				
			||||||
 | 
					    for (size_t i = 0; i != count; i++) {
 | 
				
			||||||
 | 
					      buffer.insert(buffer.end(), write_buffers[i].data, write_buffers[i].data + write_buffers[i].len);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return this->write_readv(address, buffer.data(), buffer.size(), nullptr, 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  /// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair
 | 
					  /// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair
 | 
				
			||||||
  /// that contains the address and the corresponding bool presence flag.
 | 
					  /// that contains the address and the corresponding bool presence flag.
 | 
				
			||||||
  virtual void i2c_scan() {
 | 
					  void i2c_scan_();
 | 
				
			||||||
    for (uint8_t address = 8; address < 120; address++) {
 | 
					 | 
				
			||||||
      auto err = writev(address, nullptr, 0);
 | 
					 | 
				
			||||||
      if (err == ERROR_OK) {
 | 
					 | 
				
			||||||
        scan_results_.emplace_back(address, true);
 | 
					 | 
				
			||||||
      } else if (err == ERROR_UNKNOWN) {
 | 
					 | 
				
			||||||
        scan_results_.emplace_back(address, false);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  std::vector<std::pair<uint8_t, bool>> scan_results_;  ///< array containing scan results
 | 
					  std::vector<std::pair<uint8_t, bool>> scan_results_;  ///< array containing scan results
 | 
				
			||||||
  bool scan_{false};                                    ///< Should we scan ? Can be set in the yaml
 | 
					  bool scan_{false};                                    ///< Should we scan ? Can be set in the yaml
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ void ArduinoI2CBus::setup() {
 | 
				
			|||||||
  this->initialized_ = true;
 | 
					  this->initialized_ = true;
 | 
				
			||||||
  if (this->scan_) {
 | 
					  if (this->scan_) {
 | 
				
			||||||
    ESP_LOGV(TAG, "Scanning bus for active devices");
 | 
					    ESP_LOGV(TAG, "Scanning bus for active devices");
 | 
				
			||||||
    this->i2c_scan();
 | 
					    this->i2c_scan_();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -111,88 +111,37 @@ void ArduinoI2CBus::dump_config() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
 | 
					ErrorCode ArduinoI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count,
 | 
				
			||||||
 | 
					                                     uint8_t *read_buffer, size_t read_count) {
 | 
				
			||||||
#if defined(USE_ESP8266)
 | 
					#if defined(USE_ESP8266)
 | 
				
			||||||
  this->set_pins_and_clock_();  // reconfigure Wire global state in case there are multiple instances
 | 
					  this->set_pins_and_clock_();  // reconfigure Wire global state in case there are multiple instances
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // logging is only enabled with vv level, if warnings are shown the caller
 | 
					 | 
				
			||||||
  // should log them
 | 
					 | 
				
			||||||
  if (!initialized_) {
 | 
					  if (!initialized_) {
 | 
				
			||||||
    ESP_LOGVV(TAG, "i2c bus not initialized!");
 | 
					    ESP_LOGD(TAG, "i2c bus not initialized!");
 | 
				
			||||||
    return ERROR_NOT_INITIALIZED;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  size_t to_request = 0;
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++)
 | 
					 | 
				
			||||||
    to_request += buffers[i].len;
 | 
					 | 
				
			||||||
  size_t ret = wire_->requestFrom(address, to_request, true);
 | 
					 | 
				
			||||||
  if (ret != to_request) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", to_request, address, ret);
 | 
					 | 
				
			||||||
    return ERROR_TIMEOUT;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					 | 
				
			||||||
    for (size_t j = 0; j < buf.len; j++)
 | 
					 | 
				
			||||||
      buf.data[j] = wire_->read();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
 | 
					 | 
				
			||||||
  char debug_buf[4];
 | 
					 | 
				
			||||||
  std::string debug_hex;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					 | 
				
			||||||
    for (size_t j = 0; j < buf.len; j++) {
 | 
					 | 
				
			||||||
      snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]);
 | 
					 | 
				
			||||||
      debug_hex += debug_buf;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  ESP_LOGVV(TAG, "0x%02X RX %s", address, debug_hex.c_str());
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return ERROR_OK;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) {
 | 
					 | 
				
			||||||
#if defined(USE_ESP8266)
 | 
					 | 
				
			||||||
  this->set_pins_and_clock_();  // reconfigure Wire global state in case there are multiple instances
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // logging is only enabled with vv level, if warnings are shown the caller
 | 
					 | 
				
			||||||
  // should log them
 | 
					 | 
				
			||||||
  if (!initialized_) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "i2c bus not initialized!");
 | 
					 | 
				
			||||||
    return ERROR_NOT_INITIALIZED;
 | 
					    return ERROR_NOT_INITIALIZED;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
 | 
					  ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty(write_buffer, write_count).c_str());
 | 
				
			||||||
  char debug_buf[4];
 | 
					 | 
				
			||||||
  std::string debug_hex;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					  uint8_t status = 0;
 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					  if (write_count != 0 || read_count == 0) {
 | 
				
			||||||
    for (size_t j = 0; j < buf.len; j++) {
 | 
					    wire_->beginTransmission(address);
 | 
				
			||||||
      snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]);
 | 
					    size_t ret = wire_->write(write_buffer, write_count);
 | 
				
			||||||
      debug_hex += debug_buf;
 | 
					    if (ret != write_count) {
 | 
				
			||||||
    }
 | 
					      ESP_LOGV(TAG, "TX failed");
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  ESP_LOGVV(TAG, "0x%02X TX %s", address, debug_hex.c_str());
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  wire_->beginTransmission(address);
 | 
					 | 
				
			||||||
  size_t written = 0;
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					 | 
				
			||||||
    if (buf.len == 0)
 | 
					 | 
				
			||||||
      continue;
 | 
					 | 
				
			||||||
    size_t ret = wire_->write(buf.data, buf.len);
 | 
					 | 
				
			||||||
    written += ret;
 | 
					 | 
				
			||||||
    if (ret != buf.len) {
 | 
					 | 
				
			||||||
      ESP_LOGVV(TAG, "TX failed at %u", written);
 | 
					 | 
				
			||||||
      return ERROR_UNKNOWN;
 | 
					      return ERROR_UNKNOWN;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    status = wire_->endTransmission(read_count == 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (status == 0 && read_count != 0) {
 | 
				
			||||||
 | 
					    size_t ret2 = wire_->requestFrom(address, read_count, true);
 | 
				
			||||||
 | 
					    if (ret2 != read_count) {
 | 
				
			||||||
 | 
					      ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", read_count, address, ret2);
 | 
				
			||||||
 | 
					      return ERROR_TIMEOUT;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (size_t j = 0; j != read_count; j++)
 | 
				
			||||||
 | 
					      read_buffer[j] = wire_->read();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  uint8_t status = wire_->endTransmission(stop);
 | 
					 | 
				
			||||||
  switch (status) {
 | 
					  switch (status) {
 | 
				
			||||||
    case 0:
 | 
					    case 0:
 | 
				
			||||||
      return ERROR_OK;
 | 
					      return ERROR_OK;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,8 +19,8 @@ class ArduinoI2CBus : public InternalI2CBus, public Component {
 | 
				
			|||||||
 public:
 | 
					 public:
 | 
				
			||||||
  void setup() override;
 | 
					  void setup() override;
 | 
				
			||||||
  void dump_config() override;
 | 
					  void dump_config() override;
 | 
				
			||||||
  ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override;
 | 
					  ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer,
 | 
				
			||||||
  ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override;
 | 
					                        size_t read_count) override;
 | 
				
			||||||
  float get_setup_priority() const override { return setup_priority::BUS; }
 | 
					  float get_setup_priority() const override { return setup_priority::BUS; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void set_scan(bool scan) { scan_ = scan; }
 | 
					  void set_scan(bool scan) { scan_ = scan; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
#ifdef USE_ESP_IDF
 | 
					#ifdef USE_ESP_IDF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "i2c_bus_esp_idf.h"
 | 
					#include "i2c_bus_esp_idf.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <driver/gpio.h>
 | 
					#include <driver/gpio.h>
 | 
				
			||||||
#include <cinttypes>
 | 
					#include <cinttypes>
 | 
				
			||||||
#include <cstring>
 | 
					#include <cstring>
 | 
				
			||||||
@@ -9,10 +10,6 @@
 | 
				
			|||||||
#include "esphome/core/helpers.h"
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
 | 
					 | 
				
			||||||
#define SOC_HP_I2C_NUM SOC_I2C_NUM
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace i2c {
 | 
					namespace i2c {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,7 +31,6 @@ void IDFI2CBus::setup() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  this->recover_();
 | 
					  this->recover_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
 | 
					 | 
				
			||||||
  next_port = (i2c_port_t) (next_port + 1);
 | 
					  next_port = (i2c_port_t) (next_port + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  i2c_master_bus_config_t bus_conf{};
 | 
					  i2c_master_bus_config_t bus_conf{};
 | 
				
			||||||
@@ -77,56 +73,8 @@ void IDFI2CBus::setup() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (this->scan_) {
 | 
					  if (this->scan_) {
 | 
				
			||||||
    ESP_LOGV(TAG, "Scanning for devices");
 | 
					    ESP_LOGV(TAG, "Scanning for devices");
 | 
				
			||||||
    this->i2c_scan();
 | 
					    this->i2c_scan_();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#if SOC_HP_I2C_NUM > 1
 | 
					 | 
				
			||||||
  next_port = (next_port == I2C_NUM_0) ? I2C_NUM_1 : I2C_NUM_MAX;
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
  next_port = I2C_NUM_MAX;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  i2c_config_t conf{};
 | 
					 | 
				
			||||||
  memset(&conf, 0, sizeof(conf));
 | 
					 | 
				
			||||||
  conf.mode = I2C_MODE_MASTER;
 | 
					 | 
				
			||||||
  conf.sda_io_num = sda_pin_;
 | 
					 | 
				
			||||||
  conf.sda_pullup_en = sda_pullup_enabled_;
 | 
					 | 
				
			||||||
  conf.scl_io_num = scl_pin_;
 | 
					 | 
				
			||||||
  conf.scl_pullup_en = scl_pullup_enabled_;
 | 
					 | 
				
			||||||
  conf.master.clk_speed = frequency_;
 | 
					 | 
				
			||||||
#ifdef USE_ESP32_VARIANT_ESP32S2
 | 
					 | 
				
			||||||
  // workaround for https://github.com/esphome/issues/issues/6718
 | 
					 | 
				
			||||||
  conf.clk_flags = I2C_SCLK_SRC_FLAG_AWARE_DFS;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
  esp_err_t err = i2c_param_config(port_, &conf);
 | 
					 | 
				
			||||||
  if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGW(TAG, "i2c_param_config failed: %s", esp_err_to_name(err));
 | 
					 | 
				
			||||||
    this->mark_failed();
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (timeout_ > 0) {
 | 
					 | 
				
			||||||
    err = i2c_set_timeout(port_, timeout_ * 80);  // unit: APB 80MHz clock cycle
 | 
					 | 
				
			||||||
    if (err != ESP_OK) {
 | 
					 | 
				
			||||||
      ESP_LOGW(TAG, "i2c_set_timeout failed: %s", esp_err_to_name(err));
 | 
					 | 
				
			||||||
      this->mark_failed();
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      ESP_LOGV(TAG, "i2c_timeout set to %" PRIu32 " ticks (%" PRIu32 " us)", timeout_ * 80, timeout_);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, 0);
 | 
					 | 
				
			||||||
  if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err));
 | 
					 | 
				
			||||||
    this->mark_failed();
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  initialized_ = true;
 | 
					 | 
				
			||||||
  if (this->scan_) {
 | 
					 | 
				
			||||||
    ESP_LOGV(TAG, "Scanning bus for active devices");
 | 
					 | 
				
			||||||
    this->i2c_scan();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void IDFI2CBus::dump_config() {
 | 
					void IDFI2CBus::dump_config() {
 | 
				
			||||||
@@ -166,267 +114,73 @@ void IDFI2CBus::dump_config() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
 | 
					ErrorCode IDFI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer,
 | 
				
			||||||
void IDFI2CBus::i2c_scan() {
 | 
					                                 size_t read_count) {
 | 
				
			||||||
  for (uint8_t address = 8; address < 120; address++) {
 | 
					  // logging is only enabled with v level, if warnings are shown the caller
 | 
				
			||||||
    auto err = i2c_master_probe(this->bus_, address, 20);
 | 
					 | 
				
			||||||
    if (err == ESP_OK) {
 | 
					 | 
				
			||||||
      this->scan_results_.emplace_back(address, true);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
 | 
					 | 
				
			||||||
  // logging is only enabled with vv level, if warnings are shown the caller
 | 
					 | 
				
			||||||
  // should log them
 | 
					  // should log them
 | 
				
			||||||
  if (!initialized_) {
 | 
					  if (!initialized_) {
 | 
				
			||||||
    ESP_LOGVV(TAG, "i2c bus not initialized!");
 | 
					    ESP_LOGW(TAG, "i2c bus not initialized!");
 | 
				
			||||||
    return ERROR_NOT_INITIALIZED;
 | 
					    return ERROR_NOT_INITIALIZED;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
 | 
					  i2c_operation_job_t jobs[8]{};
 | 
				
			||||||
  i2c_operation_job_t jobs[cnt + 4];
 | 
					  size_t num_jobs = 0;
 | 
				
			||||||
  uint8_t read = (address << 1) | I2C_MASTER_READ;
 | 
					  uint8_t write_addr = (address << 1) | I2C_MASTER_WRITE;
 | 
				
			||||||
  size_t last = 0, num = 0;
 | 
					  uint8_t read_addr = (address << 1) | I2C_MASTER_READ;
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Writing %zu bytes, reading %zu bytes", write_count, read_count);
 | 
				
			||||||
  jobs[num].command = I2C_MASTER_CMD_START;
 | 
					  if (read_count == 0 && write_count == 0) {
 | 
				
			||||||
  num++;
 | 
					    // basically just a bus probe. Send a start, address and stop
 | 
				
			||||||
 | 
					    ESP_LOGV(TAG, "0x%02X BUS PROBE", address);
 | 
				
			||||||
  jobs[num].command = I2C_MASTER_CMD_WRITE;
 | 
					    jobs[num_jobs++].command = I2C_MASTER_CMD_START;
 | 
				
			||||||
  jobs[num].write.ack_check = true;
 | 
					    jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
 | 
				
			||||||
  jobs[num].write.data = &read;
 | 
					    jobs[num_jobs].write.ack_check = true;
 | 
				
			||||||
  jobs[num].write.total_bytes = 1;
 | 
					    jobs[num_jobs].write.data = &write_addr;
 | 
				
			||||||
  num++;
 | 
					    jobs[num_jobs++].write.total_bytes = 1;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
  // find the last valid index
 | 
					    if (write_count != 0) {
 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					      ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty(write_buffer, write_count).c_str());
 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					      jobs[num_jobs++].command = I2C_MASTER_CMD_START;
 | 
				
			||||||
    if (buf.len == 0) {
 | 
					      jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
 | 
				
			||||||
      continue;
 | 
					      jobs[num_jobs].write.ack_check = true;
 | 
				
			||||||
 | 
					      jobs[num_jobs].write.data = &write_addr;
 | 
				
			||||||
 | 
					      jobs[num_jobs++].write.total_bytes = 1;
 | 
				
			||||||
 | 
					      jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
 | 
				
			||||||
 | 
					      jobs[num_jobs].write.ack_check = true;
 | 
				
			||||||
 | 
					      jobs[num_jobs].write.data = (uint8_t *) write_buffer;
 | 
				
			||||||
 | 
					      jobs[num_jobs++].write.total_bytes = write_count;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    last = i;
 | 
					    if (read_count != 0) {
 | 
				
			||||||
  }
 | 
					      ESP_LOGV(TAG, "0x%02X RX bytes %zu", address, read_count);
 | 
				
			||||||
 | 
					      jobs[num_jobs++].command = I2C_MASTER_CMD_START;
 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					      jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					      jobs[num_jobs].write.ack_check = true;
 | 
				
			||||||
    if (buf.len == 0) {
 | 
					      jobs[num_jobs].write.data = &read_addr;
 | 
				
			||||||
      continue;
 | 
					      jobs[num_jobs++].write.total_bytes = 1;
 | 
				
			||||||
    }
 | 
					      if (read_count > 1) {
 | 
				
			||||||
    if (i == last) {
 | 
					        jobs[num_jobs].command = I2C_MASTER_CMD_READ;
 | 
				
			||||||
      // the last byte read before stop should always be a nack,
 | 
					        jobs[num_jobs].read.ack_value = I2C_ACK_VAL;
 | 
				
			||||||
      // split the last read if len is larger than 1
 | 
					        jobs[num_jobs].read.data = read_buffer;
 | 
				
			||||||
      if (buf.len > 1) {
 | 
					        jobs[num_jobs++].read.total_bytes = read_count - 1;
 | 
				
			||||||
        jobs[num].command = I2C_MASTER_CMD_READ;
 | 
					 | 
				
			||||||
        jobs[num].read.ack_value = I2C_ACK_VAL;
 | 
					 | 
				
			||||||
        jobs[num].read.data = (uint8_t *) buf.data;
 | 
					 | 
				
			||||||
        jobs[num].read.total_bytes = buf.len - 1;
 | 
					 | 
				
			||||||
        num++;
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      jobs[num].command = I2C_MASTER_CMD_READ;
 | 
					      jobs[num_jobs].command = I2C_MASTER_CMD_READ;
 | 
				
			||||||
      jobs[num].read.ack_value = I2C_NACK_VAL;
 | 
					      jobs[num_jobs].read.ack_value = I2C_NACK_VAL;
 | 
				
			||||||
      jobs[num].read.data = (uint8_t *) buf.data + buf.len - 1;
 | 
					      jobs[num_jobs].read.data = read_buffer + read_count - 1;
 | 
				
			||||||
      jobs[num].read.total_bytes = 1;
 | 
					      jobs[num_jobs++].read.total_bytes = 1;
 | 
				
			||||||
      num++;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      jobs[num].command = I2C_MASTER_CMD_READ;
 | 
					 | 
				
			||||||
      jobs[num].read.ack_value = I2C_ACK_VAL;
 | 
					 | 
				
			||||||
      jobs[num].read.data = (uint8_t *) buf.data;
 | 
					 | 
				
			||||||
      jobs[num].read.total_bytes = buf.len;
 | 
					 | 
				
			||||||
      num++;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  jobs[num_jobs++].command = I2C_MASTER_CMD_STOP;
 | 
				
			||||||
  jobs[num].command = I2C_MASTER_CMD_STOP;
 | 
					  ESP_LOGV(TAG, "Sending %zu jobs", num_jobs);
 | 
				
			||||||
  num++;
 | 
					  esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num_jobs, 20);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num, 20);
 | 
					 | 
				
			||||||
  if (err == ESP_ERR_INVALID_STATE) {
 | 
					  if (err == ESP_ERR_INVALID_STATE) {
 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X failed: not acked", address);
 | 
					    ESP_LOGV(TAG, "TX to %02X failed: not acked", address);
 | 
				
			||||||
    return ERROR_NOT_ACKNOWLEDGED;
 | 
					    return ERROR_NOT_ACKNOWLEDGED;
 | 
				
			||||||
  } else if (err == ESP_ERR_TIMEOUT) {
 | 
					  } else if (err == ESP_ERR_TIMEOUT) {
 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X failed: timeout", address);
 | 
					    ESP_LOGV(TAG, "TX to %02X failed: timeout", address);
 | 
				
			||||||
    return ERROR_TIMEOUT;
 | 
					    return ERROR_TIMEOUT;
 | 
				
			||||||
  } else if (err != ESP_OK) {
 | 
					  } else if (err != ESP_OK) {
 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X failed: %s", address, esp_err_to_name(err));
 | 
					    ESP_LOGV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err));
 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					    return ERROR_UNKNOWN;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
 | 
					 | 
				
			||||||
  esp_err_t err = i2c_master_start(cmd);
 | 
					 | 
				
			||||||
  if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X master start failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
    i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true);
 | 
					 | 
				
			||||||
  if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X address write failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
    i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					 | 
				
			||||||
    if (buf.len == 0)
 | 
					 | 
				
			||||||
      continue;
 | 
					 | 
				
			||||||
    err = i2c_master_read(cmd, buf.data, buf.len, i == cnt - 1 ? I2C_MASTER_LAST_NACK : I2C_MASTER_ACK);
 | 
					 | 
				
			||||||
    if (err != ESP_OK) {
 | 
					 | 
				
			||||||
      ESP_LOGVV(TAG, "RX from %02X data read failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
      i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
      return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  err = i2c_master_stop(cmd);
 | 
					 | 
				
			||||||
  if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X stop failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
    i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS);
 | 
					 | 
				
			||||||
  // i2c_master_cmd_begin() will block for a whole second if no ack:
 | 
					 | 
				
			||||||
  // https://github.com/espressif/esp-idf/issues/4999
 | 
					 | 
				
			||||||
  i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
  if (err == ESP_FAIL) {
 | 
					 | 
				
			||||||
    // transfer not acked
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X failed: not acked", address);
 | 
					 | 
				
			||||||
    return ERROR_NOT_ACKNOWLEDGED;
 | 
					 | 
				
			||||||
  } else if (err == ESP_ERR_TIMEOUT) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X failed: timeout", address);
 | 
					 | 
				
			||||||
    return ERROR_TIMEOUT;
 | 
					 | 
				
			||||||
  } else if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "RX from %02X failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
 | 
					 | 
				
			||||||
  char debug_buf[4];
 | 
					 | 
				
			||||||
  std::string debug_hex;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					 | 
				
			||||||
    for (size_t j = 0; j < buf.len; j++) {
 | 
					 | 
				
			||||||
      snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]);
 | 
					 | 
				
			||||||
      debug_hex += debug_buf;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  ESP_LOGVV(TAG, "0x%02X RX %s", address, debug_hex.c_str());
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return ERROR_OK;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) {
 | 
					 | 
				
			||||||
  // logging is only enabled with vv level, if warnings are shown the caller
 | 
					 | 
				
			||||||
  // should log them
 | 
					 | 
				
			||||||
  if (!initialized_) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "i2c bus not initialized!");
 | 
					 | 
				
			||||||
    return ERROR_NOT_INITIALIZED;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
 | 
					 | 
				
			||||||
  char debug_buf[4];
 | 
					 | 
				
			||||||
  std::string debug_hex;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					 | 
				
			||||||
    for (size_t j = 0; j < buf.len; j++) {
 | 
					 | 
				
			||||||
      snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]);
 | 
					 | 
				
			||||||
      debug_hex += debug_buf;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  ESP_LOGVV(TAG, "0x%02X TX %s", address, debug_hex.c_str());
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
 | 
					 | 
				
			||||||
  i2c_operation_job_t jobs[cnt + 3];
 | 
					 | 
				
			||||||
  uint8_t write = (address << 1) | I2C_MASTER_WRITE;
 | 
					 | 
				
			||||||
  size_t num = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  jobs[num].command = I2C_MASTER_CMD_START;
 | 
					 | 
				
			||||||
  num++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  jobs[num].command = I2C_MASTER_CMD_WRITE;
 | 
					 | 
				
			||||||
  jobs[num].write.ack_check = true;
 | 
					 | 
				
			||||||
  jobs[num].write.data = &write;
 | 
					 | 
				
			||||||
  jobs[num].write.total_bytes = 1;
 | 
					 | 
				
			||||||
  num++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					 | 
				
			||||||
    if (buf.len == 0) {
 | 
					 | 
				
			||||||
      continue;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    jobs[num].command = I2C_MASTER_CMD_WRITE;
 | 
					 | 
				
			||||||
    jobs[num].write.ack_check = true;
 | 
					 | 
				
			||||||
    jobs[num].write.data = (uint8_t *) buf.data;
 | 
					 | 
				
			||||||
    jobs[num].write.total_bytes = buf.len;
 | 
					 | 
				
			||||||
    num++;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (stop) {
 | 
					 | 
				
			||||||
    jobs[num].command = I2C_MASTER_CMD_STOP;
 | 
					 | 
				
			||||||
    num++;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num, 20);
 | 
					 | 
				
			||||||
  if (err == ESP_ERR_INVALID_STATE) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "TX to %02X failed: not acked", address);
 | 
					 | 
				
			||||||
    return ERROR_NOT_ACKNOWLEDGED;
 | 
					 | 
				
			||||||
  } else if (err == ESP_ERR_TIMEOUT) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "TX to %02X failed: timeout", address);
 | 
					 | 
				
			||||||
    return ERROR_TIMEOUT;
 | 
					 | 
				
			||||||
  } else if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
 | 
					 | 
				
			||||||
  esp_err_t err = i2c_master_start(cmd);
 | 
					 | 
				
			||||||
  if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "TX to %02X master start failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
    i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
 | 
					 | 
				
			||||||
  if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "TX to %02X address write failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
    i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < cnt; i++) {
 | 
					 | 
				
			||||||
    const auto &buf = buffers[i];
 | 
					 | 
				
			||||||
    if (buf.len == 0)
 | 
					 | 
				
			||||||
      continue;
 | 
					 | 
				
			||||||
    err = i2c_master_write(cmd, buf.data, buf.len, true);
 | 
					 | 
				
			||||||
    if (err != ESP_OK) {
 | 
					 | 
				
			||||||
      ESP_LOGVV(TAG, "TX to %02X data write failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
      i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
      return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (stop) {
 | 
					 | 
				
			||||||
    err = i2c_master_stop(cmd);
 | 
					 | 
				
			||||||
    if (err != ESP_OK) {
 | 
					 | 
				
			||||||
      ESP_LOGVV(TAG, "TX to %02X master stop failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
      i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
      return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS);
 | 
					 | 
				
			||||||
  i2c_cmd_link_delete(cmd);
 | 
					 | 
				
			||||||
  if (err == ESP_FAIL) {
 | 
					 | 
				
			||||||
    // transfer not acked
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "TX to %02X failed: not acked", address);
 | 
					 | 
				
			||||||
    return ERROR_NOT_ACKNOWLEDGED;
 | 
					 | 
				
			||||||
  } else if (err == ESP_ERR_TIMEOUT) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "TX to %02X failed: timeout", address);
 | 
					 | 
				
			||||||
    return ERROR_TIMEOUT;
 | 
					 | 
				
			||||||
  } else if (err != ESP_OK) {
 | 
					 | 
				
			||||||
    ESP_LOGVV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err));
 | 
					 | 
				
			||||||
    return ERROR_UNKNOWN;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
  return ERROR_OK;
 | 
					  return ERROR_OK;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -436,8 +190,8 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, b
 | 
				
			|||||||
void IDFI2CBus::recover_() {
 | 
					void IDFI2CBus::recover_() {
 | 
				
			||||||
  ESP_LOGI(TAG, "Performing bus recovery");
 | 
					  ESP_LOGI(TAG, "Performing bus recovery");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const gpio_num_t scl_pin = static_cast<gpio_num_t>(scl_pin_);
 | 
					  const auto scl_pin = static_cast<gpio_num_t>(scl_pin_);
 | 
				
			||||||
  const gpio_num_t sda_pin = static_cast<gpio_num_t>(sda_pin_);
 | 
					  const auto sda_pin = static_cast<gpio_num_t>(sda_pin_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // For the upcoming operations, target for a 60kHz toggle frequency.
 | 
					  // For the upcoming operations, target for a 60kHz toggle frequency.
 | 
				
			||||||
  // 1000kHz is the maximum frequency for I2C running in standard-mode,
 | 
					  // 1000kHz is the maximum frequency for I2C running in standard-mode,
 | 
				
			||||||
@@ -545,5 +299,4 @@ void IDFI2CBus::recover_() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}  // namespace i2c
 | 
					}  // namespace i2c
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif  // USE_ESP_IDF
 | 
					#endif  // USE_ESP_IDF
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,14 +2,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#ifdef USE_ESP_IDF
 | 
					#ifdef USE_ESP_IDF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "esp_idf_version.h"
 | 
					 | 
				
			||||||
#include "esphome/core/component.h"
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
#include "i2c_bus.h"
 | 
					#include "i2c_bus.h"
 | 
				
			||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
 | 
					 | 
				
			||||||
#include <driver/i2c_master.h>
 | 
					#include <driver/i2c_master.h>
 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#include <driver/i2c.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace i2c {
 | 
					namespace i2c {
 | 
				
			||||||
@@ -24,36 +19,33 @@ class IDFI2CBus : public InternalI2CBus, public Component {
 | 
				
			|||||||
 public:
 | 
					 public:
 | 
				
			||||||
  void setup() override;
 | 
					  void setup() override;
 | 
				
			||||||
  void dump_config() override;
 | 
					  void dump_config() override;
 | 
				
			||||||
  ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override;
 | 
					  ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer,
 | 
				
			||||||
  ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override;
 | 
					                        size_t read_count) override;
 | 
				
			||||||
  float get_setup_priority() const override { return setup_priority::BUS; }
 | 
					  float get_setup_priority() const override { return setup_priority::BUS; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void set_scan(bool scan) { scan_ = scan; }
 | 
					  void set_scan(bool scan) { this->scan_ = scan; }
 | 
				
			||||||
  void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; }
 | 
					  void set_sda_pin(uint8_t sda_pin) { this->sda_pin_ = sda_pin; }
 | 
				
			||||||
  void set_sda_pullup_enabled(bool sda_pullup_enabled) { sda_pullup_enabled_ = sda_pullup_enabled; }
 | 
					  void set_sda_pullup_enabled(bool sda_pullup_enabled) { this->sda_pullup_enabled_ = sda_pullup_enabled; }
 | 
				
			||||||
  void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
 | 
					  void set_scl_pin(uint8_t scl_pin) { this->scl_pin_ = scl_pin; }
 | 
				
			||||||
  void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; }
 | 
					  void set_scl_pullup_enabled(bool scl_pullup_enabled) { this->scl_pullup_enabled_ = scl_pullup_enabled; }
 | 
				
			||||||
  void set_frequency(uint32_t frequency) { frequency_ = frequency; }
 | 
					  void set_frequency(uint32_t frequency) { this->frequency_ = frequency; }
 | 
				
			||||||
  void set_timeout(uint32_t timeout) { timeout_ = timeout; }
 | 
					  void set_timeout(uint32_t timeout) { this->timeout_ = timeout; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int get_port() const override { return static_cast<int>(this->port_); }
 | 
					  int get_port() const override { return this->port_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
  void recover_();
 | 
					  void recover_();
 | 
				
			||||||
  RecoveryCode recovery_result_;
 | 
					  RecoveryCode recovery_result_{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
 | 
					  i2c_master_dev_handle_t dev_{};
 | 
				
			||||||
  i2c_master_dev_handle_t dev_;
 | 
					  i2c_master_bus_handle_t bus_{};
 | 
				
			||||||
  i2c_master_bus_handle_t bus_;
 | 
					  i2c_port_t port_{};
 | 
				
			||||||
  void i2c_scan() override;
 | 
					  uint8_t sda_pin_{};
 | 
				
			||||||
#endif
 | 
					  bool sda_pullup_enabled_{};
 | 
				
			||||||
  i2c_port_t port_;
 | 
					  uint8_t scl_pin_{};
 | 
				
			||||||
  uint8_t sda_pin_;
 | 
					  bool scl_pullup_enabled_{};
 | 
				
			||||||
  bool sda_pullup_enabled_;
 | 
					  uint32_t frequency_{};
 | 
				
			||||||
  uint8_t scl_pin_;
 | 
					 | 
				
			||||||
  bool scl_pullup_enabled_;
 | 
					 | 
				
			||||||
  uint32_t frequency_;
 | 
					 | 
				
			||||||
  uint32_t timeout_ = 0;
 | 
					  uint32_t timeout_ = 0;
 | 
				
			||||||
  bool initialized_ = false;
 | 
					  bool initialized_ = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ void IAQCore::setup() {
 | 
				
			|||||||
void IAQCore::update() {
 | 
					void IAQCore::update() {
 | 
				
			||||||
  uint8_t buffer[sizeof(SensorData)];
 | 
					  uint8_t buffer[sizeof(SensorData)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (this->read_register(0xB5, buffer, sizeof(buffer), false) != i2c::ERROR_OK) {
 | 
					  if (this->read_register(0xB5, buffer, sizeof(buffer)) != i2c::ERROR_OK) {
 | 
				
			||||||
    ESP_LOGD(TAG, "Read failed");
 | 
					    ESP_LOGD(TAG, "Read failed");
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    this->publish_nans_();
 | 
					    this->publish_nans_();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ void INA2XXI2C::dump_config() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool INA2XXI2C::read_ina_register(uint8_t reg, uint8_t *data, size_t len) {
 | 
					bool INA2XXI2C::read_ina_register(uint8_t reg, uint8_t *data, size_t len) {
 | 
				
			||||||
  auto ret = this->read_register(reg, data, len, false);
 | 
					  auto ret = this->read_register(reg, data, len);
 | 
				
			||||||
  if (ret != i2c::ERROR_OK) {
 | 
					  if (ret != i2c::ERROR_OK) {
 | 
				
			||||||
    ESP_LOGE(TAG, "read_ina_register_ failed. Reg=0x%02X Err=%d", reg, ret);
 | 
					    ESP_LOGE(TAG, "read_ina_register_ failed. Reg=0x%02X Err=%d", reg, ret);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,7 @@ void KMeterISOComponent::setup() {
 | 
				
			|||||||
    this->reset_to_construction_state();
 | 
					    this->reset_to_construction_state();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  auto err = this->bus_->writev(this->address_, nullptr, 0);
 | 
					  auto err = this->bus_->write_readv(this->address_, nullptr, 0, nullptr, 0);
 | 
				
			||||||
  if (err == esphome::i2c::ERROR_OK) {
 | 
					  if (err == esphome::i2c::ERROR_OK) {
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "Could write to the address %d.", this->address_);
 | 
					    ESP_LOGCONFIG(TAG, "Could write to the address %d.", this->address_);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
@@ -33,7 +33,7 @@ void KMeterISOComponent::setup() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint8_t read_buf[4] = {1};
 | 
					  uint8_t read_buf[4] = {1};
 | 
				
			||||||
  if (!this->read_bytes(KMETER_ERROR_STATUS_REG, read_buf, 1)) {
 | 
					  if (!this->read_register(KMETER_ERROR_STATUS_REG, read_buf, 1)) {
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "Could not read from the device.");
 | 
					    ESP_LOGCONFIG(TAG, "Could not read from the device.");
 | 
				
			||||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
					    this->error_code_ = COMMUNICATION_FAILED;
 | 
				
			||||||
    this->mark_failed();
 | 
					    this->mark_failed();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -185,7 +185,7 @@ uint8_t Lc709203f::get_register_(uint8_t register_to_read, uint16_t *register_va
 | 
				
			|||||||
    //  function will send a stop between the read and the write portion of the I2C
 | 
					    //  function will send a stop between the read and the write portion of the I2C
 | 
				
			||||||
    //  transaction. This is bad in this case and will result in reading nothing but 0xFFFF
 | 
					    //  transaction. This is bad in this case and will result in reading nothing but 0xFFFF
 | 
				
			||||||
    //  from the registers.
 | 
					    //  from the registers.
 | 
				
			||||||
    return_code = this->read_register(register_to_read, &read_buffer[3], 3, false);
 | 
					    return_code = this->read_register(register_to_read, &read_buffer[3], 3);
 | 
				
			||||||
    if (return_code != i2c::NO_ERROR) {
 | 
					    if (return_code != i2c::NO_ERROR) {
 | 
				
			||||||
      // Error on the i2c bus
 | 
					      // Error on the i2c bus
 | 
				
			||||||
      this->status_set_warning(
 | 
					      this->status_set_warning(
 | 
				
			||||||
@@ -226,7 +226,7 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set)
 | 
				
			|||||||
  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.
 | 
				
			||||||
    //  This is done automatically by the write() function.
 | 
					    //  This is done automatically by the write() function.
 | 
				
			||||||
    return_code = this->write(&write_buffer[1], 4, true);
 | 
					    return_code = this->write(&write_buffer[1], 4);
 | 
				
			||||||
    if (return_code == i2c::NO_ERROR) {
 | 
					    if (return_code == i2c::NO_ERROR) {
 | 
				
			||||||
      return return_code;
 | 
					      return return_code;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -451,6 +451,7 @@ CONF_GRID_ROWS = "grid_rows"
 | 
				
			|||||||
CONF_HEADER_MODE = "header_mode"
 | 
					CONF_HEADER_MODE = "header_mode"
 | 
				
			||||||
CONF_HOME = "home"
 | 
					CONF_HOME = "home"
 | 
				
			||||||
CONF_INITIAL_FOCUS = "initial_focus"
 | 
					CONF_INITIAL_FOCUS = "initial_focus"
 | 
				
			||||||
 | 
					CONF_SELECTED_DIGIT = "selected_digit"
 | 
				
			||||||
CONF_KEY_CODE = "key_code"
 | 
					CONF_KEY_CODE = "key_code"
 | 
				
			||||||
CONF_KEYPADS = "keypads"
 | 
					CONF_KEYPADS = "keypads"
 | 
				
			||||||
CONF_LAYOUT = "layout"
 | 
					CONF_LAYOUT = "layout"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,7 +67,6 @@ class Widget:
 | 
				
			|||||||
        self.type = wtype
 | 
					        self.type = wtype
 | 
				
			||||||
        self.config = config
 | 
					        self.config = config
 | 
				
			||||||
        self.scale = 1.0
 | 
					        self.scale = 1.0
 | 
				
			||||||
        self.step = 1.0
 | 
					 | 
				
			||||||
        self.range_from = -sys.maxsize
 | 
					        self.range_from = -sys.maxsize
 | 
				
			||||||
        self.range_to = sys.maxsize
 | 
					        self.range_to = sys.maxsize
 | 
				
			||||||
        if wtype.is_compound():
 | 
					        if wtype.is_compound():
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ from ..defines import (
 | 
				
			|||||||
    CONF_ROLLOVER,
 | 
					    CONF_ROLLOVER,
 | 
				
			||||||
    CONF_SCROLLBAR,
 | 
					    CONF_SCROLLBAR,
 | 
				
			||||||
    CONF_SELECTED,
 | 
					    CONF_SELECTED,
 | 
				
			||||||
 | 
					    CONF_SELECTED_DIGIT,
 | 
				
			||||||
    CONF_TEXTAREA_PLACEHOLDER,
 | 
					    CONF_TEXTAREA_PLACEHOLDER,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from ..lv_validation import lv_bool, lv_float
 | 
					from ..lv_validation import lv_bool, lv_float
 | 
				
			||||||
@@ -38,18 +39,24 @@ def validate_spinbox(config):
 | 
				
			|||||||
    min_val = -1 - max_val
 | 
					    min_val = -1 - max_val
 | 
				
			||||||
    range_from = int(config[CONF_RANGE_FROM])
 | 
					    range_from = int(config[CONF_RANGE_FROM])
 | 
				
			||||||
    range_to = int(config[CONF_RANGE_TO])
 | 
					    range_to = int(config[CONF_RANGE_TO])
 | 
				
			||||||
    step = int(config[CONF_STEP])
 | 
					    step = config[CONF_SELECTED_DIGIT]
 | 
				
			||||||
 | 
					    digits = config[CONF_DIGITS]
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
        range_from > max_val
 | 
					        range_from > max_val
 | 
				
			||||||
        or range_from < min_val
 | 
					        or range_from < min_val
 | 
				
			||||||
        or range_to > max_val
 | 
					        or range_to > max_val
 | 
				
			||||||
        or range_to < min_val
 | 
					        or range_to < min_val
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        raise cv.Invalid("Range outside allowed limits")
 | 
					        raise cv.Invalid("Range outside allowed limits", path=[CONF_RANGE_FROM])
 | 
				
			||||||
    if step <= 0 or step >= (range_to - range_from) / 2:
 | 
					    if digits <= config[CONF_DECIMAL_PLACES]:
 | 
				
			||||||
        raise cv.Invalid("Invalid step value")
 | 
					        raise cv.Invalid(
 | 
				
			||||||
    if config[CONF_DIGITS] <= config[CONF_DECIMAL_PLACES]:
 | 
					            "Number of digits must exceed number of decimal places", path=[CONF_DIGITS]
 | 
				
			||||||
        raise cv.Invalid("Number of digits must exceed number of decimal places")
 | 
					        )
 | 
				
			||||||
 | 
					    if step >= digits:
 | 
				
			||||||
 | 
					        raise cv.Invalid(
 | 
				
			||||||
 | 
					            "Initial selected digit must be less than number of digits",
 | 
				
			||||||
 | 
					            path=[CONF_SELECTED_DIGIT],
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
    return config
 | 
					    return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,7 +66,10 @@ SPINBOX_SCHEMA = cv.Schema(
 | 
				
			|||||||
        cv.Optional(CONF_RANGE_FROM, default=0): cv.float_,
 | 
					        cv.Optional(CONF_RANGE_FROM, default=0): cv.float_,
 | 
				
			||||||
        cv.Optional(CONF_RANGE_TO, default=100): cv.float_,
 | 
					        cv.Optional(CONF_RANGE_TO, default=100): cv.float_,
 | 
				
			||||||
        cv.Optional(CONF_DIGITS, default=4): cv.int_range(1, 10),
 | 
					        cv.Optional(CONF_DIGITS, default=4): cv.int_range(1, 10),
 | 
				
			||||||
        cv.Optional(CONF_STEP, default=1.0): cv.positive_float,
 | 
					        cv.Optional(CONF_STEP): cv.invalid(
 | 
				
			||||||
 | 
					            f"{CONF_STEP} has been replaced by {CONF_SELECTED_DIGIT}"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_SELECTED_DIGIT, default=0): cv.positive_int,
 | 
				
			||||||
        cv.Optional(CONF_DECIMAL_PLACES, default=0): cv.int_range(0, 6),
 | 
					        cv.Optional(CONF_DECIMAL_PLACES, default=0): cv.int_range(0, 6),
 | 
				
			||||||
        cv.Optional(CONF_ROLLOVER, default=False): lv_bool,
 | 
					        cv.Optional(CONF_ROLLOVER, default=False): lv_bool,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -93,13 +103,12 @@ class SpinboxType(WidgetType):
 | 
				
			|||||||
            scale = 10 ** config[CONF_DECIMAL_PLACES]
 | 
					            scale = 10 ** config[CONF_DECIMAL_PLACES]
 | 
				
			||||||
            range_from = int(config[CONF_RANGE_FROM]) * scale
 | 
					            range_from = int(config[CONF_RANGE_FROM]) * scale
 | 
				
			||||||
            range_to = int(config[CONF_RANGE_TO]) * scale
 | 
					            range_to = int(config[CONF_RANGE_TO]) * scale
 | 
				
			||||||
            step = int(config[CONF_STEP]) * scale
 | 
					            step = config[CONF_SELECTED_DIGIT]
 | 
				
			||||||
            w.scale = scale
 | 
					            w.scale = scale
 | 
				
			||||||
            w.step = step
 | 
					 | 
				
			||||||
            w.range_to = range_to
 | 
					            w.range_to = range_to
 | 
				
			||||||
            w.range_from = range_from
 | 
					            w.range_from = range_from
 | 
				
			||||||
            lv.spinbox_set_range(w.obj, range_from, range_to)
 | 
					            lv.spinbox_set_range(w.obj, range_from, range_to)
 | 
				
			||||||
            await w.set_property(CONF_STEP, step)
 | 
					            await w.set_property("step", 10**step)
 | 
				
			||||||
            await w.set_property(CONF_ROLLOVER, config)
 | 
					            await w.set_property(CONF_ROLLOVER, config)
 | 
				
			||||||
            lv.spinbox_set_digit_format(
 | 
					            lv.spinbox_set_digit_format(
 | 
				
			||||||
                w.obj, digits, digits - config[CONF_DECIMAL_PLACES]
 | 
					                w.obj, digits, digits - config[CONF_DECIMAL_PLACES]
 | 
				
			||||||
@@ -120,7 +129,7 @@ class SpinboxType(WidgetType):
 | 
				
			|||||||
        return config[CONF_RANGE_FROM]
 | 
					        return config[CONF_RANGE_FROM]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_step(self, config: dict):
 | 
					    def get_step(self, config: dict):
 | 
				
			||||||
        return config[CONF_STEP]
 | 
					        return 10 ** config[CONF_SELECTED_DIGIT]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
spinbox_spec = SpinboxType()
 | 
					spinbox_spec = SpinboxType()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,8 @@ from esphome.loader import get_component
 | 
				
			|||||||
CODEOWNERS = ["@clydebarrow"]
 | 
					CODEOWNERS = ["@clydebarrow"]
 | 
				
			||||||
MULTI_CONF = True
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
map_ = cg.std_ns.class_("map")
 | 
					mapping_ns = cg.esphome_ns.namespace("mapping")
 | 
				
			||||||
 | 
					mapping_class = mapping_ns.class_("Mapping")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_ENTRIES = "entries"
 | 
					CONF_ENTRIES = "entries"
 | 
				
			||||||
CONF_CLASS = "class"
 | 
					CONF_CLASS = "class"
 | 
				
			||||||
@@ -29,7 +30,11 @@ class IndexType:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
INDEX_TYPES = {
 | 
					INDEX_TYPES = {
 | 
				
			||||||
    "int": IndexType(cv.int_, cg.int_, int),
 | 
					    "int": IndexType(cv.int_, cg.int_, int),
 | 
				
			||||||
    "string": IndexType(cv.string, cg.std_string, str),
 | 
					    "string": IndexType(
 | 
				
			||||||
 | 
					        cv.string,
 | 
				
			||||||
 | 
					        cg.std_string,
 | 
				
			||||||
 | 
					        str,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,7 +52,7 @@ def to_schema(value):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
BASE_SCHEMA = cv.Schema(
 | 
					BASE_SCHEMA = cv.Schema(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        cv.Required(CONF_ID): cv.declare_id(map_),
 | 
					        cv.Required(CONF_ID): cv.declare_id(mapping_class),
 | 
				
			||||||
        cv.Required(CONF_FROM): cv.one_of(*INDEX_TYPES, lower=True),
 | 
					        cv.Required(CONF_FROM): cv.one_of(*INDEX_TYPES, lower=True),
 | 
				
			||||||
        cv.Required(CONF_TO): cv.string,
 | 
					        cv.Required(CONF_TO): cv.string,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@@ -123,12 +128,15 @@ async def to_code(config):
 | 
				
			|||||||
        if list(entries.values())[0].op != ".":
 | 
					        if list(entries.values())[0].op != ".":
 | 
				
			||||||
            value_type = value_type.operator("ptr")
 | 
					            value_type = value_type.operator("ptr")
 | 
				
			||||||
    varid = config[CONF_ID]
 | 
					    varid = config[CONF_ID]
 | 
				
			||||||
    varid.type = map_.template(index_type, value_type)
 | 
					    varid.type = mapping_class.template(
 | 
				
			||||||
 | 
					        index_type,
 | 
				
			||||||
 | 
					        value_type,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    var = MockObj(varid, ".")
 | 
					    var = MockObj(varid, ".")
 | 
				
			||||||
    decl = VariableDeclarationExpression(varid.type, "", varid)
 | 
					    decl = VariableDeclarationExpression(varid.type, "", varid)
 | 
				
			||||||
    add_global(decl)
 | 
					    add_global(decl)
 | 
				
			||||||
    CORE.register_variable(varid, var)
 | 
					    CORE.register_variable(varid, var)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for key, value in entries.items():
 | 
					    for key, value in entries.items():
 | 
				
			||||||
        cg.add(var.insert((key, value)))
 | 
					        cg.add(var.set(key, value))
 | 
				
			||||||
    return var
 | 
					    return var
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								esphome/components/mapping/mapping.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								esphome/components/mapping/mapping.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome::mapping {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using alloc_string_t = std::basic_string<char, std::char_traits<char>, RAMAllocator<char>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Mapping class with custom allocator.
 | 
				
			||||||
 | 
					 * Additionally, when std::string is used as key or value, it will be replaced with a custom string type
 | 
				
			||||||
 | 
					 * that uses RAMAllocator.
 | 
				
			||||||
 | 
					 * @tparam K The type of the key in the mapping.
 | 
				
			||||||
 | 
					 * @tparam V The type of the value in the mapping. Should be a basic type or pointer.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "mapping";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename K, typename V> class Mapping {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  // Constructor
 | 
				
			||||||
 | 
					  Mapping() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  using key_t = const std::conditional_t<std::is_same_v<K, std::string>,
 | 
				
			||||||
 | 
					                                         alloc_string_t,  // if K is std::string, custom string type
 | 
				
			||||||
 | 
					                                         K>;
 | 
				
			||||||
 | 
					  using value_t = std::conditional_t<std::is_same_v<V, std::string>,
 | 
				
			||||||
 | 
					                                     alloc_string_t,  // if V is std::string, custom string type
 | 
				
			||||||
 | 
					                                     V>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set(const K &key, const V &value) { this->map_[key_t{key}] = value; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  V get(const K &key) const {
 | 
				
			||||||
 | 
					    auto it = this->map_.find(key_t{key});
 | 
				
			||||||
 | 
					    if (it != this->map_.end()) {
 | 
				
			||||||
 | 
					      return V{it->second};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if constexpr (std::is_pointer_v<K>) {
 | 
				
			||||||
 | 
					      esph_log_e(TAG, "Key '%p' not found in mapping", key);
 | 
				
			||||||
 | 
					    } else if constexpr (std::is_same_v<K, std::string>) {
 | 
				
			||||||
 | 
					      esph_log_e(TAG, "Key '%s' not found in mapping", key.c_str());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      esph_log_e(TAG, "Key '%s' not found in mapping", to_string(key).c_str());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // index map overload
 | 
				
			||||||
 | 
					  V operator[](K key) { return this->get(key); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // convenience function for strings to get a C-style string
 | 
				
			||||||
 | 
					  template<typename T = V, std::enable_if_t<std::is_same_v<T, std::string>, int> = 0>
 | 
				
			||||||
 | 
					  const char *operator[](K key) const {
 | 
				
			||||||
 | 
					    auto it = this->map_.find(key_t{key});
 | 
				
			||||||
 | 
					    if (it != this->map_.end()) {
 | 
				
			||||||
 | 
					      return it->second.c_str();  // safe since value remains in map
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return "";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  std::map<key_t, value_t, std::less<key_t>, RAMAllocator<std::pair<key_t, value_t>>> map_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace esphome::mapping
 | 
				
			||||||
@@ -198,7 +198,7 @@ uint16_t Mcp4461Component::get_wiper_level_(Mcp4461WiperIdx wiper) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) {
 | 
					uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) {
 | 
				
			||||||
  uint8_t addr = this->get_wiper_address_(wiper_idx);
 | 
					  uint8_t addr = this->get_wiper_address_(wiper_idx);
 | 
				
			||||||
  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
 | 
					  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::READ);
 | 
				
			||||||
  if (wiper_idx > 3) {
 | 
					  if (wiper_idx > 3) {
 | 
				
			||||||
    if (!this->is_eeprom_ready_for_writing_(true)) {
 | 
					    if (!this->is_eeprom_ready_for_writing_(true)) {
 | 
				
			||||||
      return 0;
 | 
					      return 0;
 | 
				
			||||||
@@ -328,7 +328,7 @@ bool Mcp4461Component::increase_wiper_(Mcp4461WiperIdx wiper) {
 | 
				
			|||||||
  ESP_LOGV(TAG, "Increasing wiper %u", wiper_idx);
 | 
					  ESP_LOGV(TAG, "Increasing wiper %u", wiper_idx);
 | 
				
			||||||
  uint8_t addr = this->get_wiper_address_(wiper_idx);
 | 
					  uint8_t addr = this->get_wiper_address_(wiper_idx);
 | 
				
			||||||
  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
 | 
					  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
 | 
				
			||||||
  auto err = this->write(&this->address_, reg, sizeof(reg));
 | 
					  auto err = this->write(&this->address_, reg);
 | 
				
			||||||
  if (err != i2c::ERROR_OK) {
 | 
					  if (err != i2c::ERROR_OK) {
 | 
				
			||||||
    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
 | 
					    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
@@ -359,7 +359,7 @@ bool Mcp4461Component::decrease_wiper_(Mcp4461WiperIdx wiper) {
 | 
				
			|||||||
  ESP_LOGV(TAG, "Decreasing wiper %u", wiper_idx);
 | 
					  ESP_LOGV(TAG, "Decreasing wiper %u", wiper_idx);
 | 
				
			||||||
  uint8_t addr = this->get_wiper_address_(wiper_idx);
 | 
					  uint8_t addr = this->get_wiper_address_(wiper_idx);
 | 
				
			||||||
  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::DECREMENT);
 | 
					  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::DECREMENT);
 | 
				
			||||||
  auto err = this->write(&this->address_, reg, sizeof(reg));
 | 
					  auto err = this->write(&this->address_, reg);
 | 
				
			||||||
  if (err != i2c::ERROR_OK) {
 | 
					  if (err != i2c::ERROR_OK) {
 | 
				
			||||||
    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
 | 
					    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -309,8 +309,12 @@ class DriverChip:
 | 
				
			|||||||
                CONF_NATIVE_HEIGHT, height + offset_height * 2
 | 
					                CONF_NATIVE_HEIGHT, height + offset_height * 2
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            offset_height = native_height - height - offset_height
 | 
					            offset_height = native_height - height - offset_height
 | 
				
			||||||
        # Swap default dimensions if swap_xy is set
 | 
					        # Swap default dimensions if swap_xy is set, or if rotation is 90/270 and we are not using a buffer
 | 
				
			||||||
        if transform[CONF_SWAP_XY] is True:
 | 
					        rotated = not requires_buffer(config) and config.get(CONF_ROTATION, 0) in (
 | 
				
			||||||
 | 
					            90,
 | 
				
			||||||
 | 
					            270,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if transform[CONF_SWAP_XY] is True or rotated:
 | 
				
			||||||
            width, height = height, width
 | 
					            width, height = height, width
 | 
				
			||||||
            offset_height, offset_width = offset_width, offset_height
 | 
					            offset_height, offset_width = offset_width, offset_height
 | 
				
			||||||
        return width, height, offset_width, offset_height
 | 
					        return width, height, offset_width, offset_height
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,18 +75,18 @@ float MLX90614Component::get_setup_priority() const { return setup_priority::DAT
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void MLX90614Component::update() {
 | 
					void MLX90614Component::update() {
 | 
				
			||||||
  uint8_t emissivity[3];
 | 
					  uint8_t emissivity[3];
 | 
				
			||||||
  if (this->read_register(MLX90614_EMISSIVITY, emissivity, 3, false) != i2c::ERROR_OK) {
 | 
					  if (this->read_register(MLX90614_EMISSIVITY, emissivity, 3) != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  uint8_t raw_object[3];
 | 
					  uint8_t raw_object[3];
 | 
				
			||||||
  if (this->read_register(MLX90614_TEMPERATURE_OBJECT_1, raw_object, 3, false) != i2c::ERROR_OK) {
 | 
					  if (this->read_register(MLX90614_TEMPERATURE_OBJECT_1, raw_object, 3) != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint8_t raw_ambient[3];
 | 
					  uint8_t raw_ambient[3];
 | 
				
			||||||
  if (this->read_register(MLX90614_TEMPERATURE_AMBIENT, raw_ambient, 3, false) != i2c::ERROR_OK) {
 | 
					  if (this->read_register(MLX90614_TEMPERATURE_AMBIENT, raw_ambient, 3) != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ static const char *const TAG = "mpl3115a2";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void MPL3115A2Component::setup() {
 | 
					void MPL3115A2Component::setup() {
 | 
				
			||||||
  uint8_t whoami = 0xFF;
 | 
					  uint8_t whoami = 0xFF;
 | 
				
			||||||
  if (!this->read_byte(MPL3115A2_WHOAMI, &whoami, false)) {
 | 
					  if (!this->read_byte(MPL3115A2_WHOAMI, &whoami)) {
 | 
				
			||||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
					    this->error_code_ = COMMUNICATION_FAILED;
 | 
				
			||||||
    this->mark_failed();
 | 
					    this->mark_failed();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
@@ -54,24 +54,24 @@ void MPL3115A2Component::dump_config() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void MPL3115A2Component::update() {
 | 
					void MPL3115A2Component::update() {
 | 
				
			||||||
  uint8_t mode = MPL3115A2_CTRL_REG1_OS128;
 | 
					  uint8_t mode = MPL3115A2_CTRL_REG1_OS128;
 | 
				
			||||||
  this->write_byte(MPL3115A2_CTRL_REG1, mode, true);
 | 
					  this->write_byte(MPL3115A2_CTRL_REG1, mode);
 | 
				
			||||||
  // Trigger a new reading
 | 
					  // Trigger a new reading
 | 
				
			||||||
  mode |= MPL3115A2_CTRL_REG1_OST;
 | 
					  mode |= MPL3115A2_CTRL_REG1_OST;
 | 
				
			||||||
  if (this->altitude_ != nullptr)
 | 
					  if (this->altitude_ != nullptr)
 | 
				
			||||||
    mode |= MPL3115A2_CTRL_REG1_ALT;
 | 
					    mode |= MPL3115A2_CTRL_REG1_ALT;
 | 
				
			||||||
  this->write_byte(MPL3115A2_CTRL_REG1, mode, true);
 | 
					  this->write_byte(MPL3115A2_CTRL_REG1, mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Wait until status shows reading available
 | 
					  // Wait until status shows reading available
 | 
				
			||||||
  uint8_t status = 0;
 | 
					  uint8_t status = 0;
 | 
				
			||||||
  if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status, false) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) {
 | 
					  if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) {
 | 
				
			||||||
    delay(10);
 | 
					    delay(10);
 | 
				
			||||||
    if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status, false) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) {
 | 
					    if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint8_t buffer[5] = {0, 0, 0, 0, 0};
 | 
					  uint8_t buffer[5] = {0, 0, 0, 0, 0};
 | 
				
			||||||
  this->read_register(MPL3115A2_REGISTER_PRESSURE_MSB, buffer, 5, false);
 | 
					  this->read_register(MPL3115A2_REGISTER_PRESSURE_MSB, buffer, 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  float altitude = 0, pressure = 0;
 | 
					  float altitude = 0, pressure = 0;
 | 
				
			||||||
  if (this->altitude_ != nullptr) {
 | 
					  if (this->altitude_ != nullptr) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ float NPI19Component::get_setup_priority() const { return setup_priority::DATA;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
i2c::ErrorCode NPI19Component::read_(uint16_t &raw_temperature, uint16_t &raw_pressure) {
 | 
					i2c::ErrorCode NPI19Component::read_(uint16_t &raw_temperature, uint16_t &raw_pressure) {
 | 
				
			||||||
  // initiate data read from device
 | 
					  // initiate data read from device
 | 
				
			||||||
  i2c::ErrorCode w_err = write(&READ_COMMAND, sizeof(READ_COMMAND), true);
 | 
					  i2c::ErrorCode w_err = write(&READ_COMMAND, sizeof(READ_COMMAND));
 | 
				
			||||||
  if (w_err != i2c::ERROR_OK) {
 | 
					  if (w_err != i2c::ERROR_OK) {
 | 
				
			||||||
    return w_err;
 | 
					    return w_err;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,7 +72,7 @@ void OPT3001Sensor::read_lx_(const std::function<void(float)> &f) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->set_timeout("read", OPT3001_CONVERSION_TIME_800, [this, f]() {
 | 
					  this->set_timeout("read", OPT3001_CONVERSION_TIME_800, [this, f]() {
 | 
				
			||||||
    if (this->write(&OPT3001_REG_CONFIGURATION, 1, true) != i2c::ERROR_OK) {
 | 
					    if (this->write(&OPT3001_REG_CONFIGURATION, 1) != i2c::ERROR_OK) {
 | 
				
			||||||
      ESP_LOGW(TAG, "Starting configuration register read failed");
 | 
					      ESP_LOGW(TAG, "Starting configuration register read failed");
 | 
				
			||||||
      f(NAN);
 | 
					      f(NAN);
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ void PCA6416AComponent::setup() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Test to see if the device supports pull-up resistors
 | 
					  // Test to see if the device supports pull-up resistors
 | 
				
			||||||
  if (this->read_register(PCAL6416A_PULL_EN0, &value, 1, true) == i2c::ERROR_OK) {
 | 
					  if (this->read_register(PCAL6416A_PULL_EN0, &value, 1) == i2c::ERROR_OK) {
 | 
				
			||||||
    this->has_pullup_ = true;
 | 
					    this->has_pullup_ = true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -105,7 +105,7 @@ bool PCA6416AComponent::read_register_(uint8_t reg, uint8_t *value) {
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->last_error_ = this->read_register(reg, value, 1, true);
 | 
					  this->last_error_ = this->read_register(reg, value, 1);
 | 
				
			||||||
  if (this->last_error_ != i2c::ERROR_OK) {
 | 
					  if (this->last_error_ != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
					    ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
				
			||||||
@@ -122,7 +122,7 @@ bool PCA6416AComponent::write_register_(uint8_t reg, uint8_t value) {
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->last_error_ = this->write_register(reg, &value, 1, true);
 | 
					  this->last_error_ = this->write_register(reg, &value, 1);
 | 
				
			||||||
  if (this->last_error_ != i2c::ERROR_OK) {
 | 
					  if (this->last_error_ != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
					    ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -96,7 +96,7 @@ bool PCA9554Component::read_inputs_() {
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_, true);
 | 
					  this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_);
 | 
				
			||||||
  if (this->last_error_ != i2c::ERROR_OK) {
 | 
					  if (this->last_error_ != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
					    ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
				
			||||||
@@ -114,7 +114,7 @@ bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) {
 | 
				
			|||||||
  uint8_t outputs[2];
 | 
					  uint8_t outputs[2];
 | 
				
			||||||
  outputs[0] = (uint8_t) value;
 | 
					  outputs[0] = (uint8_t) value;
 | 
				
			||||||
  outputs[1] = (uint8_t) (value >> 8);
 | 
					  outputs[1] = (uint8_t) (value >> 8);
 | 
				
			||||||
  this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_, true);
 | 
					  this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_);
 | 
				
			||||||
  if (this->last_error_ != i2c::ERROR_OK) {
 | 
					  if (this->last_error_ != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
					    ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,11 +138,37 @@ void Rtttl::stop() {
 | 
				
			|||||||
    this->set_state_(STATE_STOPPING);
 | 
					    this->set_state_(STATE_STOPPING);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					  this->position_ = this->rtttl_.length();
 | 
				
			||||||
 | 
					  this->note_duration_ = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Rtttl::finish_() {
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Rtttl::finish_()");
 | 
				
			||||||
 | 
					#ifdef USE_OUTPUT
 | 
				
			||||||
 | 
					  if (this->output_ != nullptr) {
 | 
				
			||||||
 | 
					    this->output_->set_level(0.0);
 | 
				
			||||||
 | 
					    this->set_state_(State::STATE_STOPPED);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_SPEAKER
 | 
				
			||||||
 | 
					  if (this->speaker_ != nullptr) {
 | 
				
			||||||
 | 
					    SpeakerSample sample[2];
 | 
				
			||||||
 | 
					    sample[0].left = 0;
 | 
				
			||||||
 | 
					    sample[0].right = 0;
 | 
				
			||||||
 | 
					    sample[1].left = 0;
 | 
				
			||||||
 | 
					    sample[1].right = 0;
 | 
				
			||||||
 | 
					    this->speaker_->play((uint8_t *) (&sample), 8);
 | 
				
			||||||
 | 
					    this->speaker_->finish();
 | 
				
			||||||
 | 
					    this->set_state_(State::STATE_STOPPING);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  // Ensure no more notes are played in case finish_() is called for an error.
 | 
				
			||||||
 | 
					  this->position_ = this->rtttl_.length();
 | 
				
			||||||
  this->note_duration_ = 0;
 | 
					  this->note_duration_ = 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Rtttl::loop() {
 | 
					void Rtttl::loop() {
 | 
				
			||||||
  if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) {
 | 
					  if (this->state_ == State::STATE_STOPPED) {
 | 
				
			||||||
    this->disable_loop();
 | 
					    this->disable_loop();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -152,6 +178,8 @@ void Rtttl::loop() {
 | 
				
			|||||||
    if (this->state_ == State::STATE_STOPPING) {
 | 
					    if (this->state_ == State::STATE_STOPPING) {
 | 
				
			||||||
      if (this->speaker_->is_stopped()) {
 | 
					      if (this->speaker_->is_stopped()) {
 | 
				
			||||||
        this->set_state_(State::STATE_STOPPED);
 | 
					        this->set_state_(State::STATE_STOPPED);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else if (this->state_ == State::STATE_INIT) {
 | 
					    } else if (this->state_ == State::STATE_INIT) {
 | 
				
			||||||
      if (this->speaker_->is_stopped()) {
 | 
					      if (this->speaker_->is_stopped()) {
 | 
				
			||||||
@@ -207,7 +235,7 @@ void Rtttl::loop() {
 | 
				
			|||||||
  if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_)
 | 
					  if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  if (!this->rtttl_[this->position_]) {
 | 
					  if (this->position_ >= this->rtttl_.length()) {
 | 
				
			||||||
    this->finish_();
 | 
					    this->finish_();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -346,31 +374,6 @@ void Rtttl::loop() {
 | 
				
			|||||||
  this->last_note_ = millis();
 | 
					  this->last_note_ = millis();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Rtttl::finish_() {
 | 
					 | 
				
			||||||
#ifdef USE_OUTPUT
 | 
					 | 
				
			||||||
  if (this->output_ != nullptr) {
 | 
					 | 
				
			||||||
    this->output_->set_level(0.0);
 | 
					 | 
				
			||||||
    this->set_state_(State::STATE_STOPPED);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef USE_SPEAKER
 | 
					 | 
				
			||||||
  if (this->speaker_ != nullptr) {
 | 
					 | 
				
			||||||
    SpeakerSample sample[2];
 | 
					 | 
				
			||||||
    sample[0].left = 0;
 | 
					 | 
				
			||||||
    sample[0].right = 0;
 | 
					 | 
				
			||||||
    sample[1].left = 0;
 | 
					 | 
				
			||||||
    sample[1].right = 0;
 | 
					 | 
				
			||||||
    this->speaker_->play((uint8_t *) (&sample), 8);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this->speaker_->finish();
 | 
					 | 
				
			||||||
    this->set_state_(State::STATE_STOPPING);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
  this->note_duration_ = 0;
 | 
					 | 
				
			||||||
  this->on_finished_playback_callback_.call();
 | 
					 | 
				
			||||||
  ESP_LOGD(TAG, "Playback finished");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
 | 
					#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
 | 
				
			||||||
static const LogString *state_to_string(State state) {
 | 
					static const LogString *state_to_string(State state) {
 | 
				
			||||||
  switch (state) {
 | 
					  switch (state) {
 | 
				
			||||||
@@ -397,7 +400,11 @@ void Rtttl::set_state_(State state) {
 | 
				
			|||||||
           LOG_STR_ARG(state_to_string(state)));
 | 
					           LOG_STR_ARG(state_to_string(state)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Clear loop_done when transitioning from STOPPED to any other state
 | 
					  // Clear loop_done when transitioning from STOPPED to any other state
 | 
				
			||||||
  if (old_state == State::STATE_STOPPED && state != State::STATE_STOPPED) {
 | 
					  if (state == State::STATE_STOPPED) {
 | 
				
			||||||
 | 
					    this->disable_loop();
 | 
				
			||||||
 | 
					    this->on_finished_playback_callback_.call();
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "Playback finished");
 | 
				
			||||||
 | 
					  } else if (old_state == State::STATE_STOPPED) {
 | 
				
			||||||
    this->enable_loop();
 | 
					    this->enable_loop();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,35 +60,60 @@ class Rtttl : public Component {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @brief Finalizes the playback of the RTTTL string.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * This method is called internally when the end of the RTTTL string is reached
 | 
				
			||||||
 | 
					   * or when a parsing error occurs. It stops the output, sets the component state,
 | 
				
			||||||
 | 
					   * and triggers the on_finished_playback_callback_.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
  void finish_();
 | 
					  void finish_();
 | 
				
			||||||
  void set_state_(State state);
 | 
					  void set_state_(State state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The RTTTL string to play.
 | 
				
			||||||
  std::string rtttl_{""};
 | 
					  std::string rtttl_{""};
 | 
				
			||||||
 | 
					  /// The current position in the RTTTL string.
 | 
				
			||||||
  size_t position_{0};
 | 
					  size_t position_{0};
 | 
				
			||||||
 | 
					  /// The duration of a whole note in milliseconds.
 | 
				
			||||||
  uint16_t wholenote_;
 | 
					  uint16_t wholenote_;
 | 
				
			||||||
 | 
					  /// The default duration of a note (e.g. 4 for a quarter note).
 | 
				
			||||||
  uint16_t default_duration_;
 | 
					  uint16_t default_duration_;
 | 
				
			||||||
 | 
					  /// The default octave for a note.
 | 
				
			||||||
  uint16_t default_octave_;
 | 
					  uint16_t default_octave_;
 | 
				
			||||||
 | 
					  /// The time the last note was started.
 | 
				
			||||||
  uint32_t last_note_;
 | 
					  uint32_t last_note_;
 | 
				
			||||||
 | 
					  /// The duration of the current note in milliseconds.
 | 
				
			||||||
  uint16_t note_duration_;
 | 
					  uint16_t note_duration_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The frequency of the current note in Hz.
 | 
				
			||||||
  uint32_t output_freq_;
 | 
					  uint32_t output_freq_;
 | 
				
			||||||
 | 
					  /// The gain of the output.
 | 
				
			||||||
  float gain_{0.6f};
 | 
					  float gain_{0.6f};
 | 
				
			||||||
 | 
					  /// The current state of the RTTTL player.
 | 
				
			||||||
  State state_{State::STATE_STOPPED};
 | 
					  State state_{State::STATE_STOPPED};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_OUTPUT
 | 
					#ifdef USE_OUTPUT
 | 
				
			||||||
 | 
					  /// The output to write the sound to.
 | 
				
			||||||
  output::FloatOutput *output_;
 | 
					  output::FloatOutput *output_;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_SPEAKER
 | 
					#ifdef USE_SPEAKER
 | 
				
			||||||
 | 
					  /// The speaker to write the sound to.
 | 
				
			||||||
  speaker::Speaker *speaker_{nullptr};
 | 
					  speaker::Speaker *speaker_{nullptr};
 | 
				
			||||||
 | 
					  /// The sample rate of the speaker.
 | 
				
			||||||
  int sample_rate_{16000};
 | 
					  int sample_rate_{16000};
 | 
				
			||||||
 | 
					  /// The number of samples for one full cycle of a note's waveform, in Q10 fixed-point format.
 | 
				
			||||||
  int samples_per_wave_{0};
 | 
					  int samples_per_wave_{0};
 | 
				
			||||||
 | 
					  /// The number of samples sent.
 | 
				
			||||||
  int samples_sent_{0};
 | 
					  int samples_sent_{0};
 | 
				
			||||||
 | 
					  /// The total number of samples to send.
 | 
				
			||||||
  int samples_count_{0};
 | 
					  int samples_count_{0};
 | 
				
			||||||
 | 
					  /// The number of samples for the gap between notes.
 | 
				
			||||||
  int samples_gap_{0};
 | 
					  int samples_gap_{0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The callback to call when playback is finished.
 | 
				
			||||||
  CallbackManager<void()> on_finished_playback_callback_;
 | 
					  CallbackManager<void()> on_finished_playback_callback_;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,26 +65,47 @@ ACCELERATION_MODES = {
 | 
				
			|||||||
    "high": RhtAccelerationMode.HIGH_ACCELERATION,
 | 
					    "high": RhtAccelerationMode.HIGH_ACCELERATION,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GAS_SENSOR = cv.Schema(
 | 
					
 | 
				
			||||||
    {
 | 
					def _gas_sensor(
 | 
				
			||||||
        cv.Optional(CONF_ALGORITHM_TUNING): cv.Schema(
 | 
					    *,
 | 
				
			||||||
            {
 | 
					    index_offset: int,
 | 
				
			||||||
                cv.Optional(CONF_INDEX_OFFSET, default=100): cv.int_range(1, 250),
 | 
					    learning_time_offset: int,
 | 
				
			||||||
                cv.Optional(CONF_LEARNING_TIME_OFFSET_HOURS, default=12): cv.int_range(
 | 
					    learning_time_gain: int,
 | 
				
			||||||
                    1, 1000
 | 
					    gating_max_duration: int,
 | 
				
			||||||
                ),
 | 
					    std_initial: int,
 | 
				
			||||||
                cv.Optional(CONF_LEARNING_TIME_GAIN_HOURS, default=12): cv.int_range(
 | 
					    gain_factor: int,
 | 
				
			||||||
                    1, 1000
 | 
					) -> cv.Schema:
 | 
				
			||||||
                ),
 | 
					    return sensor.sensor_schema(
 | 
				
			||||||
                cv.Optional(
 | 
					        icon=ICON_RADIATOR,
 | 
				
			||||||
                    CONF_GATING_MAX_DURATION_MINUTES, default=720
 | 
					        accuracy_decimals=0,
 | 
				
			||||||
                ): cv.int_range(0, 3000),
 | 
					        device_class=DEVICE_CLASS_AQI,
 | 
				
			||||||
                cv.Optional(CONF_STD_INITIAL, default=50): cv.int_,
 | 
					        state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
                cv.Optional(CONF_GAIN_FACTOR, default=230): cv.int_range(1, 1000),
 | 
					    ).extend(
 | 
				
			||||||
            }
 | 
					        {
 | 
				
			||||||
        )
 | 
					            cv.Optional(CONF_ALGORITHM_TUNING): cv.Schema(
 | 
				
			||||||
    }
 | 
					                {
 | 
				
			||||||
)
 | 
					                    cv.Optional(CONF_INDEX_OFFSET, default=index_offset): cv.int_range(
 | 
				
			||||||
 | 
					                        1, 250
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    cv.Optional(
 | 
				
			||||||
 | 
					                        CONF_LEARNING_TIME_OFFSET_HOURS, default=learning_time_offset
 | 
				
			||||||
 | 
					                    ): cv.int_range(1, 1000),
 | 
				
			||||||
 | 
					                    cv.Optional(
 | 
				
			||||||
 | 
					                        CONF_LEARNING_TIME_GAIN_HOURS, default=learning_time_gain
 | 
				
			||||||
 | 
					                    ): cv.int_range(1, 1000),
 | 
				
			||||||
 | 
					                    cv.Optional(
 | 
				
			||||||
 | 
					                        CONF_GATING_MAX_DURATION_MINUTES, default=gating_max_duration
 | 
				
			||||||
 | 
					                    ): cv.int_range(0, 3000),
 | 
				
			||||||
 | 
					                    cv.Optional(CONF_STD_INITIAL, default=std_initial): cv.int_range(
 | 
				
			||||||
 | 
					                        10, 5000
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    cv.Optional(CONF_GAIN_FACTOR, default=gain_factor): cv.int_range(
 | 
				
			||||||
 | 
					                        1, 1000
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def float_previously_pct(value):
 | 
					def float_previously_pct(value):
 | 
				
			||||||
@@ -127,18 +148,22 @@ CONFIG_SCHEMA = (
 | 
				
			|||||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
					                state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            cv.Optional(CONF_AUTO_CLEANING_INTERVAL): cv.update_interval,
 | 
					            cv.Optional(CONF_AUTO_CLEANING_INTERVAL): cv.update_interval,
 | 
				
			||||||
            cv.Optional(CONF_VOC): sensor.sensor_schema(
 | 
					            cv.Optional(CONF_VOC): _gas_sensor(
 | 
				
			||||||
                icon=ICON_RADIATOR,
 | 
					                index_offset=100,
 | 
				
			||||||
                accuracy_decimals=0,
 | 
					                learning_time_offset=12,
 | 
				
			||||||
                device_class=DEVICE_CLASS_AQI,
 | 
					                learning_time_gain=12,
 | 
				
			||||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
					                gating_max_duration=180,
 | 
				
			||||||
            ).extend(GAS_SENSOR),
 | 
					                std_initial=50,
 | 
				
			||||||
            cv.Optional(CONF_NOX): sensor.sensor_schema(
 | 
					                gain_factor=230,
 | 
				
			||||||
                icon=ICON_RADIATOR,
 | 
					            ),
 | 
				
			||||||
                accuracy_decimals=0,
 | 
					            cv.Optional(CONF_NOX): _gas_sensor(
 | 
				
			||||||
                device_class=DEVICE_CLASS_AQI,
 | 
					                index_offset=1,
 | 
				
			||||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
					                learning_time_offset=12,
 | 
				
			||||||
            ).extend(GAS_SENSOR),
 | 
					                learning_time_gain=12,
 | 
				
			||||||
 | 
					                gating_max_duration=720,
 | 
				
			||||||
 | 
					                std_initial=50,
 | 
				
			||||||
 | 
					                gain_factor=230,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
            cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean,
 | 
					            cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean,
 | 
				
			||||||
            cv.Optional(CONF_VOC_BASELINE): cv.hex_uint16_t,
 | 
					            cv.Optional(CONF_VOC_BASELINE): cv.hex_uint16_t,
 | 
				
			||||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
					            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
				
			||||||
@@ -194,16 +219,15 @@ async def to_code(config):
 | 
				
			|||||||
    await i2c.register_i2c_device(var, config)
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for key, funcName in SETTING_MAP.items():
 | 
					    for key, funcName in SETTING_MAP.items():
 | 
				
			||||||
        if key in config:
 | 
					        if cfg := config.get(key):
 | 
				
			||||||
            cg.add(getattr(var, funcName)(config[key]))
 | 
					            cg.add(getattr(var, funcName)(cfg))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for key, funcName in SENSOR_MAP.items():
 | 
					    for key, funcName in SENSOR_MAP.items():
 | 
				
			||||||
        if key in config:
 | 
					        if cfg := config.get(key):
 | 
				
			||||||
            sens = await sensor.new_sensor(config[key])
 | 
					            sens = await sensor.new_sensor(cfg)
 | 
				
			||||||
            cg.add(getattr(var, funcName)(sens))
 | 
					            cg.add(getattr(var, funcName)(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if CONF_VOC in config and CONF_ALGORITHM_TUNING in config[CONF_VOC]:
 | 
					    if cfg := config.get(CONF_VOC, {}).get(CONF_ALGORITHM_TUNING):
 | 
				
			||||||
        cfg = config[CONF_VOC][CONF_ALGORITHM_TUNING]
 | 
					 | 
				
			||||||
        cg.add(
 | 
					        cg.add(
 | 
				
			||||||
            var.set_voc_algorithm_tuning(
 | 
					            var.set_voc_algorithm_tuning(
 | 
				
			||||||
                cfg[CONF_INDEX_OFFSET],
 | 
					                cfg[CONF_INDEX_OFFSET],
 | 
				
			||||||
@@ -214,8 +238,7 @@ async def to_code(config):
 | 
				
			|||||||
                cfg[CONF_GAIN_FACTOR],
 | 
					                cfg[CONF_GAIN_FACTOR],
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    if CONF_NOX in config and CONF_ALGORITHM_TUNING in config[CONF_NOX]:
 | 
					    if cfg := config.get(CONF_NOX, {}).get(CONF_ALGORITHM_TUNING):
 | 
				
			||||||
        cfg = config[CONF_NOX][CONF_ALGORITHM_TUNING]
 | 
					 | 
				
			||||||
        cg.add(
 | 
					        cg.add(
 | 
				
			||||||
            var.set_nox_algorithm_tuning(
 | 
					            var.set_nox_algorithm_tuning(
 | 
				
			||||||
                cfg[CONF_INDEX_OFFSET],
 | 
					                cfg[CONF_INDEX_OFFSET],
 | 
				
			||||||
@@ -225,12 +248,12 @@ async def to_code(config):
 | 
				
			|||||||
                cfg[CONF_GAIN_FACTOR],
 | 
					                cfg[CONF_GAIN_FACTOR],
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    if CONF_TEMPERATURE_COMPENSATION in config:
 | 
					    if cfg := config.get(CONF_TEMPERATURE_COMPENSATION):
 | 
				
			||||||
        cg.add(
 | 
					        cg.add(
 | 
				
			||||||
            var.set_temperature_compensation(
 | 
					            var.set_temperature_compensation(
 | 
				
			||||||
                config[CONF_TEMPERATURE_COMPENSATION][CONF_OFFSET],
 | 
					                cfg[CONF_OFFSET],
 | 
				
			||||||
                config[CONF_TEMPERATURE_COMPENSATION][CONF_NORMALIZED_OFFSET_SLOPE],
 | 
					                cfg[CONF_NORMALIZED_OFFSET_SLOPE],
 | 
				
			||||||
                config[CONF_TEMPERATURE_COMPENSATION][CONF_TIME_CONSTANT],
 | 
					                cfg[CONF_TIME_CONSTANT],
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,8 +51,7 @@ void HOT I2CST7567::write_display_data() {
 | 
				
			|||||||
    static const size_t BLOCK_SIZE = 64;
 | 
					    static const size_t BLOCK_SIZE = 64;
 | 
				
			||||||
    for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x += BLOCK_SIZE) {
 | 
					    for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x += BLOCK_SIZE) {
 | 
				
			||||||
      this->write_register(esphome::st7567_base::ST7567_SET_START_LINE, &buffer_[y * this->get_width_internal() + x],
 | 
					      this->write_register(esphome::st7567_base::ST7567_SET_START_LINE, &buffer_[y * this->get_width_internal() + x],
 | 
				
			||||||
                           this->get_width_internal() - x > BLOCK_SIZE ? BLOCK_SIZE : this->get_width_internal() - x,
 | 
					                           this->get_width_internal() - x > BLOCK_SIZE ? BLOCK_SIZE : this->get_width_internal() - x);
 | 
				
			||||||
                           true);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,23 +6,15 @@ namespace tca9548a {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static const char *const TAG = "tca9548a";
 | 
					static const char *const TAG = "tca9548a";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
i2c::ErrorCode TCA9548AChannel::readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) {
 | 
					i2c::ErrorCode TCA9548AChannel::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count,
 | 
				
			||||||
 | 
					                                            uint8_t *read_buffer, size_t read_count) {
 | 
				
			||||||
  auto err = this->parent_->switch_to_channel(channel_);
 | 
					  auto err = this->parent_->switch_to_channel(channel_);
 | 
				
			||||||
  if (err != i2c::ERROR_OK)
 | 
					  if (err != i2c::ERROR_OK)
 | 
				
			||||||
    return err;
 | 
					    return err;
 | 
				
			||||||
  err = this->parent_->bus_->readv(address, buffers, cnt);
 | 
					  err = this->parent_->bus_->write_readv(address, write_buffer, write_count, read_buffer, read_count);
 | 
				
			||||||
  this->parent_->disable_all_channels();
 | 
					  this->parent_->disable_all_channels();
 | 
				
			||||||
  return err;
 | 
					  return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
i2c::ErrorCode TCA9548AChannel::writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt, bool stop) {
 | 
					 | 
				
			||||||
  auto err = this->parent_->switch_to_channel(channel_);
 | 
					 | 
				
			||||||
  if (err != i2c::ERROR_OK)
 | 
					 | 
				
			||||||
    return err;
 | 
					 | 
				
			||||||
  err = this->parent_->bus_->writev(address, buffers, cnt, stop);
 | 
					 | 
				
			||||||
  this->parent_->disable_all_channels();
 | 
					 | 
				
			||||||
  return err;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void TCA9548AComponent::setup() {
 | 
					void TCA9548AComponent::setup() {
 | 
				
			||||||
  uint8_t status = 0;
 | 
					  uint8_t status = 0;
 | 
				
			||||||
  if (this->read(&status, 1) != i2c::ERROR_OK) {
 | 
					  if (this->read(&status, 1) != i2c::ERROR_OK) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,8 +14,8 @@ class TCA9548AChannel : public i2c::I2CBus {
 | 
				
			|||||||
  void set_channel(uint8_t channel) { channel_ = channel; }
 | 
					  void set_channel(uint8_t channel) { channel_ = channel; }
 | 
				
			||||||
  void set_parent(TCA9548AComponent *parent) { parent_ = parent; }
 | 
					  void set_parent(TCA9548AComponent *parent) { parent_ = parent; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  i2c::ErrorCode readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) override;
 | 
					  i2c::ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer,
 | 
				
			||||||
  i2c::ErrorCode writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt, bool stop) override;
 | 
					                             size_t read_count) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  uint8_t channel_;
 | 
					  uint8_t channel_;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,9 +9,9 @@ static const char *const TAG = "tee501";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void TEE501Component::setup() {
 | 
					void TEE501Component::setup() {
 | 
				
			||||||
  uint8_t address[] = {0x70, 0x29};
 | 
					  uint8_t address[] = {0x70, 0x29};
 | 
				
			||||||
  this->write(address, 2, false);
 | 
					 | 
				
			||||||
  uint8_t identification[9];
 | 
					  uint8_t identification[9];
 | 
				
			||||||
  this->read(identification, 9);
 | 
					  this->read(identification, 9);
 | 
				
			||||||
 | 
					  this->write_read(address, sizeof address, identification, sizeof identification);
 | 
				
			||||||
  if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) {
 | 
					  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();
 | 
				
			||||||
@@ -41,7 +41,7 @@ void TEE501Component::dump_config() {
 | 
				
			|||||||
float TEE501Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
					float TEE501Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
				
			||||||
void TEE501Component::update() {
 | 
					void TEE501Component::update() {
 | 
				
			||||||
  uint8_t address_1[] = {0x2C, 0x1B};
 | 
					  uint8_t address_1[] = {0x2C, 0x1B};
 | 
				
			||||||
  this->write(address_1, 2, true);
 | 
					  this->write(address_1, 2);
 | 
				
			||||||
  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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,7 +74,8 @@ void TLC59208FOutput::setup() {
 | 
				
			|||||||
  ESP_LOGV(TAG, "  Resetting all devices on the bus");
 | 
					  ESP_LOGV(TAG, "  Resetting all devices on the bus");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Reset all devices on the bus
 | 
					  // Reset all devices on the bus
 | 
				
			||||||
  if (this->bus_->write(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, 2) != i2c::ERROR_OK) {
 | 
					  if (this->bus_->write_readv(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, sizeof TLC59208F_SWRST_SEQ, nullptr, 0) !=
 | 
				
			||||||
 | 
					      i2c::ERROR_OK) {
 | 
				
			||||||
    ESP_LOGE(TAG, "RESET failed");
 | 
					    ESP_LOGE(TAG, "RESET failed");
 | 
				
			||||||
    this->mark_failed();
 | 
					    this->mark_failed();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,14 +14,12 @@ void VEML3235Sensor::setup() {
 | 
				
			|||||||
    this->mark_failed();
 | 
					    this->mark_failed();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if ((this->write(&ID_REG, 1, false) != i2c::ERROR_OK) || !this->read_bytes_raw(device_id, 2)) {
 | 
					  if ((this->read_register(ID_REG, device_id, sizeof device_id) != i2c::ERROR_OK)) {
 | 
				
			||||||
    ESP_LOGE(TAG, "Unable to read ID");
 | 
					    ESP_LOGE(TAG, "Unable to read ID");
 | 
				
			||||||
    this->mark_failed();
 | 
					    this->mark_failed();
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  } else if (device_id[0] != DEVICE_ID) {
 | 
					  } else if (device_id[0] != DEVICE_ID) {
 | 
				
			||||||
    ESP_LOGE(TAG, "Incorrect device ID - expected 0x%.2x, read 0x%.2x", DEVICE_ID, device_id[0]);
 | 
					    ESP_LOGE(TAG, "Incorrect device ID - expected 0x%.2x, read 0x%.2x", DEVICE_ID, device_id[0]);
 | 
				
			||||||
    this->mark_failed();
 | 
					    this->mark_failed();
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -49,7 +47,7 @@ float VEML3235Sensor::read_lx_() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint8_t als_regs[] = {0, 0};
 | 
					  uint8_t als_regs[] = {0, 0};
 | 
				
			||||||
  if ((this->write(&ALS_REG, 1, false) != i2c::ERROR_OK) || !this->read_bytes_raw(als_regs, 2)) {
 | 
					  if ((this->read_register(ALS_REG, als_regs, sizeof als_regs) != i2c::ERROR_OK)) {
 | 
				
			||||||
    this->status_set_warning();
 | 
					    this->status_set_warning();
 | 
				
			||||||
    return NAN;
 | 
					    return NAN;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -279,20 +279,18 @@ ErrorCode VEML7700Component::reconfigure_time_and_gain_(IntegrationTime time, Ga
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ErrorCode VEML7700Component::read_sensor_output_(Readings &data) {
 | 
					ErrorCode VEML7700Component::read_sensor_output_(Readings &data) {
 | 
				
			||||||
  auto als_err =
 | 
					  auto als_err = this->read_register((uint8_t) CommandRegisters::ALS, (uint8_t *) &data.als_counts, VEML_REG_SIZE);
 | 
				
			||||||
      this->read_register((uint8_t) CommandRegisters::ALS, (uint8_t *) &data.als_counts, VEML_REG_SIZE, false);
 | 
					 | 
				
			||||||
  if (als_err != i2c::ERROR_OK) {
 | 
					  if (als_err != i2c::ERROR_OK) {
 | 
				
			||||||
    ESP_LOGW(TAG, "Error reading ALS register, err = %d", als_err);
 | 
					    ESP_LOGW(TAG, "Error reading ALS register, err = %d", als_err);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  auto white_err =
 | 
					  auto white_err =
 | 
				
			||||||
      this->read_register((uint8_t) CommandRegisters::WHITE, (uint8_t *) &data.white_counts, VEML_REG_SIZE, false);
 | 
					      this->read_register((uint8_t) CommandRegisters::WHITE, (uint8_t *) &data.white_counts, VEML_REG_SIZE);
 | 
				
			||||||
  if (white_err != i2c::ERROR_OK) {
 | 
					  if (white_err != i2c::ERROR_OK) {
 | 
				
			||||||
    ESP_LOGW(TAG, "Error reading WHITE register, err = %d", white_err);
 | 
					    ESP_LOGW(TAG, "Error reading WHITE register, err = %d", white_err);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ConfigurationRegister conf{0};
 | 
					  ConfigurationRegister conf{0};
 | 
				
			||||||
  auto err =
 | 
					  auto err = this->read_register((uint8_t) CommandRegisters::ALS_CONF_0, (uint8_t *) conf.raw_bytes, VEML_REG_SIZE);
 | 
				
			||||||
      this->read_register((uint8_t) CommandRegisters::ALS_CONF_0, (uint8_t *) conf.raw_bytes, VEML_REG_SIZE, false);
 | 
					 | 
				
			||||||
  if (err != i2c::ERROR_OK) {
 | 
					  if (err != i2c::ERROR_OK) {
 | 
				
			||||||
    ESP_LOGW(TAG, "Error reading ALS_CONF_0 register, err = %d", white_err);
 | 
					    ESP_LOGW(TAG, "Error reading ALS_CONF_0 register, err = %d", white_err);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,6 @@
 | 
				
			|||||||
#include "esphome/components/i2c/i2c.h"
 | 
					#include "esphome/components/i2c/i2c.h"
 | 
				
			||||||
#include "esphome/components/sensor/sensor.h"
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
#include "esphome/core/component.h"
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
#include "esphome/core/optional.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace veml7700 {
 | 
					namespace veml7700 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,6 +151,8 @@ void WiFiComponent::loop() {
 | 
				
			|||||||
        this->status_set_warning("waiting to reconnect");
 | 
					        this->status_set_warning("waiting to reconnect");
 | 
				
			||||||
        if (millis() - this->action_started_ > 5000) {
 | 
					        if (millis() - this->action_started_ > 5000) {
 | 
				
			||||||
          if (this->fast_connect_ || this->retry_hidden_) {
 | 
					          if (this->fast_connect_ || this->retry_hidden_) {
 | 
				
			||||||
 | 
					            if (!this->selected_ap_.get_bssid().has_value())
 | 
				
			||||||
 | 
					              this->selected_ap_ = this->sta_[0];
 | 
				
			||||||
            this->start_connecting(this->selected_ap_, false);
 | 
					            this->start_connecting(this->selected_ap_, false);
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            this->start_scanning();
 | 
					            this->start_scanning();
 | 
				
			||||||
@@ -670,10 +672,12 @@ void WiFiComponent::check_connecting_finished() {
 | 
				
			|||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ESP_LOGI(TAG, "Connected");
 | 
				
			||||||
    // We won't retry hidden networks unless a reconnect fails more than three times again
 | 
					    // We won't retry hidden networks unless a reconnect fails more than three times again
 | 
				
			||||||
 | 
					    if (this->retry_hidden_ && !this->selected_ap_.get_hidden())
 | 
				
			||||||
 | 
					      ESP_LOGW(TAG, "Network '%s' should be marked as hidden", this->selected_ap_.get_ssid().c_str());
 | 
				
			||||||
    this->retry_hidden_ = false;
 | 
					    this->retry_hidden_ = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ESP_LOGI(TAG, "Connected");
 | 
					 | 
				
			||||||
    this->print_connect_params_();
 | 
					    this->print_connect_params_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this->has_ap()) {
 | 
					    if (this->has_ap()) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ pyserial==3.5
 | 
				
			|||||||
platformio==6.1.18  # When updating platformio, also update /docker/Dockerfile
 | 
					platformio==6.1.18  # When updating platformio, also update /docker/Dockerfile
 | 
				
			||||||
esptool==5.0.2
 | 
					esptool==5.0.2
 | 
				
			||||||
click==8.1.7
 | 
					click==8.1.7
 | 
				
			||||||
esphome-dashboard==20250814.0
 | 
					esphome-dashboard==20250828.0
 | 
				
			||||||
aioesphomeapi==39.0.0
 | 
					aioesphomeapi==39.0.0
 | 
				
			||||||
zeroconf==0.147.0
 | 
					zeroconf==0.147.0
 | 
				
			||||||
puremagic==1.30
 | 
					puremagic==1.30
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
pylint==3.3.8
 | 
					pylint==3.3.8
 | 
				
			||||||
flake8==7.3.0  # also change in .pre-commit-config.yaml when updating
 | 
					flake8==7.3.0  # also change in .pre-commit-config.yaml when updating
 | 
				
			||||||
ruff==0.12.10  # also change in .pre-commit-config.yaml when updating
 | 
					ruff==0.12.11  # also change in .pre-commit-config.yaml when updating
 | 
				
			||||||
pyupgrade==3.20.0  # also change in .pre-commit-config.yaml when updating
 | 
					pyupgrade==3.20.0  # also change in .pre-commit-config.yaml when updating
 | 
				
			||||||
pre-commit
 | 
					pre-commit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -684,7 +684,7 @@ lvgl:
 | 
				
			|||||||
            width: 120
 | 
					            width: 120
 | 
				
			||||||
            range_from: -10
 | 
					            range_from: -10
 | 
				
			||||||
            range_to: 1000
 | 
					            range_to: 1000
 | 
				
			||||||
            step: 5.0
 | 
					            selected_digit: 2
 | 
				
			||||||
            rollover: false
 | 
					            rollover: false
 | 
				
			||||||
            digits: 6
 | 
					            digits: 6
 | 
				
			||||||
            decimal_places: 2
 | 
					            decimal_places: 2
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								tests/components/mapping/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/components/mapping/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					*.ttf        -text
 | 
				
			||||||
@@ -50,6 +50,14 @@ mapping:
 | 
				
			|||||||
      red: red_id
 | 
					      red: red_id
 | 
				
			||||||
      blue: blue_id
 | 
					      blue: blue_id
 | 
				
			||||||
      green: green_id
 | 
					      green: green_id
 | 
				
			||||||
 | 
					  - id: string_map_2
 | 
				
			||||||
 | 
					    from: string
 | 
				
			||||||
 | 
					    to: string
 | 
				
			||||||
 | 
					    entries:
 | 
				
			||||||
 | 
					      one: "one"
 | 
				
			||||||
 | 
					      two: "two"
 | 
				
			||||||
 | 
					      three: "three"
 | 
				
			||||||
 | 
					      seventy-seven: "seventy-seven"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
color:
 | 
					color:
 | 
				
			||||||
  - id: red_id
 | 
					  - id: red_id
 | 
				
			||||||
@@ -65,7 +73,14 @@ color:
 | 
				
			|||||||
    green: 0.0
 | 
					    green: 0.0
 | 
				
			||||||
    blue: 1.0
 | 
					    blue: 1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					font:
 | 
				
			||||||
 | 
					  - file: "$component_dir/helvetica.ttf"
 | 
				
			||||||
 | 
					    id: font_id
 | 
				
			||||||
 | 
					    size: 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
display:
 | 
					display:
 | 
				
			||||||
  lambda: |-
 | 
					  lambda: |-
 | 
				
			||||||
      it.image(0, 0, id(weather_map)[0]);
 | 
					      std::string value = id(int_map)[2];
 | 
				
			||||||
      it.image(0, 100, id(weather_map)[1]);
 | 
					      it.print(0, 0, id(font_id), TextAlign::TOP_LEFT, value.c_str());
 | 
				
			||||||
 | 
					      it.image(0, 0, id(weather_map)["clear-night"]);
 | 
				
			||||||
 | 
					      it.image(0, 100, id(weather_map)["sunny"]);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								tests/components/mapping/helvetica.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/components/mapping/helvetica.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -4,14 +4,14 @@ spi:
 | 
				
			|||||||
    mosi_pin: 17
 | 
					    mosi_pin: 17
 | 
				
			||||||
    miso_pin: 15
 | 
					    miso_pin: 15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
display:
 | 
					 | 
				
			||||||
  - platform: ili9xxx
 | 
					 | 
				
			||||||
    id: main_lcd
 | 
					 | 
				
			||||||
    model: ili9342
 | 
					 | 
				
			||||||
    cs_pin: 12
 | 
					 | 
				
			||||||
    dc_pin: 13
 | 
					 | 
				
			||||||
    reset_pin: 21
 | 
					 | 
				
			||||||
    invert_colors: false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
packages:
 | 
					packages:
 | 
				
			||||||
  map: !include common.yaml
 | 
					  map: !include common.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					display:
 | 
				
			||||||
 | 
					  platform: ili9xxx
 | 
				
			||||||
 | 
					  id: main_lcd
 | 
				
			||||||
 | 
					  model: ili9342
 | 
				
			||||||
 | 
					  cs_pin: 12
 | 
				
			||||||
 | 
					  dc_pin: 13
 | 
				
			||||||
 | 
					  reset_pin: 21
 | 
				
			||||||
 | 
					  invert_colors: false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,13 @@ spi:
 | 
				
			|||||||
    miso_pin: 5
 | 
					    miso_pin: 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
display:
 | 
					display:
 | 
				
			||||||
  - platform: ili9xxx
 | 
					  platform: ili9xxx
 | 
				
			||||||
    id: main_lcd
 | 
					  id: main_lcd
 | 
				
			||||||
    model: ili9342
 | 
					  model: ili9342
 | 
				
			||||||
    cs_pin: 8
 | 
					  cs_pin: 8
 | 
				
			||||||
    dc_pin: 9
 | 
					  dc_pin: 9
 | 
				
			||||||
    reset_pin: 10
 | 
					  reset_pin: 10
 | 
				
			||||||
    invert_colors: false
 | 
					  invert_colors: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
packages:
 | 
					packages:
 | 
				
			||||||
  map: !include common.yaml
 | 
					  map: !include common.yaml
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,13 @@ spi:
 | 
				
			|||||||
    miso_pin: 5
 | 
					    miso_pin: 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
display:
 | 
					display:
 | 
				
			||||||
  - platform: ili9xxx
 | 
					  platform: ili9xxx
 | 
				
			||||||
    id: main_lcd
 | 
					  id: main_lcd
 | 
				
			||||||
    model: ili9342
 | 
					  model: ili9342
 | 
				
			||||||
    cs_pin: 8
 | 
					  cs_pin: 8
 | 
				
			||||||
    dc_pin: 9
 | 
					  dc_pin: 9
 | 
				
			||||||
    reset_pin: 10
 | 
					  reset_pin: 10
 | 
				
			||||||
    invert_colors: false
 | 
					  invert_colors: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
packages:
 | 
					packages:
 | 
				
			||||||
  map: !include common.yaml
 | 
					  map: !include common.yaml
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,13 @@ spi:
 | 
				
			|||||||
    miso_pin: 15
 | 
					    miso_pin: 15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
display:
 | 
					display:
 | 
				
			||||||
  - platform: ili9xxx
 | 
					  platform: ili9xxx
 | 
				
			||||||
    id: main_lcd
 | 
					  id: main_lcd
 | 
				
			||||||
    model: ili9342
 | 
					  model: ili9342
 | 
				
			||||||
    cs_pin: 12
 | 
					  cs_pin: 12
 | 
				
			||||||
    dc_pin: 13
 | 
					  dc_pin: 13
 | 
				
			||||||
    reset_pin: 21
 | 
					  reset_pin: 21
 | 
				
			||||||
    invert_colors: false
 | 
					  invert_colors: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
packages:
 | 
					packages:
 | 
				
			||||||
  map: !include common.yaml
 | 
					  map: !include common.yaml
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,13 @@ spi:
 | 
				
			|||||||
    miso_pin: 12
 | 
					    miso_pin: 12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
display:
 | 
					display:
 | 
				
			||||||
  - platform: ili9xxx
 | 
					  platform: ili9xxx
 | 
				
			||||||
    id: main_lcd
 | 
					  id: main_lcd
 | 
				
			||||||
    model: ili9342
 | 
					  model: ili9342
 | 
				
			||||||
    cs_pin: 5
 | 
					  cs_pin: 5
 | 
				
			||||||
    dc_pin: 15
 | 
					  dc_pin: 15
 | 
				
			||||||
    reset_pin: 16
 | 
					  reset_pin: 16
 | 
				
			||||||
    invert_colors: false
 | 
					  invert_colors: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
packages:
 | 
					packages:
 | 
				
			||||||
  map: !include common.yaml
 | 
					  map: !include common.yaml
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
display:
 | 
					display:
 | 
				
			||||||
  - platform: sdl
 | 
					  platform: sdl
 | 
				
			||||||
    id: sdl_display
 | 
					  id: sdl_display
 | 
				
			||||||
    update_interval: 1s
 | 
					  update_interval: 1s
 | 
				
			||||||
    auto_clear_enabled: false
 | 
					  auto_clear_enabled: false
 | 
				
			||||||
    show_test_card: true
 | 
					  show_test_card: true
 | 
				
			||||||
    dimensions:
 | 
					  dimensions:
 | 
				
			||||||
      width: 450
 | 
					    width: 450
 | 
				
			||||||
      height: 600
 | 
					    height: 600
 | 
				
			||||||
 | 
					
 | 
				
			||||||
packages:
 | 
					packages:
 | 
				
			||||||
  map: !include common.yaml
 | 
					  map: !include common.yaml
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,13 @@ spi:
 | 
				
			|||||||
    miso_pin: 4
 | 
					    miso_pin: 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
display:
 | 
					display:
 | 
				
			||||||
  - platform: ili9xxx
 | 
					  platform: ili9xxx
 | 
				
			||||||
    id: main_lcd
 | 
					  id: main_lcd
 | 
				
			||||||
    model: ili9342
 | 
					  model: ili9342
 | 
				
			||||||
    cs_pin: 20
 | 
					  cs_pin: 20
 | 
				
			||||||
    dc_pin: 21
 | 
					  dc_pin: 21
 | 
				
			||||||
    reset_pin: 22
 | 
					  reset_pin: 22
 | 
				
			||||||
    invert_colors: false
 | 
					  invert_colors: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
packages:
 | 
					packages:
 | 
				
			||||||
  map: !include common.yaml
 | 
					  map: !include common.yaml
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user