From 264541456e2093c6a6d990c85a9fb1e7a8beb042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Sun, 10 Dec 2023 17:00:14 +0100 Subject: [PATCH] Current firmware updates --- firmware/components/custom_sgp30/__init__.py | 0 firmware/components/custom_sgp30/sensor.py | 119 ++++++++ firmware/components/custom_sgp30/sgp30.cpp | 304 +++++++++++++++++++ firmware/components/custom_sgp30/sgp30.h | 71 +++++ firmware/datenzwerg.yaml | 4 +- firmware/packages/base.yaml | 12 +- firmware/packages/uptime.yaml | 3 + firmware/packages/webserver.yaml | 2 + 8 files changed, 511 insertions(+), 4 deletions(-) create mode 100644 firmware/components/custom_sgp30/__init__.py create mode 100644 firmware/components/custom_sgp30/sensor.py create mode 100644 firmware/components/custom_sgp30/sgp30.cpp create mode 100644 firmware/components/custom_sgp30/sgp30.h create mode 100644 firmware/packages/uptime.yaml create mode 100644 firmware/packages/webserver.yaml diff --git a/firmware/components/custom_sgp30/__init__.py b/firmware/components/custom_sgp30/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/firmware/components/custom_sgp30/sensor.py b/firmware/components/custom_sgp30/sensor.py new file mode 100644 index 0000000..33cdefa --- /dev/null +++ b/firmware/components/custom_sgp30/sensor.py @@ -0,0 +1,119 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor, sensirion_common + +from esphome.const import ( + CONF_ID, + CONF_BASELINE, + CONF_ECO2, + CONF_STORE_BASELINE, + CONF_TEMPERATURE_SOURCE, + CONF_TVOC, + ICON_RADIATOR, + DEVICE_CLASS_CARBON_DIOXIDE, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_MILLION, + UNIT_PARTS_PER_BILLION, + ICON_MOLECULE_CO2, + ENTITY_CATEGORY_DIAGNOSTIC, +) + +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["sensirion_common"] + +custom_sgp30_ns = cg.esphome_ns.namespace("custom_sgp30") +CustomSGP30Component = custom_sgp30_ns.class_( + "CustomSGP30Component", cg.PollingComponent, sensirion_common.SensirionI2CDevice +) + +CONF_ECO2_BASELINE = "eco2_baseline" +CONF_TVOC_BASELINE = "tvoc_baseline" +CONF_UPTIME = "uptime" +CONF_COMPENSATION = "compensation" +CONF_HUMIDITY_SOURCE = "humidity_source" + + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomSGP30Component), + cv.Required(CONF_ECO2): 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, + ), + cv.Required(CONF_TVOC): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_BILLION, + icon=ICON_RADIATOR, + accuracy_decimals=0, + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_ECO2_BASELINE): sensor.sensor_schema( + icon=ICON_MOLECULE_CO2, + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_TVOC_BASELINE): sensor.sensor_schema( + icon=ICON_RADIATOR, + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean, + cv.Optional(CONF_BASELINE): cv.Schema( + { + cv.Required(CONF_ECO2_BASELINE): cv.hex_uint16_t, + cv.Required(CONF_TVOC_BASELINE): cv.hex_uint16_t, + } + ), + cv.Optional(CONF_COMPENSATION): cv.Schema( + { + cv.Required(CONF_HUMIDITY_SOURCE): cv.use_id(sensor.Sensor), + cv.Required(CONF_TEMPERATURE_SOURCE): cv.use_id(sensor.Sensor), + } + ), + } + ) + .extend(cv.polling_component_schema("1s")) + .extend(i2c.i2c_device_schema(0x58)) +) + + +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_ECO2 in config: + sens = await sensor.new_sensor(config[CONF_ECO2]) + cg.add(var.set_eco2_sensor(sens)) + + if CONF_TVOC in config: + sens = await sensor.new_sensor(config[CONF_TVOC]) + cg.add(var.set_tvoc_sensor(sens)) + + if CONF_ECO2_BASELINE in config: + sens = await sensor.new_sensor(config[CONF_ECO2_BASELINE]) + cg.add(var.set_eco2_baseline_sensor(sens)) + + if CONF_TVOC_BASELINE in config: + sens = await sensor.new_sensor(config[CONF_TVOC_BASELINE]) + cg.add(var.set_tvoc_baseline_sensor(sens)) + + if CONF_STORE_BASELINE in config: + cg.add(var.set_store_baseline(config[CONF_STORE_BASELINE])) + + if CONF_BASELINE in config: + baseline_config = config[CONF_BASELINE] + cg.add(var.set_eco2_baseline(baseline_config[CONF_ECO2_BASELINE])) + cg.add(var.set_tvoc_baseline(baseline_config[CONF_TVOC_BASELINE])) + + if CONF_COMPENSATION in config: + compensation_config = config[CONF_COMPENSATION] + sens = await cg.get_variable(compensation_config[CONF_HUMIDITY_SOURCE]) + cg.add(var.set_humidity_sensor(sens)) + sens = await cg.get_variable(compensation_config[CONF_TEMPERATURE_SOURCE]) + cg.add(var.set_temperature_sensor(sens)) diff --git a/firmware/components/custom_sgp30/sgp30.cpp b/firmware/components/custom_sgp30/sgp30.cpp new file mode 100644 index 0000000..81135f9 --- /dev/null +++ b/firmware/components/custom_sgp30/sgp30.cpp @@ -0,0 +1,304 @@ +#include "sgp30.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" +#include + +namespace esphome { +namespace custom_sgp30 { + +static const char *const TAG = "custom_sgp30"; + +static const uint16_t SGP30_CMD_GET_SERIAL_ID = 0x3682; +static const uint16_t SGP30_CMD_GET_FEATURESET = 0x202f; +static const uint16_t SGP30_CMD_IAQ_INIT = 0x2003; +static const uint16_t SGP30_CMD_MEASURE_IAQ = 0x2008; +static const uint16_t SGP30_CMD_SET_ABSOLUTE_HUMIDITY = 0x2061; +static const uint16_t SGP30_CMD_GET_IAQ_BASELINE = 0x2015; +static const uint16_t SGP30_CMD_SET_IAQ_BASELINE = 0x201E; + +// Sensor baseline should first be relied on after 1H of operation, +// if the sensor starts with a baseline value provided +const uint32_t IAQ_BASELINE_WARM_UP_SECONDS_WITH_BASELINE_PROVIDED = 3600; + +// Sensor baseline could first be relied on after 12H of operation, +// if the sensor starts without any prior baseline value provided +const uint32_t IAQ_BASELINE_WARM_UP_SECONDS_WITHOUT_BASELINE = 43200; + +// Shortest time interval of 1H for storing baseline values. +// Prevents wear of the flash because of too many write operations +const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 3600; + +// Store anyway if the baseline difference exceeds the max storage diff value +const uint32_t MAXIMUM_STORAGE_DIFF = 50; + +void CustomSGP30Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up SGP30..."); + + // Serial Number identification + uint16_t raw_serial_number[3]; + if (!this->get_register(SGP30_CMD_GET_SERIAL_ID, raw_serial_number, 3)) { + this->mark_failed(); + return; + } + this->serial_number_ = (uint64_t(raw_serial_number[0]) << 24) | (uint64_t(raw_serial_number[1]) << 16) | + (uint64_t(raw_serial_number[2])); + ESP_LOGD(TAG, "Serial Number: %" PRIu64, this->serial_number_); + + // Featureset identification for future use + uint16_t raw_featureset; + if (!this->get_register(SGP30_CMD_GET_FEATURESET, raw_featureset)) { + this->mark_failed(); + return; + } + this->featureset_ = raw_featureset; + if (uint16_t(this->featureset_ >> 12) != 0x0) { + if (uint16_t(this->featureset_ >> 12) == 0x1) { + // ID matching a different sensor: SGPC3 + this->error_code_ = UNSUPPORTED_ID; + } else { + // Unknown ID + this->error_code_ = INVALID_ID; + } + this->mark_failed(); + return; + } + ESP_LOGD(TAG, "Product version: 0x%0X", uint16_t(this->featureset_ & 0x1FF)); + + // Sensor initialization + if (!this->write_command(SGP30_CMD_IAQ_INIT)) { + ESP_LOGE(TAG, "Sensor sgp30_iaq_init failed."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + + // Hash with compilation time and serial number + // This ensures the baseline storage is cleared after OTA + // Serial numbers are unique to each sensor, so mulitple sensors can be used without conflict + uint32_t hash = fnv1_hash(App.get_compilation_time() + std::to_string(this->serial_number_)); + this->pref_ = global_preferences->make_preference(hash, true); + + if (this->pref_.load(&this->baselines_storage_)) { + ESP_LOGI(TAG, "Loaded eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2, + baselines_storage_.tvoc); + this->eco2_baseline_ = this->baselines_storage_.eco2; + this->tvoc_baseline_ = this->baselines_storage_.tvoc; + } + + // Initialize storage timestamp + this->seconds_since_last_store_ = 0; + + // Sensor baseline reliability timer + if (this->eco2_baseline_ > 0 && this->tvoc_baseline_ > 0) { + this->required_warm_up_time_ = IAQ_BASELINE_WARM_UP_SECONDS_WITH_BASELINE_PROVIDED; + this->write_iaq_baseline_(this->eco2_baseline_, this->tvoc_baseline_); + } else { + this->required_warm_up_time_ = IAQ_BASELINE_WARM_UP_SECONDS_WITHOUT_BASELINE; + } +} + +bool CustomSGP30Component::is_sensor_baseline_reliable_() { + if ((this->required_warm_up_time_ == 0) || (std::floor(millis() / 1000) >= this->required_warm_up_time_)) { + // requirement for warm up is removed once the millis uptime surpasses the required warm_up_time + // this avoids the repetitive warm up when the millis uptime is rolled over every ~40 days + this->required_warm_up_time_ = 0; + return true; + } + return false; +} + +void CustomSGP30Component::read_iaq_baseline_() { + if (!this->write_command(SGP30_CMD_GET_IAQ_BASELINE)) { + ESP_LOGD(TAG, "Error getting baseline"); + this->status_set_warning(); + return; + } + this->set_timeout(50, [this]() { + uint16_t raw_data[2]; + if (!this->read_data(raw_data, 2)) { + this->status_set_warning(); + return; + } + + uint16_t eco2baseline = (raw_data[0]); + uint16_t tvocbaseline = (raw_data[1]); + + ESP_LOGI(TAG, "Current eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2baseline, tvocbaseline); + if (eco2baseline != this->eco2_baseline_ || tvocbaseline != this->tvoc_baseline_) { + this->eco2_baseline_ = eco2baseline; + this->tvoc_baseline_ = tvocbaseline; + if (this->eco2_sensor_baseline_ != nullptr) + this->eco2_sensor_baseline_->publish_state(this->eco2_baseline_); + if (this->tvoc_sensor_baseline_ != nullptr) + this->tvoc_sensor_baseline_->publish_state(this->tvoc_baseline_); + + // Store baselines after defined interval or if the difference between current and stored baseline becomes too + // much + if (this->is_sensor_baseline_reliable_() && this->store_baseline_ && + (this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL || + (uint32_t) abs(this->baselines_storage_.eco2 - this->eco2_baseline_) > MAXIMUM_STORAGE_DIFF || + (uint32_t) abs(this->baselines_storage_.tvoc - this->tvoc_baseline_) > MAXIMUM_STORAGE_DIFF)) { + this->seconds_since_last_store_ = 0; + this->baselines_storage_.eco2 = this->eco2_baseline_; + this->baselines_storage_.tvoc = this->tvoc_baseline_; + if (this->pref_.save(&this->baselines_storage_)) { + ESP_LOGI(TAG, "Store eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2, + this->baselines_storage_.tvoc); + } else { + ESP_LOGW(TAG, "Could not store eCO2 and TVOC baselines"); + } + } + } + this->status_clear_warning(); + }); +} + +void CustomSGP30Component::send_env_data_() { + if (this->humidity_sensor_ == nullptr && this->temperature_sensor_ == nullptr) + return; + float humidity = NAN; + if (this->humidity_sensor_ != nullptr) + humidity = this->humidity_sensor_->state; + if (std::isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { + ESP_LOGW(TAG, "Compensation not possible yet: bad humidity data."); + return; + } else { + ESP_LOGD(TAG, "External compensation data received: Humidity %0.2f%%", humidity); + } + float temperature = NAN; + if (this->temperature_sensor_ != nullptr) { + temperature = float(this->temperature_sensor_->state); + } + if (std::isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { + ESP_LOGW(TAG, "Compensation not possible yet: bad temperature value data."); + return; + } else { + ESP_LOGD(TAG, "External compensation data received: Temperature %0.2f°C", temperature); + } + + float absolute_humidity; + if (temperature < 0) { + absolute_humidity = + 216.67f * + ((humidity * 0.061121f * std::exp((23.036f - temperature / 333.7f) * (temperature / (279.82f + temperature)))) / + (273.15f + temperature)); + } else { + absolute_humidity = + 216.67f * + ((humidity * 0.061121f * std::exp((18.678f - temperature / 234.5f) * (temperature / (257.14f + temperature)))) / + (273.15f + temperature)); + } + uint8_t humidity_full = uint8_t(std::floor(absolute_humidity)); + uint8_t humidity_dec = uint8_t(std::floor((absolute_humidity - std::floor(absolute_humidity)) * 256)); + ESP_LOGD(TAG, "Calculated Absolute humidity: %0.3f g/m³ (0x%04X)", absolute_humidity, + uint16_t(uint16_t(humidity_full) << 8 | uint16_t(humidity_dec))); + uint8_t crc = sht_crc_(humidity_full, humidity_dec); + uint8_t data[4]; + data[0] = SGP30_CMD_SET_ABSOLUTE_HUMIDITY & 0xFF; + data[1] = humidity_full; + data[2] = humidity_dec; + data[3] = crc; + if (!this->write_bytes(SGP30_CMD_SET_ABSOLUTE_HUMIDITY >> 8, data, 4)) { + ESP_LOGE(TAG, "Error sending compensation data."); + } +} + +void CustomSGP30Component::write_iaq_baseline_(uint16_t eco2_baseline, uint16_t tvoc_baseline) { + uint8_t data[7]; + data[0] = SGP30_CMD_SET_IAQ_BASELINE & 0xFF; + data[1] = tvoc_baseline >> 8; + data[2] = tvoc_baseline & 0xFF; + data[3] = sht_crc_(data[1], data[2]); + data[4] = eco2_baseline >> 8; + data[5] = eco2_baseline & 0xFF; + data[6] = sht_crc_(data[4], data[5]); + if (!this->write_bytes(SGP30_CMD_SET_IAQ_BASELINE >> 8, data, 7)) { + ESP_LOGE(TAG, "Error applying eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2_baseline, tvoc_baseline); + } else { + ESP_LOGI(TAG, "Initial baselines applied successfully! eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2_baseline, + tvoc_baseline); + } +} + +void CustomSGP30Component::dump_config() { + ESP_LOGCONFIG(TAG, "SGP30:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + switch (this->error_code_) { + case COMMUNICATION_FAILED: + ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + break; + case MEASUREMENT_INIT_FAILED: + ESP_LOGW(TAG, "Measurement Initialization failed!"); + break; + case INVALID_ID: + ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this an SGP30?"); + break; + case UNSUPPORTED_ID: + ESP_LOGW(TAG, "Sensor reported an unsupported ID (SGPC3)."); + break; + default: + ESP_LOGW(TAG, "Unknown setup error!"); + break; + } + } else { + ESP_LOGCONFIG(TAG, " Serial number: %" PRIu64, this->serial_number_); + if (this->eco2_baseline_ != 0x0000 && this->tvoc_baseline_ != 0x0000) { + ESP_LOGCONFIG(TAG, " Baseline:"); + ESP_LOGCONFIG(TAG, " eCO2 Baseline: 0x%04X", this->eco2_baseline_); + ESP_LOGCONFIG(TAG, " TVOC Baseline: 0x%04X", this->tvoc_baseline_); + } else { + ESP_LOGCONFIG(TAG, " Baseline: No baseline configured"); + } + ESP_LOGCONFIG(TAG, " Warm up time: %" PRIu32 "s", this->required_warm_up_time_); + } + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "eCO2 sensor", this->eco2_sensor_); + LOG_SENSOR(" ", "TVOC sensor", this->tvoc_sensor_); + LOG_SENSOR(" ", "eCO2 baseline sensor", this->eco2_sensor_baseline_); + LOG_SENSOR(" ", "TVOC baseline sensor", this->tvoc_sensor_baseline_); + ESP_LOGCONFIG(TAG, "Store baseline: %s", YESNO(this->store_baseline_)); + if (this->humidity_sensor_ != nullptr && this->temperature_sensor_ != nullptr) { + ESP_LOGCONFIG(TAG, " Compensation:"); + LOG_SENSOR(" ", "Temperature Source:", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity Source:", this->humidity_sensor_); + } else { + ESP_LOGCONFIG(TAG, " Compensation: No source configured"); + } +} + +void CustomSGP30Component::update() { + if (!this->write_command(SGP30_CMD_MEASURE_IAQ)) { + this->status_set_warning(); + return; + } + this->seconds_since_last_store_ += this->update_interval_ / 1000; + this->set_timeout(50, [this]() { + uint16_t raw_data[2]; + if (!this->read_data(raw_data, 2)) { + this->status_set_warning(); + return; + } + + float eco2 = (raw_data[0]); + float tvoc = (raw_data[1]); + + ESP_LOGD(TAG, "Got eCO2=%.1fppm TVOC=%.1fppb", eco2, tvoc); + if (this->eco2_sensor_ != nullptr) + this->eco2_sensor_->publish_state(eco2); + if (this->tvoc_sensor_ != nullptr) + this->tvoc_sensor_->publish_state(tvoc); + + if (this->get_update_interval() != 1000) { + ESP_LOGW(TAG, "Update interval for SGP30 sensor must be set to 1s for optimized readout"); + } + + this->status_clear_warning(); + this->send_env_data_(); + this->read_iaq_baseline_(); + }); +} + +} // namespace custom_sgp30 +} // namespace esphome diff --git a/firmware/components/custom_sgp30/sgp30.h b/firmware/components/custom_sgp30/sgp30.h new file mode 100644 index 0000000..5438d7c --- /dev/null +++ b/firmware/components/custom_sgp30/sgp30.h @@ -0,0 +1,71 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/sensirion_common/i2c_sensirion.h" +#include "esphome/core/preferences.h" + +#include +#include + +namespace esphome { +namespace custom_sgp30 { + +struct CustomSGP30Baselines { + uint16_t eco2; + uint16_t tvoc; +} PACKED; + +/// This class implements support for the Sensirion SGP30 i2c GAS (VOC and CO2eq) sensors. +class CustomSGP30Component : public PollingComponent, public sensirion_common::SensirionI2CDevice { + public: + void set_eco2_sensor(sensor::Sensor *eco2) { eco2_sensor_ = eco2; } + void set_tvoc_sensor(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; } + void set_eco2_baseline_sensor(sensor::Sensor *eco2_baseline) { eco2_sensor_baseline_ = eco2_baseline; } + void set_tvoc_baseline_sensor(sensor::Sensor *tvoc_baseline) { tvoc_sensor_baseline_ = tvoc_baseline; } + void set_store_baseline(bool store_baseline) { store_baseline_ = store_baseline; } + void set_eco2_baseline(uint16_t eco2_baseline) { eco2_baseline_ = eco2_baseline; } + void set_tvoc_baseline(uint16_t tvoc_baseline) { tvoc_baseline_ = tvoc_baseline; } + void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } + void set_temperature_sensor(sensor::Sensor *temperature) { temperature_sensor_ = temperature; } + + void setup() override; + void update() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + void send_env_data_(); + void read_iaq_baseline_(); + bool is_sensor_baseline_reliable_(); + void write_iaq_baseline_(uint16_t eco2_baseline, uint16_t tvoc_baseline); + uint64_t serial_number_; + uint16_t featureset_; + uint32_t required_warm_up_time_; + uint32_t seconds_since_last_store_; + CustomSGP30Baselines baselines_storage_; + ESPPreferenceObject pref_; + + enum ErrorCode { + COMMUNICATION_FAILED, + MEASUREMENT_INIT_FAILED, + INVALID_ID, + UNSUPPORTED_ID, + UNKNOWN + } error_code_{UNKNOWN}; + + sensor::Sensor *eco2_sensor_{nullptr}; + sensor::Sensor *tvoc_sensor_{nullptr}; + sensor::Sensor *eco2_sensor_baseline_{nullptr}; + sensor::Sensor *tvoc_sensor_baseline_{nullptr}; + uint16_t eco2_baseline_{0x0000}; + uint16_t tvoc_baseline_{0x0000}; + bool store_baseline_; + + /// Input sensor for humidity and temperature compensation. + sensor::Sensor *humidity_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_{nullptr}; +}; + +} // namespace custom_sgp30 +} // namespace esphome diff --git a/firmware/datenzwerg.yaml b/firmware/datenzwerg.yaml index 5e86f32..65b132c 100644 --- a/firmware/datenzwerg.yaml +++ b/firmware/datenzwerg.yaml @@ -1,4 +1,6 @@ packages: base: !include packages/base.yaml - deepsleep: !include packages/deepsleep.yaml + #deepsleep: !include packages/deepsleep.yaml #ota: !include packages/ota.yaml + uptime: !include packages/uptime.yaml + #webserver: !include packages/webserver.yaml diff --git a/firmware/packages/base.yaml b/firmware/packages/base.yaml index 36eed0c..3a69f2b 100644 --- a/firmware/packages/base.yaml +++ b/firmware/packages/base.yaml @@ -21,7 +21,7 @@ external_components: - source: type: local path: components - components: [ peak_to_peak ] + components: [ peak_to_peak, custom_sgp30 ] influxdb2: host: !secret influxdb_host @@ -86,7 +86,7 @@ sensor: address: 0x76 update_interval: ${update_interval} - - platform: sgp30 + - platform: custom_sgp30 eco2: name: "eCO2" id: eco2 @@ -95,11 +95,17 @@ sensor: name: "TVOC" id: tvoc accuracy_decimals: 2 + eco2_baseline: + name: "eCO2 Baseline" + id: eco2_baseline + tvoc_baseline: + name: "TVOC Baseline" + id: tvoc_baseline compensation: humidity_source: bme280_humidity temperature_source: bme280_temperature store_baseline: yes - update_interval: ${update_interval} + update_interval: 1s - platform: ads1115 gain: 6.144 diff --git a/firmware/packages/uptime.yaml b/firmware/packages/uptime.yaml new file mode 100644 index 0000000..f4082a8 --- /dev/null +++ b/firmware/packages/uptime.yaml @@ -0,0 +1,3 @@ +sensor: + - platform: uptime + name: Uptime diff --git a/firmware/packages/webserver.yaml b/firmware/packages/webserver.yaml new file mode 100644 index 0000000..c3bc240 --- /dev/null +++ b/firmware/packages/webserver.yaml @@ -0,0 +1,2 @@ +web_server: + port: 80 \ No newline at end of file