diff --git a/esphome/components/mhz19/mhz19.cpp b/esphome/components/mhz19/mhz19.cpp index c3c8120362..00e6e14d85 100644 --- a/esphome/components/mhz19/mhz19.cpp +++ b/esphome/components/mhz19/mhz19.cpp @@ -13,6 +13,9 @@ static const uint8_t MHZ19_COMMAND_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x static const uint8_t MHZ19_COMMAND_ABC_ENABLE[] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00}; static const uint8_t MHZ19_COMMAND_ABC_DISABLE[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00}; static const uint8_t MHZ19_COMMAND_CALIBRATE_ZERO[] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0}; +static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88}; +static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x27, 0x10}; uint8_t mhz19_checksum(const uint8_t *command) { uint8_t sum = 0; @@ -28,6 +31,8 @@ void MHZ19Component::setup() { } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) { this->abc_disable(); } + + this->range_set(this->detection_range_); } void MHZ19Component::update() { @@ -86,6 +91,26 @@ void MHZ19Component::abc_disable() { this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, nullptr); } +void MHZ19Component::range_set(MHZ19DetectionRange detection_ppm) { + switch (detection_ppm) { + case MHZ19_DETECTION_RANGE_DEFAULT: + ESP_LOGV(TAG, "Using previously set detection range (no change)"); + break; + case MHZ19_DETECTION_RANGE_0_2000PPM: + ESP_LOGD(TAG, "Setting detection range to 0 to 2000ppm"); + this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM, nullptr); + break; + case MHZ19_DETECTION_RANGE_0_5000PPM: + ESP_LOGD(TAG, "Setting detection range to 0 to 5000ppm"); + this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM, nullptr); + break; + case MHZ19_DETECTION_RANGE_0_10000PPM: + ESP_LOGD(TAG, "Setting detection range to 0 to 10000ppm"); + this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM, nullptr); + break; + } +} + bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) { // Empty RX Buffer while (this->available()) @@ -99,7 +124,9 @@ bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *respo return this->read_array(response, MHZ19_RESPONSE_LENGTH); } + float MHZ19Component::get_setup_priority() const { return setup_priority::DATA; } + void MHZ19Component::dump_config() { ESP_LOGCONFIG(TAG, "MH-Z19:"); LOG_SENSOR(" ", "CO2", this->co2_sensor_); @@ -113,6 +140,23 @@ void MHZ19Component::dump_config() { } ESP_LOGCONFIG(TAG, " Warmup time: %" PRIu32 " s", this->warmup_seconds_); + + const char *range_str; + switch (this->detection_range_) { + case MHZ19_DETECTION_RANGE_DEFAULT: + range_str = "default"; + break; + case MHZ19_DETECTION_RANGE_0_2000PPM: + range_str = "0 to 2000ppm"; + break; + case MHZ19_DETECTION_RANGE_0_5000PPM: + range_str = "0 to 5000ppm"; + break; + case MHZ19_DETECTION_RANGE_0_10000PPM: + range_str = "0 to 10000ppm"; + break; + } + ESP_LOGCONFIG(TAG, " Detection range: %s", range_str); } } // namespace mhz19 diff --git a/esphome/components/mhz19/mhz19.h b/esphome/components/mhz19/mhz19.h index be36886d62..feb67bdd82 100644 --- a/esphome/components/mhz19/mhz19.h +++ b/esphome/components/mhz19/mhz19.h @@ -8,7 +8,18 @@ namespace esphome { namespace mhz19 { -enum MHZ19ABCLogic { MHZ19_ABC_NONE = 0, MHZ19_ABC_ENABLED, MHZ19_ABC_DISABLED }; +enum MHZ19ABCLogic { + MHZ19_ABC_NONE = 0, + MHZ19_ABC_ENABLED, + MHZ19_ABC_DISABLED, +}; + +enum MHZ19DetectionRange { + MHZ19_DETECTION_RANGE_DEFAULT = 0, + MHZ19_DETECTION_RANGE_0_2000PPM, + MHZ19_DETECTION_RANGE_0_5000PPM, + MHZ19_DETECTION_RANGE_0_10000PPM, +}; class MHZ19Component : public PollingComponent, public uart::UARTDevice { public: @@ -21,11 +32,13 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice { void calibrate_zero(); void abc_enable(); void abc_disable(); + void range_set(MHZ19DetectionRange detection_ppm); void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; } void set_abc_enabled(bool abc_enabled) { abc_boot_logic_ = abc_enabled ? MHZ19_ABC_ENABLED : MHZ19_ABC_DISABLED; } void set_warmup_seconds(uint32_t seconds) { warmup_seconds_ = seconds; } + void set_detection_range(MHZ19DetectionRange detection_range) { detection_range_ = detection_range; } protected: bool mhz19_write_command_(const uint8_t *command, uint8_t *response); @@ -33,7 +46,10 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice { sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *co2_sensor_{nullptr}; MHZ19ABCLogic abc_boot_logic_{MHZ19_ABC_NONE}; + uint32_t warmup_seconds_; + + MHZ19DetectionRange detection_range_{MHZ19_DETECTION_RANGE_DEFAULT}; }; template class MHZ19CalibrateZeroAction : public Action { @@ -66,5 +82,16 @@ template class MHZ19ABCDisableAction : public Action { MHZ19Component *mhz19_; }; +template class MHZ19DetectionRangeSetAction : public Action { + public: + MHZ19DetectionRangeSetAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} + TEMPLATABLE_VALUE(MHZ19DetectionRange, detection_range) + + void play(const Ts &...x) override { this->mhz19_->range_set(this->detection_range_.value(x...)); } + + protected: + MHZ19Component *mhz19_; +}; + } // namespace mhz19 } // namespace esphome diff --git a/esphome/components/mhz19/sensor.py b/esphome/components/mhz19/sensor.py index 106636a6ba..0156ee4be0 100644 --- a/esphome/components/mhz19/sensor.py +++ b/esphome/components/mhz19/sensor.py @@ -19,6 +19,7 @@ DEPENDENCIES = ["uart"] CONF_AUTOMATIC_BASELINE_CALIBRATION = "automatic_baseline_calibration" CONF_WARMUP_TIME = "warmup_time" +CONF_DETECTION_RANGE = "detection_range" mhz19_ns = cg.esphome_ns.namespace("mhz19") MHZ19Component = mhz19_ns.class_("MHZ19Component", cg.PollingComponent, uart.UARTDevice) @@ -27,6 +28,18 @@ MHZ19CalibrateZeroAction = mhz19_ns.class_( ) MHZ19ABCEnableAction = mhz19_ns.class_("MHZ19ABCEnableAction", automation.Action) MHZ19ABCDisableAction = mhz19_ns.class_("MHZ19ABCDisableAction", automation.Action) +MHZ19DetectionRangeSetAction = mhz19_ns.class_( + "MHZ19DetectionRangeSetAction", automation.Action +) + +mhz19_detection_range = mhz19_ns.enum("MHZ19DetectionRange") +MHZ19_DETECTION_RANGE_ENUM = { + 2000: mhz19_detection_range.MHZ19_DETECTION_RANGE_0_2000PPM, + 5000: mhz19_detection_range.MHZ19_DETECTION_RANGE_0_5000PPM, + 10000: mhz19_detection_range.MHZ19_DETECTION_RANGE_0_10000PPM, +} + +_validate_ppm = cv.float_with_unit("parts per million", "ppm") CONFIG_SCHEMA = ( cv.Schema( @@ -49,6 +62,9 @@ CONFIG_SCHEMA = ( cv.Optional( CONF_WARMUP_TIME, default="75s" ): cv.positive_time_period_seconds, + cv.Optional(CONF_DETECTION_RANGE): cv.All( + _validate_ppm, cv.enum(MHZ19_DETECTION_RANGE_ENUM) + ), } ) .extend(cv.polling_component_schema("60s")) @@ -78,6 +94,9 @@ async def to_code(config): cg.add(var.set_warmup_seconds(config[CONF_WARMUP_TIME])) + if CONF_DETECTION_RANGE in config: + cg.add(var.set_detection_range(config[CONF_DETECTION_RANGE])) + CALIBRATION_ACTION_SCHEMA = maybe_simple_id( { @@ -98,3 +117,25 @@ CALIBRATION_ACTION_SCHEMA = maybe_simple_id( async def mhz19_calibration_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) return cg.new_Pvariable(action_id, template_arg, paren) + + +RANGE_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(MHZ19Component), + cv.Required(CONF_DETECTION_RANGE): cv.All( + _validate_ppm, cv.enum(MHZ19_DETECTION_RANGE_ENUM) + ), + } +) + + +@automation.register_action( + "mhz19.detection_range_set", MHZ19DetectionRangeSetAction, RANGE_ACTION_SCHEMA +) +async def mhz19_detection_range_set_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + detection_range = config.get(CONF_DETECTION_RANGE) + template_ = await cg.templatable(detection_range, args, mhz19_detection_range) + cg.add(var.set_detection_range(template_)) + return var diff --git a/tests/components/mhz19/common.yaml b/tests/components/mhz19/common.yaml index 94989fecbe..b12ca50197 100644 --- a/tests/components/mhz19/common.yaml +++ b/tests/components/mhz19/common.yaml @@ -6,3 +6,4 @@ sensor: name: MH-Z19 Temperature automatic_baseline_calibration: false update_interval: 15s + detection_range: 5000ppm