From 14bcc5ff7534785f1ecc6b7372bcb2fb1d14a600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Ryb=C3=A1rsky?= Date: Sat, 7 Dec 2024 17:02:06 +0100 Subject: [PATCH] init --- components/kqm6600ta/__init__.py | 0 components/kqm6600ta/kqm6600ta.cpp | 70 ++++++++++++++++++++++++++++++ components/kqm6600ta/kqm6600ta.h | 31 +++++++++++++ components/kqm6600ta/sensor.py | 70 ++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 components/kqm6600ta/__init__.py create mode 100644 components/kqm6600ta/kqm6600ta.cpp create mode 100644 components/kqm6600ta/kqm6600ta.h create mode 100644 components/kqm6600ta/sensor.py diff --git a/components/kqm6600ta/__init__.py b/components/kqm6600ta/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/kqm6600ta/kqm6600ta.cpp b/components/kqm6600ta/kqm6600ta.cpp new file mode 100644 index 0000000..0011bb2 --- /dev/null +++ b/components/kqm6600ta/kqm6600ta.cpp @@ -0,0 +1,70 @@ +#include "kqm6600ta.h" +#include "esphome/core/log.h" + +#include + +namespace esphome +{ + namespace kqm6600ta + { + + static const char *const TAG = "kqm6600ta"; + static const uint8_t KQM6600TA_RESPONSE_LENGTH = 8; + + uint8_t kqm6600ta_checksum(const uint8_t *command) + { + uint8_t sum = 0; + for (uint8_t i = 0; i < KQM6600TA_REQUEST_LENGTH; i++) + { + sum += command[i]; + } + return sum; + } + + void KQM6600TAComponent::update() + { + uint32_t now_ms = millis(); + + if (response[0] != 0x5F) + { + ESP_LOGW(TAG, "Invalid preamble from KQM6600TA!"); + this->status_set_warning(); + return; + } + + uint8_t checksum = kqm6600ta_checksum(response); + if (response[7] != checksum) + { + ESP_LOGW(TAG, "KQM6600TA Checksum doesn't match: 0x%02X!=0x%02X", response[8], checksum); + this->status_set_warning(); + return; + } + + this->status_clear_warning(); + const float voc = ((uint16_t(response[1]) << 8) | response[2]) * 0.1f; + const uint32_t formaldehyde = ((uint16_t(response[3]) << 8) | response[4]) * 10; + const uint16_t co2 = ((uint16_t(response[5]) << 8) | response[6]) * 10; + + + ESP_LOGD(TAG, "KQM6600TA Received VOC=%f Formaldehyde=%dmg/m³ CO₂=%dppm", voc, formaldehyde, co2); + if (this->voc_sensor_ != nullptr) + this->voc_sensor_->publish_state(voc); + if (this->formaldehyde_sensor_ != nullptr) + this->formaldehyde_sensor_->publish_state(formaldehyde); + if (this->co2_sensor_ != nullptr) + this->co2_sensor_->publish_state(co2); + } + + float KQM6600TAComponent::get_setup_priority() const { return setup_priority::DATA; } + + void MHZ19Component::dump_config() + { + ESP_LOGCONFIG(TAG, "KQM6600TA:"); + LOG_SENSOR(" ", "CO2", this->co2_sensor_); + LOG_SENSOR(" ", "VOC", this->voc_sensor_); + LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_); + this->check_uart_settings(9600); + } + + } // namespace kqm6600ta +} // namespace esphome \ No newline at end of file diff --git a/components/kqm6600ta/kqm6600ta.h b/components/kqm6600ta/kqm6600ta.h new file mode 100644 index 0000000..a01cf42 --- /dev/null +++ b/components/kqm6600ta/kqm6600ta.h @@ -0,0 +1,31 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace kqm6600ta { + +class KQM6600TAComponent : public PollingComponent, public uart::UARTDevice { + public: + float get_setup_priority() const override; + + void setup() override; + void update() override; + void dump_config() override; + + void set_formaldehyde_sensor(sensor::Sensor *formaldehyde_sensor) { formaldehyde_sensor_ = formaldehyde_sensor; } + void set_voc_sensor(sensor::Sensor *voc_sensor) { voc_sensor_ = formaldehyde_sensor; } + void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; } + + protected: + + sensor::Sensor *formaldehyde_sensor_{nullptr}; + sensor::Sensor *voc_sensor_{nullptr}; + sensor::Sensor *co2_sensor_{nullptr}; +}; + +} // namespace kqm6600ta +} // namespace esphome \ No newline at end of file diff --git a/components/kqm6600ta/sensor.py b/components/kqm6600ta/sensor.py new file mode 100644 index 0000000..d9eb1f9 --- /dev/null +++ b/components/kqm6600ta/sensor.py @@ -0,0 +1,70 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, uart +from esphome.const import ( + CONF_CO2, + CONF_ID, + UNIT_MICROGRAMS_PER_CUBIC_METER, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, + DEVICE_CLASS_CARBON_DIOXIDE, + ICON_MOLECULE_CO2, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_MILLION, + CONF_TVOC, + CONF_FORMADEHYDE, + DEVICE_CLASS_GAS, +) + +DEPENDENCIES = ["uart"] + +kqm6600ta_ns = cg.esphome_ns.namespace("kqm6600ta") +KQM6600TAComponent = kqm6600ta_ns.class_("KQM6600TAComponent", cg.PollingComponent, uart.UARTDevice) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(KQM6600TAComponent), + cv.Required(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, + ), + cv.Required(CONF_TVOC): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + icon="mdi:molecule", + accuracy_decimals=1, #needs to be divided by 10 + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Required(CONF_FORMADEHYDE): sensor.sensor_schema( + unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, + icon="mdi:molecule", + accuracy_decimals=0, #needs to be multiplied by 10 + device_class=DEVICE_CLASS_GAS, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("5s")) + .extend(uart.UART_DEVICE_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + + if CONF_CO2 in config: + sens = await sensor.new_sensor(config[CONF_CO2]) + cg.add(var.set_co2_sensor(sens)) + + if CONF_TVOC in config: + sens = await sensor.new_sensor(config[CONF_TVOC]) + cg.add(var.set_voc_sensor(sens)) + + if CONF_FORMADEHYDE in config: + sens = await sensor.new_sensor(config[CONF_FORMADEHYDE]) + cg.add(var.set_formaldehyde_sensor(sens))