mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	extend scd4x (#3409)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -168,7 +168,7 @@ esphome/components/rf_bridge/* @jesserockz | |||||||
| esphome/components/rgbct/* @jesserockz | esphome/components/rgbct/* @jesserockz | ||||||
| esphome/components/rtttl/* @glmnet | esphome/components/rtttl/* @glmnet | ||||||
| esphome/components/safe_mode/* @jsuanet @paulmonigatti | esphome/components/safe_mode/* @jsuanet @paulmonigatti | ||||||
| esphome/components/scd4x/* @sjtrny | esphome/components/scd4x/* @martgras @sjtrny | ||||||
| esphome/components/script/* @esphome/core | esphome/components/script/* @esphome/core | ||||||
| esphome/components/sdm_meter/* @jesserockz @polyfaces | esphome/components/sdm_meter/* @jesserockz @polyfaces | ||||||
| esphome/components/sdp3x/* @Azimath | esphome/components/sdp3x/* @Azimath | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								esphome/components/scd4x/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/scd4x/automation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/automation.h" | ||||||
|  | #include "scd4x.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace scd4x { | ||||||
|  |  | ||||||
|  | template<typename... Ts> class PerformForcedCalibrationAction : public Action<Ts...>, public Parented<SCD4XComponent> { | ||||||
|  |  public: | ||||||
|  |   void play(Ts... x) override { | ||||||
|  |     if (this->value_.has_value()) { | ||||||
|  |       this->parent_->perform_forced_calibration(value_.value()); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   TEMPLATABLE_VALUE(uint16_t, value) | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class FactoryResetAction : public Action<Ts...>, public Parented<SCD4XComponent> { | ||||||
|  |  public: | ||||||
|  |   void play(Ts... x) override { this->parent_->factory_reset(); } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace scd4x | ||||||
|  | }  // namespace esphome | ||||||
| @@ -13,29 +13,24 @@ static const uint16_t SCD4X_CMD_ALTITUDE_COMPENSATION = 0x2427; | |||||||
| static const uint16_t SCD4X_CMD_AMBIENT_PRESSURE_COMPENSATION = 0xe000; | static const uint16_t SCD4X_CMD_AMBIENT_PRESSURE_COMPENSATION = 0xe000; | ||||||
| static const uint16_t SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION = 0x2416; | static const uint16_t SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION = 0x2416; | ||||||
| static const uint16_t SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS = 0x21b1; | static const uint16_t SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS = 0x21b1; | ||||||
|  | static const uint16_t SCD4X_CMD_START_LOW_POWER_CONTINUOUS_MEASUREMENTS = 0x21ac; | ||||||
|  | static const uint16_t SCD4X_CMD_START_LOW_POWER_SINGLE_SHOT = 0x219d;  // SCD41 only | ||||||
|  | static const uint16_t SCD4X_CMD_START_LOW_POWER_SINGLE_SHOT_RHT_ONLY = 0x2196; | ||||||
| static const uint16_t SCD4X_CMD_GET_DATA_READY_STATUS = 0xe4b8; | static const uint16_t SCD4X_CMD_GET_DATA_READY_STATUS = 0xe4b8; | ||||||
| static const uint16_t SCD4X_CMD_READ_MEASUREMENT = 0xec05; | static const uint16_t SCD4X_CMD_READ_MEASUREMENT = 0xec05; | ||||||
| static const uint16_t SCD4X_CMD_PERFORM_FORCED_CALIBRATION = 0x362f; | static const uint16_t SCD4X_CMD_PERFORM_FORCED_CALIBRATION = 0x362f; | ||||||
| static const uint16_t SCD4X_CMD_STOP_MEASUREMENTS = 0x3f86; | static const uint16_t SCD4X_CMD_STOP_MEASUREMENTS = 0x3f86; | ||||||
|  | static const uint16_t SCD4X_CMD_FACTORY_RESET = 0x3632; | ||||||
|  | static const uint16_t SCD4X_CMD_GET_FEATURESET = 0x202f; | ||||||
| static const float SCD4X_TEMPERATURE_OFFSET_MULTIPLIER = (1 << 16) / 175.0f; | static const float SCD4X_TEMPERATURE_OFFSET_MULTIPLIER = (1 << 16) / 175.0f; | ||||||
|  | static const uint16_t SCD41_ID = 0x1408; | ||||||
|  | static const uint16_t SCD40_ID = 0x440; | ||||||
|  |  | ||||||
| void SCD4XComponent::setup() { | void SCD4XComponent::setup() { | ||||||
|   ESP_LOGCONFIG(TAG, "Setting up scd4x..."); |   ESP_LOGCONFIG(TAG, "Setting up scd4x..."); | ||||||
|  |  | ||||||
|   // the sensor needs 1000 ms to enter the idle state |   // the sensor needs 1000 ms to enter the idle state | ||||||
|   this->set_timeout(1000, [this]() { |   this->set_timeout(1000, [this]() { | ||||||
|     uint16_t raw_read_status; |     this->status_clear_error(); | ||||||
|     if (!this->get_register(SCD4X_CMD_GET_DATA_READY_STATUS, raw_read_status)) { |  | ||||||
|       ESP_LOGE(TAG, "Failed to read data ready status"); |  | ||||||
|       this->mark_failed(); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint32_t stop_measurement_delay = 0; |  | ||||||
|     // In order to query the device periodic measurement must be ceased |  | ||||||
|     if (raw_read_status) { |  | ||||||
|       ESP_LOGD(TAG, "Sensor has data available, stopping periodic measurement"); |  | ||||||
|     if (!this->write_command(SCD4X_CMD_STOP_MEASUREMENTS)) { |     if (!this->write_command(SCD4X_CMD_STOP_MEASUREMENTS)) { | ||||||
|       ESP_LOGE(TAG, "Failed to stop measurements"); |       ESP_LOGE(TAG, "Failed to stop measurements"); | ||||||
|       this->mark_failed(); |       this->mark_failed(); | ||||||
| @@ -43,9 +38,7 @@ void SCD4XComponent::setup() { | |||||||
|     } |     } | ||||||
|     // According to the SCD4x datasheet the sensor will only respond to other commands after waiting 500 ms after |     // According to the SCD4x datasheet the sensor will only respond to other commands after waiting 500 ms after | ||||||
|     // issuing the stop_periodic_measurement command |     // issuing the stop_periodic_measurement command | ||||||
|       stop_measurement_delay = 500; |     this->set_timeout(500, [this]() { | ||||||
|     } |  | ||||||
|     this->set_timeout(stop_measurement_delay, [this]() { |  | ||||||
|       uint16_t raw_serial_number[3]; |       uint16_t raw_serial_number[3]; | ||||||
|       if (!this->get_register(SCD4X_CMD_GET_SERIAL_NUMBER, raw_serial_number, 3, 1)) { |       if (!this->get_register(SCD4X_CMD_GET_SERIAL_NUMBER, raw_serial_number, 3, 1)) { | ||||||
|         ESP_LOGE(TAG, "Failed to read serial number"); |         ESP_LOGE(TAG, "Failed to read serial number"); | ||||||
| @@ -89,15 +82,9 @@ void SCD4XComponent::setup() { | |||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       // Finally start sensor measurements |  | ||||||
|       if (!this->write_command(SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS)) { |  | ||||||
|         ESP_LOGE(TAG, "Error starting continuous measurements."); |  | ||||||
|         this->error_code_ = MEASUREMENT_INIT_FAILED; |  | ||||||
|         this->mark_failed(); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       initialized_ = true; |       initialized_ = true; | ||||||
|  |       // Finally start sensor measurements | ||||||
|  |       this->start_measurement_(); | ||||||
|       ESP_LOGD(TAG, "Sensor initialized"); |       ESP_LOGD(TAG, "Sensor initialized"); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| @@ -123,6 +110,10 @@ void SCD4XComponent::dump_config() { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   ESP_LOGCONFIG(TAG, "  Automatic self calibration: %s", ONOFF(this->enable_asc_)); |   ESP_LOGCONFIG(TAG, "  Automatic self calibration: %s", ONOFF(this->enable_asc_)); | ||||||
|  |   if (this->ambient_pressure_source_ != nullptr) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  Dynamic ambient pressure compensation using sensor '%s'", | ||||||
|  |                   this->ambient_pressure_source_->get_name().c_str()); | ||||||
|  |   } else { | ||||||
|     if (this->ambient_pressure_compensation_) { |     if (this->ambient_pressure_compensation_) { | ||||||
|       ESP_LOGCONFIG(TAG, "  Altitude compensation disabled"); |       ESP_LOGCONFIG(TAG, "  Altitude compensation disabled"); | ||||||
|       ESP_LOGCONFIG(TAG, "  Ambient pressure compensation: %dmBar", this->ambient_pressure_); |       ESP_LOGCONFIG(TAG, "  Ambient pressure compensation: %dmBar", this->ambient_pressure_); | ||||||
| @@ -130,6 +121,21 @@ void SCD4XComponent::dump_config() { | |||||||
|       ESP_LOGCONFIG(TAG, "  Ambient pressure compensation disabled"); |       ESP_LOGCONFIG(TAG, "  Ambient pressure compensation disabled"); | ||||||
|       ESP_LOGCONFIG(TAG, "  Altitude compensation: %dm", this->altitude_compensation_); |       ESP_LOGCONFIG(TAG, "  Altitude compensation: %dm", this->altitude_compensation_); | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |   switch (this->measurement_mode_) { | ||||||
|  |     case PERIODIC: | ||||||
|  |       ESP_LOGCONFIG(TAG, "  Measurement mode: periodic (5s)"); | ||||||
|  |       break; | ||||||
|  |     case LOW_POWER_PERIODIC: | ||||||
|  |       ESP_LOGCONFIG(TAG, "  Measurement mode: low power periodic (30s)"); | ||||||
|  |       break; | ||||||
|  |     case SINGLE_SHOT: | ||||||
|  |       ESP_LOGCONFIG(TAG, "  Measurement mode: single shot"); | ||||||
|  |       break; | ||||||
|  |     case SINGLE_SHOT_RHT_ONLY: | ||||||
|  |       ESP_LOGCONFIG(TAG, "  Measurement mode: single shot rht only"); | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|   ESP_LOGCONFIG(TAG, "  Temperature offset: %.2f °C", this->temperature_offset_); |   ESP_LOGCONFIG(TAG, "  Temperature offset: %.2f °C", this->temperature_offset_); | ||||||
|   LOG_UPDATE_INTERVAL(this); |   LOG_UPDATE_INTERVAL(this); | ||||||
|   LOG_SENSOR("  ", "CO2", this->co2_sensor_); |   LOG_SENSOR("  ", "CO2", this->co2_sensor_); | ||||||
| @@ -149,6 +155,13 @@ void SCD4XComponent::update() { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   uint32_t wait_time = 0; | ||||||
|  |   if (this->measurement_mode_ == SINGLE_SHOT || this->measurement_mode_ == SINGLE_SHOT_RHT_ONLY) { | ||||||
|  |     start_measurement_(); | ||||||
|  |     wait_time = | ||||||
|  |         this->measurement_mode_ == SINGLE_SHOT ? 5000 : 50;  // Single shot measurement takes 5 secs rht mode 50 ms | ||||||
|  |   } | ||||||
|  |   this->set_timeout(wait_time, [this]() { | ||||||
|     // Check if data is ready |     // Check if data is ready | ||||||
|     if (!this->write_command(SCD4X_CMD_GET_DATA_READY_STATUS)) { |     if (!this->write_command(SCD4X_CMD_GET_DATA_READY_STATUS)) { | ||||||
|       this->status_set_warning(); |       this->status_set_warning(); | ||||||
| @@ -156,6 +169,7 @@ void SCD4XComponent::update() { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint16_t raw_read_status; |     uint16_t raw_read_status; | ||||||
|  |  | ||||||
|     if (!this->read_data(raw_read_status) || raw_read_status == 0x00) { |     if (!this->read_data(raw_read_status) || raw_read_status == 0x00) { | ||||||
|       this->status_set_warning(); |       this->status_set_warning(); | ||||||
|       ESP_LOGW(TAG, "Data not ready yet!"); |       ESP_LOGW(TAG, "Data not ready yet!"); | ||||||
| @@ -165,16 +179,14 @@ void SCD4XComponent::update() { | |||||||
|     if (!this->write_command(SCD4X_CMD_READ_MEASUREMENT)) { |     if (!this->write_command(SCD4X_CMD_READ_MEASUREMENT)) { | ||||||
|       ESP_LOGW(TAG, "Error reading measurement!"); |       ESP_LOGW(TAG, "Error reading measurement!"); | ||||||
|       this->status_set_warning(); |       this->status_set_warning(); | ||||||
|     return; |       return;  // NO RETRY | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Read off sensor data |     // Read off sensor data | ||||||
|     uint16_t raw_data[3]; |     uint16_t raw_data[3]; | ||||||
|     if (!this->read_data(raw_data, 3)) { |     if (!this->read_data(raw_data, 3)) { | ||||||
|       this->status_set_warning(); |       this->status_set_warning(); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (this->co2_sensor_ != nullptr) |     if (this->co2_sensor_ != nullptr) | ||||||
|       this->co2_sensor_->publish_state(raw_data[0]); |       this->co2_sensor_->publish_state(raw_data[0]); | ||||||
|  |  | ||||||
| @@ -182,14 +194,66 @@ void SCD4XComponent::update() { | |||||||
|       const float temperature = -45.0f + (175.0f * (raw_data[1])) / (1 << 16); |       const float temperature = -45.0f + (175.0f * (raw_data[1])) / (1 << 16); | ||||||
|       this->temperature_sensor_->publish_state(temperature); |       this->temperature_sensor_->publish_state(temperature); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (this->humidity_sensor_ != nullptr) { |     if (this->humidity_sensor_ != nullptr) { | ||||||
|       const float humidity = (100.0f * raw_data[2]) / (1 << 16); |       const float humidity = (100.0f * raw_data[2]) / (1 << 16); | ||||||
|       this->humidity_sensor_->publish_state(humidity); |       this->humidity_sensor_->publish_state(humidity); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     this->status_clear_warning(); |     this->status_clear_warning(); | ||||||
|  |   });  // set_timeout | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool SCD4XComponent::perform_forced_calibration(uint16_t current_co2_concentration) { | ||||||
|  |   /* | ||||||
|  |     Operate the SCD4x in the operation mode later used in normal sensor operation (periodic measurement, low power | ||||||
|  |     periodic measurement or single shot) for > 3 minutes in an environment with homogenous and constant CO2 | ||||||
|  |     concentration before performing a forced recalibration. | ||||||
|  |   */ | ||||||
|  |   if (!this->write_command(SCD4X_CMD_STOP_MEASUREMENTS)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to stop measurements"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |   } | ||||||
|  |   this->set_timeout(500, [this, current_co2_concentration]() { | ||||||
|  |     if (this->write_command(SCD4X_CMD_PERFORM_FORCED_CALIBRATION, current_co2_concentration)) { | ||||||
|  |       ESP_LOGD(TAG, "setting forced calibration Co2 level %d ppm", current_co2_concentration); | ||||||
|  |       // frc takes 400 ms | ||||||
|  |       // because this method will be used very rarly | ||||||
|  |       // the simple aproach with delay is ok | ||||||
|  |       delay(400);  // NOLINT' | ||||||
|  |       if (!this->start_measurement_()) { | ||||||
|  |         return false; | ||||||
|  |       } else { | ||||||
|  |         ESP_LOGD(TAG, "forced calibration complete"); | ||||||
|  |       } | ||||||
|  |       return true; | ||||||
|  |     } else { | ||||||
|  |       ESP_LOGE(TAG, "force calibration failed"); | ||||||
|  |       this->error_code_ = FRC_FAILED; | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool SCD4XComponent::factory_reset() { | ||||||
|  |   if (!this->write_command(SCD4X_CMD_STOP_MEASUREMENTS)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to stop measurements"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->set_timeout(500, [this]() { | ||||||
|  |     if (!this->write_command(SCD4X_CMD_FACTORY_RESET)) { | ||||||
|  |       ESP_LOGE(TAG, "Failed to send factory reset command"); | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     ESP_LOGD(TAG, "Factory reset complete"); | ||||||
|  |     return true; | ||||||
|  |   }); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| // Note pressure in bar here. Convert to hPa | // Note pressure in bar here. Convert to hPa | ||||||
| void SCD4XComponent::set_ambient_pressure_compensation(float pressure_in_bar) { | void SCD4XComponent::set_ambient_pressure_compensation(float pressure_in_bar) { | ||||||
|   ambient_pressure_compensation_ = true; |   ambient_pressure_compensation_ = true; | ||||||
| @@ -213,5 +277,38 @@ bool SCD4XComponent::update_ambient_pressure_compensation_(uint16_t pressure_in_ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool SCD4XComponent::start_measurement_() { | ||||||
|  |   uint16_t measurement_command = SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS; | ||||||
|  |   switch (this->measurement_mode_) { | ||||||
|  |     case PERIODIC: | ||||||
|  |       measurement_command = SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS; | ||||||
|  |       break; | ||||||
|  |     case LOW_POWER_PERIODIC: | ||||||
|  |       measurement_command = SCD4X_CMD_START_LOW_POWER_CONTINUOUS_MEASUREMENTS; | ||||||
|  |       break; | ||||||
|  |     case SINGLE_SHOT: | ||||||
|  |       measurement_command = SCD4X_CMD_START_LOW_POWER_SINGLE_SHOT; | ||||||
|  |       break; | ||||||
|  |     case SINGLE_SHOT_RHT_ONLY: | ||||||
|  |       measurement_command = SCD4X_CMD_START_LOW_POWER_SINGLE_SHOT_RHT_ONLY; | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static uint8_t remaining_retries = 3; | ||||||
|  |   while (remaining_retries) { | ||||||
|  |     if (!this->write_command(measurement_command)) { | ||||||
|  |       ESP_LOGE(TAG, "Error starting measurements."); | ||||||
|  |       this->error_code_ = MEASUREMENT_INIT_FAILED; | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       if (--remaining_retries == 0) | ||||||
|  |         return false; | ||||||
|  |       delay(50);  // NOLINT wait 50 ms and try again | ||||||
|  |     } | ||||||
|  |     this->status_clear_warning(); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
| }  // namespace scd4x | }  // namespace scd4x | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  | #include <vector> | ||||||
|  | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
| #include "esphome/components/sensirion_common/i2c_sensirion.h" | #include "esphome/components/sensirion_common/i2c_sensirion.h" | ||||||
| @@ -7,7 +8,14 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace scd4x { | namespace scd4x { | ||||||
|  |  | ||||||
| enum ERRORCODE { COMMUNICATION_FAILED, SERIAL_NUMBER_IDENTIFICATION_FAILED, MEASUREMENT_INIT_FAILED, UNKNOWN }; | enum ERRORCODE { | ||||||
|  |   COMMUNICATION_FAILED, | ||||||
|  |   SERIAL_NUMBER_IDENTIFICATION_FAILED, | ||||||
|  |   MEASUREMENT_INIT_FAILED, | ||||||
|  |   FRC_FAILED, | ||||||
|  |   UNKNOWN | ||||||
|  | }; | ||||||
|  | enum MeasurementMode { PERIODIC, LOW_POWER_PERIODIC, SINGLE_SHOT, SINGLE_SHOT_RHT_ONLY }; | ||||||
|  |  | ||||||
| class SCD4XComponent : public PollingComponent, public sensirion_common::SensirionI2CDevice { | class SCD4XComponent : public PollingComponent, public sensirion_common::SensirionI2CDevice { | ||||||
|  public: |  public: | ||||||
| @@ -25,10 +33,13 @@ class SCD4XComponent : public PollingComponent, public sensirion_common::Sensiri | |||||||
|   void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; } |   void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; } | ||||||
|   void set_temperature_sensor(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }; |   void set_temperature_sensor(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }; | ||||||
|   void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } |   void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } | ||||||
|  |   void set_measurement_mode(MeasurementMode mode) { measurement_mode_ = mode; } | ||||||
|  |   bool perform_forced_calibration(uint16_t current_co2_concentration); | ||||||
|  |   bool factory_reset(); | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   bool update_ambient_pressure_compensation_(uint16_t pressure_in_hpa); |   bool update_ambient_pressure_compensation_(uint16_t pressure_in_hpa); | ||||||
|  |   bool start_measurement_(); | ||||||
|   ERRORCODE error_code_; |   ERRORCODE error_code_; | ||||||
|  |  | ||||||
|   bool initialized_{false}; |   bool initialized_{false}; | ||||||
| @@ -38,7 +49,7 @@ class SCD4XComponent : public PollingComponent, public sensirion_common::Sensiri | |||||||
|   bool ambient_pressure_compensation_; |   bool ambient_pressure_compensation_; | ||||||
|   uint16_t ambient_pressure_; |   uint16_t ambient_pressure_; | ||||||
|   bool enable_asc_; |   bool enable_asc_; | ||||||
|  |   MeasurementMode measurement_mode_{PERIODIC}; | ||||||
|   sensor::Sensor *co2_sensor_{nullptr}; |   sensor::Sensor *co2_sensor_{nullptr}; | ||||||
|   sensor::Sensor *temperature_sensor_{nullptr}; |   sensor::Sensor *temperature_sensor_{nullptr}; | ||||||
|   sensor::Sensor *humidity_sensor_{nullptr}; |   sensor::Sensor *humidity_sensor_{nullptr}; | ||||||
|   | |||||||
| @@ -2,11 +2,15 @@ import esphome.codegen as cg | |||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.components import i2c, sensor | from esphome.components import i2c, sensor | ||||||
| from esphome.components import sensirion_common | from esphome.components import sensirion_common | ||||||
|  | from esphome import automation | ||||||
|  | from esphome.automation import maybe_simple_id | ||||||
|  |  | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|     CONF_ID, |     CONF_ID, | ||||||
|     CONF_CO2, |     CONF_CO2, | ||||||
|     CONF_HUMIDITY, |     CONF_HUMIDITY, | ||||||
|     CONF_TEMPERATURE, |     CONF_TEMPERATURE, | ||||||
|  |     CONF_VALUE, | ||||||
|     DEVICE_CLASS_CARBON_DIOXIDE, |     DEVICE_CLASS_CARBON_DIOXIDE, | ||||||
|     DEVICE_CLASS_HUMIDITY, |     DEVICE_CLASS_HUMIDITY, | ||||||
|     DEVICE_CLASS_TEMPERATURE, |     DEVICE_CLASS_TEMPERATURE, | ||||||
| @@ -19,7 +23,7 @@ from esphome.const import ( | |||||||
|     UNIT_PERCENT, |     UNIT_PERCENT, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| CODEOWNERS = ["@sjtrny"] | CODEOWNERS = ["@sjtrny", "@martgras"] | ||||||
| DEPENDENCIES = ["i2c"] | DEPENDENCIES = ["i2c"] | ||||||
| AUTO_LOAD = ["sensirion_common"] | AUTO_LOAD = ["sensirion_common"] | ||||||
|  |  | ||||||
| @@ -27,12 +31,29 @@ scd4x_ns = cg.esphome_ns.namespace("scd4x") | |||||||
| SCD4XComponent = scd4x_ns.class_( | SCD4XComponent = scd4x_ns.class_( | ||||||
|     "SCD4XComponent", cg.PollingComponent, sensirion_common.SensirionI2CDevice |     "SCD4XComponent", cg.PollingComponent, sensirion_common.SensirionI2CDevice | ||||||
| ) | ) | ||||||
|  | MeasurementMode = scd4x_ns.enum("MEASUREMENT_MODE") | ||||||
|  | MEASUREMENT_MODE_OPTIONS = { | ||||||
|  |     "periodic": MeasurementMode.PERIODIC, | ||||||
|  |     "low_power_periodic": MeasurementMode.LOW_POWER_PERIODIC, | ||||||
|  |     "single_shot": MeasurementMode.SINGLE_SHOT, | ||||||
|  |     "single_shot_rht_only": MeasurementMode.SINGLE_SHOT_RHT_ONLY, | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Actions | ||||||
|  | PerformForcedCalibrationAction = scd4x_ns.class_( | ||||||
|  |     "PerformForcedCalibrationAction", automation.Action | ||||||
|  | ) | ||||||
|  | FactoryResetAction = scd4x_ns.class_("FactoryResetAction", automation.Action) | ||||||
|  |  | ||||||
|  |  | ||||||
| CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration" |  | ||||||
| CONF_ALTITUDE_COMPENSATION = "altitude_compensation" | CONF_ALTITUDE_COMPENSATION = "altitude_compensation" | ||||||
| CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation" | CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation" | ||||||
| CONF_TEMPERATURE_OFFSET = "temperature_offset" |  | ||||||
| CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE = "ambient_pressure_compensation_source" | CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE = "ambient_pressure_compensation_source" | ||||||
|  | CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration" | ||||||
|  | CONF_MEASUREMENT_MODE = "measurement_mode" | ||||||
|  | CONF_TEMPERATURE_OFFSET = "temperature_offset" | ||||||
|  |  | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = ( | CONFIG_SCHEMA = ( | ||||||
|     cv.Schema( |     cv.Schema( | ||||||
| @@ -69,6 +90,9 @@ CONFIG_SCHEMA = ( | |||||||
|             cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE): cv.use_id( |             cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE): cv.use_id( | ||||||
|                 sensor.Sensor |                 sensor.Sensor | ||||||
|             ), |             ), | ||||||
|  |             cv.Optional(CONF_MEASUREMENT_MODE, default="periodic"): cv.enum( | ||||||
|  |                 MEASUREMENT_MODE_OPTIONS, lower=True | ||||||
|  |             ), | ||||||
|         } |         } | ||||||
|     ) |     ) | ||||||
|     .extend(cv.polling_component_schema("60s")) |     .extend(cv.polling_component_schema("60s")) | ||||||
| @@ -106,3 +130,42 @@ async def to_code(config): | |||||||
|     if CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE in config: |     if CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE in config: | ||||||
|         sens = await cg.get_variable(config[CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE]) |         sens = await cg.get_variable(config[CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE]) | ||||||
|         cg.add(var.set_ambient_pressure_source(sens)) |         cg.add(var.set_ambient_pressure_source(sens)) | ||||||
|  |  | ||||||
|  |     cg.add(var.set_measurement_mode(config[CONF_MEASUREMENT_MODE])) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | SCD4X_ACTION_SCHEMA = maybe_simple_id( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(): cv.use_id(SCD4XComponent), | ||||||
|  |         cv.Required(CONF_VALUE): cv.templatable(cv.positive_int), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "scd4x.perform_forced_calibration", | ||||||
|  |     PerformForcedCalibrationAction, | ||||||
|  |     SCD4X_ACTION_SCHEMA, | ||||||
|  | ) | ||||||
|  | async def scd4x_frc_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     await cg.register_parented(var, config[CONF_ID]) | ||||||
|  |     template_ = await cg.templatable(config[CONF_VALUE], args, cg.uint16) | ||||||
|  |     cg.add(var.set_value(template_)) | ||||||
|  |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | SCD4X_RESET_ACTION_SCHEMA = maybe_simple_id( | ||||||
|  |     { | ||||||
|  |         cv.Required(CONF_ID): cv.use_id(SCD4XComponent), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "scd4x.factory_reset", FactoryResetAction, SCD4X_RESET_ACTION_SCHEMA | ||||||
|  | ) | ||||||
|  | async def scd4x_reset_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     await cg.register_parented(var, config[CONF_ID]) | ||||||
|  |     return var | ||||||
|   | |||||||
| @@ -890,6 +890,7 @@ sensor: | |||||||
|     temperature_offset: 4.2C |     temperature_offset: 4.2C | ||||||
|     i2c_id: i2c_bus |     i2c_id: i2c_bus | ||||||
|   - platform: scd4x |   - platform: scd4x | ||||||
|  |     id: scd40 | ||||||
|     co2: |     co2: | ||||||
|       name: "SCD4X CO2" |       name: "SCD4X CO2" | ||||||
|     temperature: |     temperature: | ||||||
| @@ -2822,3 +2823,14 @@ lock: | |||||||
|   - platform: copy |   - platform: copy | ||||||
|     source_id: test_lock2 |     source_id: test_lock2 | ||||||
|     name: Generic Output Lock Copy |     name: Generic Output Lock Copy | ||||||
|  |  | ||||||
|  | button: | ||||||
|  |   - platform: template | ||||||
|  |     name: "Start calibration" | ||||||
|  |     on_press: | ||||||
|  |       - scd4x.perform_forced_calibration: | ||||||
|  |           value: 419 | ||||||
|  |           id: scd40 | ||||||
|  |       - scd4x.factory_reset: | ||||||
|  |           id: scd40 | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user