mirror of
https://github.com/esphome/esphome.git
synced 2024-11-22 11:47:30 +01:00
extend scd4x (#3409)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
4e1f6518e8
commit
782186e13d
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user