From 6fe22a7e629edf9243ba822d77a75f4587e6afd2 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Mon, 25 Apr 2022 23:50:36 +0200 Subject: [PATCH] SPS30: Add fan action (#3410) * Add fan action to SPS30 * add codeowner --- CODEOWNERS | 1 + esphome/components/sps30/automation.h | 21 +++++++++++++++++++ esphome/components/sps30/sensor.py | 27 ++++++++++++++++++++++++ esphome/components/sps30/sps30.cpp | 30 ++++++++++++++++++++++++++- esphome/components/sps30/sps30.h | 5 ++++- 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 esphome/components/sps30/automation.h diff --git a/CODEOWNERS b/CODEOWNERS index 7fd049f46e..e2a356360a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -183,6 +183,7 @@ esphome/components/sm2135/* @BoukeHaarsma23 esphome/components/socket/* @esphome/core esphome/components/sonoff_d1/* @anatoly-savchenkov esphome/components/spi/* @esphome/core +esphome/components/sps30/* @martgras esphome/components/ssd1322_base/* @kbx81 esphome/components/ssd1322_spi/* @kbx81 esphome/components/ssd1325_base/* @kbx81 diff --git a/esphome/components/sps30/automation.h b/esphome/components/sps30/automation.h new file mode 100644 index 0000000000..443aafb575 --- /dev/null +++ b/esphome/components/sps30/automation.h @@ -0,0 +1,21 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "sps30.h" + +namespace esphome { +namespace sps30 { + +template class StartFanAction : public Action { + public: + explicit StartFanAction(SPS30Component *sps30) : sps30_(sps30) {} + + void play(Ts... x) override { this->sps30_->start_fan_cleaning(); } + + protected: + SPS30Component *sps30_; +}; + +} // namespace sps30 +} // namespace esphome diff --git a/esphome/components/sps30/sensor.py b/esphome/components/sps30/sensor.py index 89cb25c24f..ff8d5a3594 100644 --- a/esphome/components/sps30/sensor.py +++ b/esphome/components/sps30/sensor.py @@ -1,6 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor, sensirion_common +from esphome import automation +from esphome.automation import maybe_simple_id from esphome.const import ( CONF_ID, CONF_PM_1_0, @@ -25,6 +27,7 @@ from esphome.const import ( ICON_RULER, ) +CODEOWNERS = ["@martgras"] DEPENDENCIES = ["i2c"] AUTO_LOAD = ["sensirion_common"] @@ -33,6 +36,11 @@ SPS30Component = sps30_ns.class_( "SPS30Component", cg.PollingComponent, sensirion_common.SensirionI2CDevice ) +# Actions +StartFanAction = sps30_ns.class_("StartFanAction", automation.Action) + +CONF_AUTO_CLEANING_INTERVAL = "auto_cleaning_interval" + CONFIG_SCHEMA = ( cv.Schema( { @@ -100,6 +108,7 @@ CONFIG_SCHEMA = ( accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_AUTO_CLEANING_INTERVAL): cv.update_interval, } ) .extend(cv.polling_component_schema("60s")) @@ -151,3 +160,21 @@ async def to_code(config): if CONF_PM_SIZE in config: sens = await sensor.new_sensor(config[CONF_PM_SIZE]) cg.add(var.set_pm_size_sensor(sens)) + + if CONF_AUTO_CLEANING_INTERVAL in config: + cg.add(var.set_auto_cleaning_interval(config[CONF_AUTO_CLEANING_INTERVAL])) + + +SPS30_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(SPS30Component), + } +) + + +@automation.register_action( + "sps30.start_fan_autoclean", StartFanAction, SPS30_ACTION_SCHEMA +) +async def sps30_fan_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) diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index 2885125a8a..cdcd4a6a54 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -1,5 +1,6 @@ -#include "sps30.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" +#include "sps30.h" namespace esphome { namespace sps30 { @@ -44,6 +45,22 @@ void SPS30Component::setup() { this->serial_number_[i * 2 + 1] = uint16_t(uint16_t(raw_serial_number[i] & 0xFF)); } ESP_LOGD(TAG, " Serial Number: '%s'", this->serial_number_); + + bool result; + if (this->fan_interval_.has_value()) { + // override default value + result = write_command(SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS, this->fan_interval_.value()); + } else { + result = write_command(SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS); + } + if (result) { + delay(20); + uint16_t secs[2]; + if (this->read_data(secs, 2)) { + fan_interval_ = secs[0] << 16 | secs[1]; + } + } + this->status_clear_warning(); this->skipped_data_read_cycles_ = 0; this->start_continuous_measurement_(); @@ -206,5 +223,16 @@ bool SPS30Component::start_continuous_measurement_() { return true; } +bool SPS30Component::start_fan_cleaning() { + if (!write_command(SPS30_CMD_START_FAN_CLEANING)) { + this->status_set_warning(); + ESP_LOGE(TAG, "write error start fan (%d)", this->last_error_); + return false; + } else { + ESP_LOGD(TAG, "Fan auto clean started"); + } + return true; +} + } // namespace sps30 } // namespace esphome diff --git a/esphome/components/sps30/sps30.h b/esphome/components/sps30/sps30.h index 9a93df8597..cf2e7a7d4f 100644 --- a/esphome/components/sps30/sps30.h +++ b/esphome/components/sps30/sps30.h @@ -22,12 +22,14 @@ class SPS30Component : public PollingComponent, public sensirion_common::Sensiri void set_pmc_10_0_sensor(sensor::Sensor *pmc_10_0) { pmc_10_0_sensor_ = pmc_10_0; } void set_pm_size_sensor(sensor::Sensor *pm_size) { pm_size_sensor_ = pm_size; } - + void set_auto_cleaning_interval(uint32_t auto_cleaning_interval) { fan_interval_ = auto_cleaning_interval; } void setup() override; void update() override; void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } + bool start_fan_cleaning(); + protected: char serial_number_[17] = {0}; /// Terminating NULL character uint16_t raw_firmware_version_; @@ -54,6 +56,7 @@ class SPS30Component : public PollingComponent, public sensirion_common::Sensiri sensor::Sensor *pmc_4_0_sensor_{nullptr}; sensor::Sensor *pmc_10_0_sensor_{nullptr}; sensor::Sensor *pm_size_sensor_{nullptr}; + optional fan_interval_; }; } // namespace sps30