[sen5x] Add model option to override autodetection (#17764)
CI / Create common environment (push) Canceled after 0s
CI / Check pylint (push) Canceled after 0s
CI / Run script/ci-custom (push) Canceled after 0s
CI / Check import esphome.__main__ time (push) Canceled after 0s
CI / Test downstream esphome/device-builder (push) Canceled after 0s
CI / Run pytest (macOS-latest, 3.12) (push) Canceled after 0s
CI / Run pytest (macOS-latest, 3.14) (push) Canceled after 0s
CI / Run pytest (ubuntu-latest, 3.12) (push) Canceled after 0s
CI / Run pytest (ubuntu-latest, 3.13) (push) Canceled after 0s
CI / Run pytest (ubuntu-latest, 3.14) (push) Canceled after 0s
CI / Run pytest (windows-latest, 3.12) (push) Canceled after 0s
CI / Run pytest (windows-latest, 3.14) (push) Canceled after 0s
CI / Determine which jobs to run (push) Canceled after 0s
CI / Run integration tests () (push) Canceled after 0s
CI / Run C++ unit tests (push) Canceled after 0s
CI / Run CodSpeed benchmarks (push) Canceled after 0s
CI / Run script/clang-tidy for LibreTiny (push) Canceled after 0s
CI / Run script/clang-tidy for ESP8266 (push) Canceled after 0s
CI / Run script/clang-tidy for RP2 (push) Canceled after 0s
CI / Run script/clang-tidy for ESP32 Arduino (push) Canceled after 0s
CI / Run script/clang-tidy for ZEPHYR (push) Canceled after 0s
CI / Run script/clang-tidy for ESP32 IDF (push) Canceled after 0s
CI / Run script/clang-tidy for ESP32 IDF 1/3 (push) Canceled after 0s
CI / Run script/clang-tidy for ESP32 IDF 2/3 (push) Canceled after 0s
CI / Run script/clang-tidy for ESP32 IDF 3/3 (push) Canceled after 0s
CI / Run script/clang-tidy for ESP32 C6 (push) Canceled after 0s
CI / Run script/clang-tidy for ESP32 P4 (push) Canceled after 0s
CI / Run script/clang-tidy for ESP32 S3 (push) Canceled after 0s
CI / Test components batch () (push) Canceled after 0s
CI / Test esp32 components with PlatformIO (push) Canceled after 0s
CI / Seed pre-commit cache (push) Canceled after 0s
CI / pre-commit.ci lite (push) Canceled after 0s
CI / Build target branch for memory impact (push) Canceled after 0s
CI / Build PR branch for memory impact (push) Canceled after 0s
CI / Comment memory impact (push) Canceled after 0s
CI / CI Status (push) Canceled after 0s
CI - GitHub Scripts / Test auto-label-pr scripts (push) Canceled after 0s

This commit is contained in:
Brandon Harvey
2026-07-21 13:18:21 -10:00
committed by GitHub
parent 2edb089f71
commit 69085a7a42
4 changed files with 41 additions and 17 deletions
+24 -16
View File
@@ -101,25 +101,31 @@ void SEN5XComponent::setup() {
ESP_LOGV(TAG, "Serial number %s", this->serial_number_);
uint16_t raw_product_name[16];
if (!this->get_register(SEN5X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) {
ESP_LOGE(TAG, "Failed to read product name");
this->error_code_ = PRODUCT_NAME_FAILED;
this->mark_failed();
return;
Sen5xType detected_type = Sen5xType::UNKNOWN;
if (this->get_register(SEN5X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) {
const char *product_name = sensirion_convert_to_string_in_place(raw_product_name, 16);
if (strncmp(product_name, "SEN50", 5) == 0) {
detected_type = Sen5xType::SEN50;
} else if (strncmp(product_name, "SEN54", 5) == 0) {
detected_type = Sen5xType::SEN54;
} else if (strncmp(product_name, "SEN55", 5) == 0) {
detected_type = Sen5xType::SEN55;
}
}
const char *product_name = sensirion_convert_to_string_in_place(raw_product_name, 16);
if (strncmp(product_name, "SEN50", 5) == 0) {
this->type_ = Sen5xType::SEN50;
} else if (strncmp(product_name, "SEN54", 5) == 0) {
this->type_ = Sen5xType::SEN54;
} else if (strncmp(product_name, "SEN55", 5) == 0) {
this->type_ = Sen5xType::SEN55;
} else {
if (this->model_override_.has_value()) {
if (detected_type != this->model_override_.value()) {
ESP_LOGW(TAG, "Detected %s, using %s", LOG_STR_ARG(type_to_string(detected_type)),
LOG_STR_ARG(type_to_string(this->model_override_.value())));
}
this->type_ = this->model_override_.value();
} else if (detected_type == Sen5xType::UNKNOWN) {
this->type_ = Sen5xType::UNKNOWN;
ESP_LOGE(TAG, "Unknown product name: %.32s", product_name);
this->error_code_ = PRODUCT_NAME_FAILED;
this->mark_failed();
return;
} else {
this->type_ = detected_type;
}
ESP_LOGD(TAG, "Type: %s", LOG_STR_ARG(type_to_string(this->type_)));
@@ -255,10 +261,12 @@ void SEN5XComponent::dump_config() {
}
}
ESP_LOGCONFIG(TAG,
" Type: %s\n"
" Type: %s%s\n"
" Firmware version: %d\n"
" Serial number: %s",
LOG_STR_ARG(type_to_string(this->type_)), this->firmware_version_, this->serial_number_);
LOG_STR_ARG(type_to_string(this->type_)),
this->model_override_.has_value() ? LOG_STR_LITERAL(" (overridden)") : LOG_STR_LITERAL(""),
this->firmware_version_, this->serial_number_);
if (this->auto_cleaning_interval_.has_value()) {
ESP_LOGCONFIG(TAG, " Auto cleaning interval: %" PRId32 "s", this->auto_cleaning_interval_.value());
}
+2
View File
@@ -95,6 +95,7 @@ class SEN5XComponent final : public PollingComponent, public sensirion_common::S
temp_comp.time_constant = time_constant;
this->temperature_compensation_ = temp_comp;
}
void set_model(Sen5xType model) { this->model_override_ = model; }
bool start_fan_cleaning();
protected:
@@ -126,6 +127,7 @@ class SEN5XComponent final : public PollingComponent, public sensirion_common::S
optional<GasTuning> voc_tuning_params_;
optional<GasTuning> nox_tuning_params_;
optional<TemperatureCompensation> temperature_compensation_;
optional<Sen5xType> model_override_;
ESPPreferenceObject pref_;
};
+14 -1
View File
@@ -12,6 +12,7 @@ from esphome.const import (
CONF_INDEX_OFFSET,
CONF_LEARNING_TIME_GAIN_HOURS,
CONF_LEARNING_TIME_OFFSET_HOURS,
CONF_MODEL,
CONF_NORMALIZED_OFFSET_SLOPE,
CONF_NOX,
CONF_OFFSET,
@@ -39,6 +40,7 @@ from esphome.const import (
UNIT_MICROGRAMS_PER_CUBIC_METER,
UNIT_PERCENT,
)
from esphome.types import ConfigType
CODEOWNERS = ["@martgras"]
DEPENDENCIES = ["i2c"]
@@ -49,6 +51,7 @@ SEN5XComponent = sen5x_ns.class_(
"SEN5XComponent", cg.PollingComponent, sensirion_common.SensirionI2CDevice
)
RhtAccelerationMode = sen5x_ns.enum("RhtAccelerationMode")
Sen5xType = sen5x_ns.enum("Sen5xType", is_class=True)
CONF_ACCELERATION_MODE = "acceleration_mode"
CONF_AUTO_CLEANING_INTERVAL = "auto_cleaning_interval"
@@ -63,6 +66,12 @@ ACCELERATION_MODES = {
"high": RhtAccelerationMode.HIGH_ACCELERATION,
}
MODELS = {
"SEN50": Sen5xType.SEN50,
"SEN54": Sen5xType.SEN54,
"SEN55": Sen5xType.SEN55,
}
def _gas_sensor(
*,
@@ -186,6 +195,7 @@ CONFIG_SCHEMA = (
}
),
cv.Optional(CONF_ACCELERATION_MODE): cv.enum(ACCELERATION_MODES),
cv.Optional(CONF_MODEL): cv.enum(MODELS, upper=True),
}
)
.extend(cv.polling_component_schema("60s"))
@@ -210,7 +220,7 @@ SETTING_MAP = {
}
async def to_code(config):
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
@@ -219,6 +229,9 @@ async def to_code(config):
if cfg := config.get(key):
cg.add(getattr(var, funcName)(cfg))
if (model := config.get(CONF_MODEL)) is not None:
cg.add(var.set_model(model))
for key, funcName in SENSOR_MAP.items():
if cfg := config.get(key):
sens = await sensor.new_sensor(cfg)
+1
View File
@@ -42,4 +42,5 @@ sensor:
auto_cleaning_interval: 604800s
acceleration_mode: low
store_baseline: true
model: sen55
address: 0x69