diff --git a/CODEOWNERS b/CODEOWNERS index c630db7948..985f8e0667 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -76,6 +76,7 @@ esphome/components/cap1188/* @mreditor97 esphome/components/captive_portal/* @OttoWinter esphome/components/ccs811/* @habbie esphome/components/cd74hc4067/* @asoehlke +esphome/components/cdm7160/* @goatchurchprime esphome/components/climate/* @esphome/core esphome/components/climate_ir/* @glmnet esphome/components/color_temperature/* @jesserockz diff --git a/esphome/components/cdm7160/__init__.py b/esphome/components/cdm7160/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/esphome/components/cdm7160/__init__.py @@ -0,0 +1 @@ + diff --git a/esphome/components/cdm7160/cdm7160.cpp b/esphome/components/cdm7160/cdm7160.cpp new file mode 100644 index 0000000000..742f4a5ffb --- /dev/null +++ b/esphome/components/cdm7160/cdm7160.cpp @@ -0,0 +1,70 @@ +#include "cdm7160.h" + +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace cdm7160 { + +static const uint8_t CDM7160_DATA_CO2_LO = 0x03; +static const uint8_t CDM7160_DATA_CO2_HI = 0x04; +static const uint8_t CDM7160_HPA_PRESSURE = 0x09; +static const uint8_t CDM7160_HIT_ALTITUDE = 0x0A; + +static const char *const TAG = "cdm7160"; + +void CDM7160Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up CDM7160..."); + if (!this->write_altitude_()) { + ESP_LOGE(TAG, "Communication with MLX90614 failed!"); + this->mark_failed(); + return; + } +} + +bool CDM7160Component::write_altitude_() { + if (std::isnan(this->altitude_)) + return true; + uint8_t value = (uint8_t) (this->altitude_ / 10); + if (!this->write_bytes(CDM7160_HIT_ALTITUDE, &value, 1)) { + return false; + } + delay(10); + if (!this->write_bytes(CDM7160_HIT_ALTITUDE, &value, 1)) { + return false; + } + delay(10); + return true; +} + + +void CDM7160Component::dump_config() { + ESP_LOGCONFIG(TAG, "CDM7160:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with CDM7160 failed!"); + } + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "co2", this->co2_sensor_); +} + +float CDM7160Component::get_setup_priority() const { return setup_priority::DATA; } + +void CDM7160Component::update() { + uint8_t raw_co2[2]; + if (this->read_register(CDM7160_DATA_CO2_LO, raw_co2, 2, false) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + + int co2 = encode_uint16(raw_co2[1], raw_co2[0]); + + ESP_LOGD(TAG, "Got CO2=%dppm", co2); + + if (this->co2_sensor_ != nullptr && !std::isnan(co2)) + this->co2_sensor_->publish_state(co2); + this->status_clear_warning(); +} + +} // namespace cdm7160 +} // namespace esphome diff --git a/esphome/components/cdm7160/cdm7160.h b/esphome/components/cdm7160/cdm7160.h new file mode 100644 index 0000000000..3bad157632 --- /dev/null +++ b/esphome/components/cdm7160/cdm7160.h @@ -0,0 +1,28 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace cdm7160 { + +class CDM7160Component : public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + void update() override; + float get_setup_priority() const override; + + void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; } + + void set_altitude(float altitude) { altitude_ = altitude; } + + protected: + sensor::Sensor *co2_sensor_{nullptr}; + bool write_altitude_(); + + float altitude_ = 5.0f; +}; +} // namespace cdm7160 +} // namespace esphome diff --git a/esphome/components/cdm7160/sensor.py b/esphome/components/cdm7160/sensor.py new file mode 100644 index 0000000000..ca0f059456 --- /dev/null +++ b/esphome/components/cdm7160/sensor.py @@ -0,0 +1,49 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_ID, + CONF_CO2, + DEVICE_CLASS_CARBON_DIOXIDE, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_MILLION, + ICON_MOLECULE_CO2, + UNIT_CELSIUS, +) + +CODEOWNERS = ["@goatchurchprime"] +DEPENDENCIES = ["i2c"] + +CONF_CO2 = "co2" + +cdm7160_ns = cg.esphome_ns.namespace("cdm7160") +CDM7160Component = cdm7160_ns.class_( + "CDM7160Component", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CDM7160Component), + cv.Optional(CONF_CO2): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + icon=ICON_MOLECULE_CO2, + accuracy_decimals=0, + device_class=DEVICE_CLASS_CARBON_DIOXIDE, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x69)) +) + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + if CONF_CO2 in config: + sens = await sensor.new_sensor(config[CONF_CO2]) + cg.add(var.set_co2_sensor(sens)) + diff --git a/tests/test1.yaml b/tests/test1.yaml index 79b836da4a..586e6466fc 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -741,6 +741,10 @@ sensor: update_interval: 15s iir_filter: 16x i2c_id: i2c_bus + - platform: cdm7160 + i2c_id: i2c_bus + co2: + name: CDM7160 CO2 Value - platform: dallas address: 0x1C0000031EDD2A28 name: Living Room Temperature