mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 23:48:11 +01:00
Merge branch 'dev' into optolink
This commit is contained in:
commit
b1d09d1de5
28 changed files with 1158 additions and 21 deletions
|
@ -85,6 +85,7 @@ esphome/components/dsmr/* @glmnet @zuidwijk
|
|||
esphome/components/duty_time/* @dudanov
|
||||
esphome/components/ee895/* @Stock-M
|
||||
esphome/components/ektf2232/* @jesserockz
|
||||
esphome/components/emc2101/* @ellull
|
||||
esphome/components/ens210/* @itn3rd77
|
||||
esphome/components/esp32/* @esphome/core
|
||||
esphome/components/esp32_ble/* @jesserockz
|
||||
|
@ -122,6 +123,7 @@ esphome/components/hitachi_ac424/* @sourabhjaiswal
|
|||
esphome/components/hm3301/* @freekode
|
||||
esphome/components/homeassistant/* @OttoWinter
|
||||
esphome/components/honeywellabp/* @RubyBailey
|
||||
esphome/components/honeywellabp2_i2c/* @jpfaff
|
||||
esphome/components/host/* @esphome/core
|
||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||
esphome/components/hte501/* @Stock-M
|
||||
|
@ -233,6 +235,7 @@ esphome/components/pulse_meter/* @TrentHouliston @cstaahl @stevebaxter
|
|||
esphome/components/pvvx_mithermometer/* @pasiz
|
||||
esphome/components/qmp6988/* @andrewpc
|
||||
esphome/components/qr_code/* @wjtje
|
||||
esphome/components/qwiic_pir/* @kahrendt
|
||||
esphome/components/radon_eye_ble/* @jeffeb3
|
||||
esphome/components/radon_eye_rd200/* @jeffeb3
|
||||
esphome/components/rc522/* @glmnet
|
||||
|
|
|
@ -104,7 +104,8 @@ void CurrentBasedCover::loop() {
|
|||
ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur);
|
||||
this->direction_idle_(COVER_CLOSED);
|
||||
}
|
||||
} else if (now - this->start_dir_time_ > this->max_duration_) {
|
||||
}
|
||||
if (now - this->start_dir_time_ > this->max_duration_) {
|
||||
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
|
||||
this->direction_idle_();
|
||||
}
|
||||
|
|
81
esphome/components/emc2101/__init__.py
Normal file
81
esphome/components/emc2101/__init__.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ID, CONF_INVERTED, CONF_RESOLUTION
|
||||
|
||||
CODEOWNERS = ["@ellull"]
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_PWM = "pwm"
|
||||
CONF_DIVIDER = "divider"
|
||||
CONF_DAC = "dac"
|
||||
CONF_CONVERSION_RATE = "conversion_rate"
|
||||
|
||||
CONF_EMC2101_ID = "emc2101_id"
|
||||
|
||||
emc2101_ns = cg.esphome_ns.namespace("emc2101")
|
||||
|
||||
Emc2101DACConversionRate = emc2101_ns.enum("Emc2101DACConversionRate")
|
||||
CONVERSIONS_PER_SECOND = {
|
||||
"1/16": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_16S,
|
||||
"1/8": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_8S,
|
||||
"1/4": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_4S,
|
||||
"1/2": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_2S,
|
||||
"1": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_SECOND,
|
||||
"2": Emc2101DACConversionRate.Emc2101_DAC_2_EVERY_SECOND,
|
||||
"4": Emc2101DACConversionRate.Emc2101_DAC_4_EVERY_SECOND,
|
||||
"8": Emc2101DACConversionRate.Emc2101_DAC_8_EVERY_SECOND,
|
||||
"16": Emc2101DACConversionRate.Emc2101_DAC_16_EVERY_SECOND,
|
||||
"32": Emc2101DACConversionRate.Emc2101_DAC_32_EVERY_SECOND,
|
||||
}
|
||||
|
||||
Emc2101Component = emc2101_ns.class_("Emc2101Component", cg.Component, i2c.I2CDevice)
|
||||
|
||||
EMC2101_COMPONENT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_EMC2101_ID): cv.use_id(Emc2101Component),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Emc2101Component),
|
||||
cv.Optional(CONF_PWM): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_RESOLUTION, default=23): cv.int_range(
|
||||
min=0, max=31
|
||||
),
|
||||
cv.Optional(CONF_DIVIDER, default=1): cv.uint8_t,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_DAC): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_CONVERSION_RATE, default="16"): cv.enum(
|
||||
CONVERSIONS_PER_SECOND
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(i2c.i2c_device_schema(0x4C)),
|
||||
cv.has_exactly_one_key(CONF_PWM, CONF_DAC),
|
||||
)
|
||||
|
||||
|
||||
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 pwm_config := config.get(CONF_PWM):
|
||||
cg.add(var.set_dac_mode(False))
|
||||
cg.add(var.set_pwm_resolution(pwm_config[CONF_RESOLUTION]))
|
||||
cg.add(var.set_pwm_divider(pwm_config[CONF_DIVIDER]))
|
||||
if dac_config := config.get(CONF_DAC):
|
||||
cg.add(var.set_dac_mode(True))
|
||||
cg.add(var.set_dac_conversion_rate(dac_config[CONF_CONVERSION_RATE]))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
169
esphome/components/emc2101/emc2101.cpp
Normal file
169
esphome/components/emc2101/emc2101.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
// Implementation based on:
|
||||
// - Adafruit_EMC2101: https://github.com/adafruit/Adafruit_EMC2101
|
||||
// - Official Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/2101.pdf
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "emc2101.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace emc2101 {
|
||||
|
||||
static const char *const TAG = "EMC2101";
|
||||
|
||||
static const uint8_t EMC2101_CHIP_ID = 0x16; // EMC2101 default device id from part id
|
||||
static const uint8_t EMC2101_ALT_CHIP_ID = 0x28; // EMC2101 alternate device id from part id
|
||||
|
||||
// EMC2101 registers from the datasheet. We only define what we use.
|
||||
static const uint8_t EMC2101_REGISTER_INTERNAL_TEMP = 0x00; // The internal temperature register
|
||||
static const uint8_t EMC2101_REGISTER_EXTERNAL_TEMP_MSB = 0x01; // high byte for the external temperature reading
|
||||
static const uint8_t EMC2101_REGISTER_DAC_CONV_RATE = 0x04; // DAC convesion rate config
|
||||
static const uint8_t EMC2101_REGISTER_EXTERNAL_TEMP_LSB = 0x10; // low byte for the external temperature reading
|
||||
static const uint8_t EMC2101_REGISTER_CONFIG = 0x03; // configuration register
|
||||
static const uint8_t EMC2101_REGISTER_TACH_LSB = 0x46; // Tach RPM data low byte
|
||||
static const uint8_t EMC2101_REGISTER_TACH_MSB = 0x47; // Tach RPM data high byte
|
||||
static const uint8_t EMC2101_REGISTER_FAN_CONFIG = 0x4A; // General fan config register
|
||||
static const uint8_t EMC2101_REGISTER_FAN_SETTING = 0x4C; // Fan speed for non-LUT settings
|
||||
static const uint8_t EMC2101_REGISTER_PWM_FREQ = 0x4D; // PWM frequency setting
|
||||
static const uint8_t EMC2101_REGISTER_PWM_DIV = 0x4E; // PWM frequency divisor
|
||||
static const uint8_t EMC2101_REGISTER_WHOAMI = 0xFD; // Chip ID register
|
||||
|
||||
// EMC2101 configuration bits from the datasheet. We only define what we use.
|
||||
|
||||
// Determines the funcionallity of the ALERT/TACH pin.
|
||||
// 0 (default): The ALERT/TECH pin will function as an open drain, active low interrupt.
|
||||
// 1: The ALERT/TECH pin will function as a high impedance TACH input. This may require an
|
||||
// external pull-up resistor to set the proper signaling levels.
|
||||
static const uint8_t EMC2101_ALT_TCH_BIT = 1 << 2;
|
||||
|
||||
// Determines the FAN output mode.
|
||||
// 0 (default): PWM output enabled at FAN pin.
|
||||
// 1: DAC output enabled at FAN ping.
|
||||
static const uint8_t EMC2101_DAC_BIT = 1 << 4;
|
||||
|
||||
// Overrides the CLK_SEL bit and uses the Frequency Divide Register to determine
|
||||
// the base PWM frequency. It is recommended that this bit be set for maximum PWM resolution.
|
||||
// 0 (default): The base clock frequency for the PWM is determined by the CLK_SEL bit.
|
||||
// 1 (recommended): The base clock that is used to determine the PWM frequency is set by the
|
||||
// Frequency Divide Register
|
||||
static const uint8_t EMC2101_CLK_OVR_BIT = 1 << 2;
|
||||
|
||||
// Sets the polarity of the Fan output driver.
|
||||
// 0 (default): The polarity of the Fan output driver is non-inverted. A '00h' setting will
|
||||
// correspond to a 0% duty cycle or a minimum DAC output voltage.
|
||||
// 1: The polarity of the Fan output driver is inverted. A '00h' setting will correspond to a
|
||||
// 100% duty cycle or a maximum DAC output voltage.
|
||||
static const uint8_t EMC2101_POLARITY_BIT = 1 << 4;
|
||||
|
||||
float Emc2101Component::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
void Emc2101Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Emc2101 sensor...");
|
||||
|
||||
// make sure we're talking to the right chip
|
||||
uint8_t chip_id = reg(EMC2101_REGISTER_WHOAMI).get();
|
||||
if ((chip_id != EMC2101_CHIP_ID) && (chip_id != EMC2101_ALT_CHIP_ID)) {
|
||||
ESP_LOGE(TAG, "Wrong chip ID %02X", chip_id);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure EMC2101
|
||||
i2c::I2CRegister config = reg(EMC2101_REGISTER_CONFIG);
|
||||
config |= EMC2101_ALT_TCH_BIT;
|
||||
if (this->dac_mode_) {
|
||||
config |= EMC2101_DAC_BIT;
|
||||
}
|
||||
if (this->inverted_) {
|
||||
config |= EMC2101_POLARITY_BIT;
|
||||
}
|
||||
|
||||
if (this->dac_mode_) { // DAC mode configurations
|
||||
// set DAC conversion rate
|
||||
reg(EMC2101_REGISTER_DAC_CONV_RATE) = this->dac_conversion_rate_;
|
||||
} else { // PWM mode configurations
|
||||
// set PWM divider
|
||||
reg(EMC2101_REGISTER_FAN_CONFIG) |= EMC2101_CLK_OVR_BIT;
|
||||
reg(EMC2101_REGISTER_PWM_DIV) = this->pwm_divider_;
|
||||
|
||||
// set PWM resolution
|
||||
reg(EMC2101_REGISTER_PWM_FREQ) = this->pwm_resolution_;
|
||||
}
|
||||
}
|
||||
|
||||
void Emc2101Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Emc2101 component:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Mode: %s", this->dac_mode_ ? "DAC" : "PWM");
|
||||
if (this->dac_mode_) {
|
||||
ESP_LOGCONFIG(TAG, " DAC Conversion Rate: %X", this->dac_conversion_rate_);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " PWM Resolution: %02X", this->pwm_resolution_);
|
||||
ESP_LOGCONFIG(TAG, " PWM Divider: %02X", this->pwm_divider_);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Inverted: %s", YESNO(this->inverted_));
|
||||
}
|
||||
|
||||
void Emc2101Component::set_duty_cycle(float value) {
|
||||
uint8_t duty_cycle = remap(value, 0.0f, 1.0f, (uint8_t) 0, this->max_output_value_);
|
||||
ESP_LOGD(TAG, "Setting duty fan setting to %02X", duty_cycle);
|
||||
if (!this->write_byte(EMC2101_REGISTER_FAN_SETTING, duty_cycle)) {
|
||||
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float Emc2101Component::get_duty_cycle() {
|
||||
uint8_t duty_cycle;
|
||||
if (!this->read_byte(EMC2101_REGISTER_FAN_SETTING, &duty_cycle)) {
|
||||
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
return remap(duty_cycle, (uint8_t) 0, this->max_output_value_, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
float Emc2101Component::get_internal_temperature() {
|
||||
uint8_t temperature;
|
||||
if (!this->read_byte(EMC2101_REGISTER_INTERNAL_TEMP, &temperature)) {
|
||||
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
return temperature;
|
||||
}
|
||||
|
||||
float Emc2101Component::get_external_temperature() {
|
||||
// Read **MSB** first to match 'Data Read Interlock' behavior from 6.1 of datasheet
|
||||
uint8_t lsb, msb;
|
||||
if (!this->read_byte(EMC2101_REGISTER_EXTERNAL_TEMP_MSB, &msb) ||
|
||||
!this->read_byte(EMC2101_REGISTER_EXTERNAL_TEMP_LSB, &lsb)) {
|
||||
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// join msb and lsb (5 least significant bits are not used)
|
||||
uint16_t raw = (msb << 8 | lsb) >> 5;
|
||||
return raw * 0.125;
|
||||
}
|
||||
|
||||
float Emc2101Component::get_speed() {
|
||||
// Read **LSB** first to match 'Data Read Interlock' behavior from 6.1 of datasheet
|
||||
uint8_t lsb, msb;
|
||||
if (!this->read_byte(EMC2101_REGISTER_TACH_LSB, &lsb) || !this->read_byte(EMC2101_REGISTER_TACH_MSB, &msb)) {
|
||||
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// calculate RPMs
|
||||
uint16_t tach = msb << 8 | lsb;
|
||||
return tach == 0xFFFF ? 0.0f : 5400000.0f / tach;
|
||||
}
|
||||
|
||||
} // namespace emc2101
|
||||
} // namespace esphome
|
115
esphome/components/emc2101/emc2101.h
Normal file
115
esphome/components/emc2101/emc2101.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace emc2101 {
|
||||
|
||||
/** Enum listing all DAC conversion rates for the EMC2101.
|
||||
*
|
||||
* Specific values of the enum constants are register values taken from the EMC2101 datasheet.
|
||||
*/
|
||||
enum Emc2101DACConversionRate {
|
||||
EMC2101_DAC_1_EVERY_16_S,
|
||||
EMC2101_DAC_1_EVERY_8_S,
|
||||
EMC2101_DAC_1_EVERY_4_S,
|
||||
EMC2101_DAC_1_EVERY_2_S,
|
||||
EMC2101_DAC_1_EVERY_SECOND,
|
||||
EMC2101_DAC_2_EVERY_SECOND,
|
||||
EMC2101_DAC_4_EVERY_SECOND,
|
||||
EMC2101_DAC_8_EVERY_SECOND,
|
||||
EMC2101_DAC_16_EVERY_SECOND,
|
||||
EMC2101_DAC_32_EVERY_SECOND,
|
||||
};
|
||||
|
||||
/// This class includes support for the EMC2101 i2c fan controller.
|
||||
/// The device has an output (PWM or DAC) and several sensors and this
|
||||
/// class is for the EMC2101 configuration.
|
||||
class Emc2101Component : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
/** Sets the mode of the output.
|
||||
*
|
||||
* @param dac_mode false for PWM output and true for DAC mode.
|
||||
*/
|
||||
void set_dac_mode(bool dac_mode) {
|
||||
this->dac_mode_ = dac_mode;
|
||||
this->max_output_value_ = 63;
|
||||
}
|
||||
|
||||
/** Sets the PWM resolution.
|
||||
*
|
||||
* @param resolution the PWM resolution.
|
||||
*/
|
||||
void set_pwm_resolution(uint8_t resolution) {
|
||||
this->pwm_resolution_ = resolution;
|
||||
this->max_output_value_ = 2 * resolution;
|
||||
}
|
||||
|
||||
/** Sets the PWM divider used to derive the PWM frequency.
|
||||
*
|
||||
* @param divider The PWM divider.
|
||||
*/
|
||||
void set_pwm_divider(uint8_t divider) { this->pwm_divider_ = divider; }
|
||||
|
||||
/** Sets the DAC conversion rate (how many conversions per second).
|
||||
*
|
||||
* @param conversion_rate The DAC conversion rate.
|
||||
*/
|
||||
void set_dac_conversion_rate(Emc2101DACConversionRate conversion_rate) {
|
||||
this->dac_conversion_rate_ = conversion_rate;
|
||||
}
|
||||
|
||||
/** Inverts the polarity of the Fan output.
|
||||
*
|
||||
* @param inverted Invert or not the Fan output.
|
||||
*/
|
||||
void set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||
|
||||
/** Sets the Fan output duty cycle
|
||||
*
|
||||
* @param value The duty cycle value, from 0.0f to 1.0f.
|
||||
*/
|
||||
void set_duty_cycle(float value);
|
||||
|
||||
/** Gets the Fan output duty cycle
|
||||
*
|
||||
* @return The duty cycle percentage from 0.0f to 1.0f.
|
||||
*/
|
||||
float get_duty_cycle();
|
||||
|
||||
/** Gets the internal temperature sensor reading.
|
||||
*
|
||||
* @return The temperature in degrees celsius.
|
||||
*/
|
||||
float get_internal_temperature();
|
||||
|
||||
/** Gets the external temperature sensor reading.
|
||||
*
|
||||
* @return The temperature in degrees celsius.
|
||||
*/
|
||||
float get_external_temperature();
|
||||
|
||||
/** Gets the tachometer speed sensor reading.
|
||||
*
|
||||
* @return The fan speed in RPMs.
|
||||
*/
|
||||
float get_speed();
|
||||
|
||||
/** Used by ESPHome framework. */
|
||||
void setup() override;
|
||||
/** Used by ESPHome framework. */
|
||||
void dump_config() override;
|
||||
/** Used by ESPHome framework. */
|
||||
float get_setup_priority() const override;
|
||||
|
||||
bool dac_mode_{false};
|
||||
bool inverted_{false};
|
||||
uint8_t max_output_value_;
|
||||
uint8_t pwm_resolution_;
|
||||
uint8_t pwm_divider_;
|
||||
Emc2101DACConversionRate dac_conversion_rate_;
|
||||
};
|
||||
|
||||
} // namespace emc2101
|
||||
} // namespace esphome
|
21
esphome/components/emc2101/output/__init__.py
Normal file
21
esphome/components/emc2101/output/__init__.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import output
|
||||
from esphome.const import CONF_ID
|
||||
from .. import EMC2101_COMPONENT_SCHEMA, CONF_EMC2101_ID, emc2101_ns
|
||||
|
||||
DEPENDENCIES = ["emc2101"]
|
||||
|
||||
EMC2101Output = emc2101_ns.class_("EMC2101Output", output.FloatOutput)
|
||||
|
||||
CONFIG_SCHEMA = EMC2101_COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(EMC2101Output),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_EMC2101_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
await output.register_output(var, config)
|
9
esphome/components/emc2101/output/emc2101_output.cpp
Normal file
9
esphome/components/emc2101/output/emc2101_output.cpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "emc2101_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace emc2101 {
|
||||
|
||||
void EMC2101Output::write_state(float state) { this->parent_->set_duty_cycle(state); }
|
||||
|
||||
} // namespace emc2101
|
||||
} // namespace esphome
|
22
esphome/components/emc2101/output/emc2101_output.h
Normal file
22
esphome/components/emc2101/output/emc2101_output.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "../emc2101.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace emc2101 {
|
||||
|
||||
/// This class allows to control the EMC2101 output.
|
||||
class EMC2101Output : public output::FloatOutput {
|
||||
public:
|
||||
EMC2101Output(Emc2101Component *parent) : parent_(parent) {}
|
||||
|
||||
protected:
|
||||
/** Used by ESPHome framework. */
|
||||
void write_state(float state) override;
|
||||
|
||||
Emc2101Component *parent_;
|
||||
};
|
||||
|
||||
} // namespace emc2101
|
||||
} // namespace esphome
|
74
esphome/components/emc2101/sensor/__init__.py
Normal file
74
esphome/components/emc2101/sensor/__init__.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_SPEED,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PERCENT,
|
||||
UNIT_REVOLUTIONS_PER_MINUTE,
|
||||
ICON_PERCENT,
|
||||
)
|
||||
from .. import EMC2101_COMPONENT_SCHEMA, CONF_EMC2101_ID, emc2101_ns
|
||||
|
||||
DEPENDENCIES = ["emc2101"]
|
||||
|
||||
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
|
||||
CONF_EXTERNAL_TEMPERATURE = "external_temperature"
|
||||
CONF_DUTY_CYCLE = "duty_cycle"
|
||||
|
||||
EMC2101Sensor = emc2101_ns.class_("EMC2101Sensor", cg.PollingComponent)
|
||||
|
||||
CONFIG_SCHEMA = EMC2101_COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(EMC2101Sensor),
|
||||
cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_SPEED): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_REVOLUTIONS_PER_MINUTE,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
icon="mdi:fan",
|
||||
),
|
||||
cv.Optional(CONF_DUTY_CYCLE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
icon=ICON_PERCENT,
|
||||
),
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s"))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_EMC2101_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_INTERNAL_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_INTERNAL_TEMPERATURE])
|
||||
cg.add(var.set_internal_temperature_sensor(sens))
|
||||
|
||||
if CONF_EXTERNAL_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_EXTERNAL_TEMPERATURE])
|
||||
cg.add(var.set_external_temperature_sensor(sens))
|
||||
|
||||
if CONF_SPEED in config:
|
||||
sens = await sensor.new_sensor(config[CONF_SPEED])
|
||||
cg.add(var.set_speed_sensor(sens))
|
||||
|
||||
if CONF_DUTY_CYCLE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_DUTY_CYCLE])
|
||||
cg.add(var.set_duty_cycle_sensor(sens))
|
43
esphome/components/emc2101/sensor/emc2101_sensor.cpp
Normal file
43
esphome/components/emc2101/sensor/emc2101_sensor.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "emc2101_sensor.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace emc2101 {
|
||||
|
||||
static const char *const TAG = "EMC2101.sensor";
|
||||
|
||||
float EMC2101Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void EMC2101Sensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Emc2101 sensor:");
|
||||
LOG_SENSOR(" ", "Internal temperature", this->internal_temperature_sensor_);
|
||||
LOG_SENSOR(" ", "External temperature", this->external_temperature_sensor_);
|
||||
LOG_SENSOR(" ", "Speed", this->speed_sensor_);
|
||||
LOG_SENSOR(" ", "Duty cycle", this->duty_cycle_sensor_);
|
||||
}
|
||||
|
||||
void EMC2101Sensor::update() {
|
||||
if (this->internal_temperature_sensor_ != nullptr) {
|
||||
float internal_temperature = this->parent_->get_internal_temperature();
|
||||
this->internal_temperature_sensor_->publish_state(internal_temperature);
|
||||
}
|
||||
|
||||
if (this->external_temperature_sensor_ != nullptr) {
|
||||
float external_temperature = this->parent_->get_external_temperature();
|
||||
this->external_temperature_sensor_->publish_state(external_temperature);
|
||||
}
|
||||
|
||||
if (this->speed_sensor_ != nullptr) {
|
||||
float speed = this->parent_->get_speed();
|
||||
this->speed_sensor_->publish_state(speed);
|
||||
}
|
||||
|
||||
if (this->duty_cycle_sensor_ != nullptr) {
|
||||
float duty_cycle = this->parent_->get_duty_cycle();
|
||||
this->duty_cycle_sensor_->publish_state(duty_cycle * 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emc2101
|
||||
} // namespace esphome
|
39
esphome/components/emc2101/sensor/emc2101_sensor.h
Normal file
39
esphome/components/emc2101/sensor/emc2101_sensor.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "../emc2101.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace emc2101 {
|
||||
|
||||
/// This class exposes the EMC2101 sensors.
|
||||
class EMC2101Sensor : public PollingComponent {
|
||||
public:
|
||||
EMC2101Sensor(Emc2101Component *parent) : parent_(parent) {}
|
||||
/** Used by ESPHome framework. */
|
||||
void dump_config() override;
|
||||
/** Used by ESPHome framework. */
|
||||
void update() override;
|
||||
/** Used by ESPHome framework. */
|
||||
float get_setup_priority() const override;
|
||||
|
||||
/** Used by ESPHome framework. */
|
||||
void set_internal_temperature_sensor(sensor::Sensor *sensor) { this->internal_temperature_sensor_ = sensor; }
|
||||
/** Used by ESPHome framework. */
|
||||
void set_external_temperature_sensor(sensor::Sensor *sensor) { this->external_temperature_sensor_ = sensor; }
|
||||
/** Used by ESPHome framework. */
|
||||
void set_speed_sensor(sensor::Sensor *sensor) { this->speed_sensor_ = sensor; }
|
||||
/** Used by ESPHome framework. */
|
||||
void set_duty_cycle_sensor(sensor::Sensor *sensor) { this->duty_cycle_sensor_ = sensor; }
|
||||
|
||||
protected:
|
||||
Emc2101Component *parent_;
|
||||
sensor::Sensor *internal_temperature_sensor_{nullptr};
|
||||
sensor::Sensor *external_temperature_sensor_{nullptr};
|
||||
sensor::Sensor *speed_sensor_{nullptr};
|
||||
sensor::Sensor *duty_cycle_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace emc2101
|
||||
} // namespace esphome
|
2
esphome/components/honeywellabp2_i2c/__init__.py
Normal file
2
esphome/components/honeywellabp2_i2c/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
"""Support for Honeywell ABP2"""
|
||||
CODEOWNERS = ["@jpfaff"]
|
108
esphome/components/honeywellabp2_i2c/honeywellabp2.cpp
Normal file
108
esphome/components/honeywellabp2_i2c/honeywellabp2.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
#include "honeywellabp2.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace honeywellabp2_i2c {
|
||||
|
||||
static const uint8_t STATUS_BIT_POWER = 6;
|
||||
static const uint8_t STATUS_BIT_BUSY = 5;
|
||||
static const uint8_t STATUS_BIT_ERROR = 2;
|
||||
static const uint8_t STATUS_MATH_SAT = 0;
|
||||
|
||||
static const char *const TAG = "honeywellabp2";
|
||||
|
||||
void HONEYWELLABP2Sensor::read_sensor_data() {
|
||||
if (this->read(raw_data_, 7) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Communication with ABP2 failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
float press_counts = encode_uint24(raw_data_[1], raw_data_[2], raw_data_[3]); // calculate digital pressure counts
|
||||
float temp_counts = encode_uint24(raw_data_[4], raw_data_[5], raw_data_[6]); // calculate digital temperature counts
|
||||
|
||||
this->last_pressure_ = (((press_counts - this->min_count_) / (this->max_count_ - this->min_count_)) *
|
||||
(this->max_pressure_ - this->min_pressure_)) +
|
||||
this->min_pressure_;
|
||||
this->last_temperature_ = (temp_counts * 200 / 16777215) - 50;
|
||||
}
|
||||
|
||||
void HONEYWELLABP2Sensor::start_measurement() {
|
||||
if (this->write(i2c_cmd_, 3) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Communication with ABP2 failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->measurement_running_ = true;
|
||||
}
|
||||
|
||||
bool HONEYWELLABP2Sensor::is_measurement_ready() {
|
||||
if (this->read(raw_data_, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Communication with ABP2 failed!");
|
||||
this->mark_failed();
|
||||
return false;
|
||||
}
|
||||
if ((raw_data_[0] & (0x1 << STATUS_BIT_BUSY)) > 0) {
|
||||
return false;
|
||||
}
|
||||
this->measurement_running_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HONEYWELLABP2Sensor::measurement_timeout() {
|
||||
ESP_LOGE(TAG, "Timeout!");
|
||||
this->measurement_running_ = false;
|
||||
this->mark_failed();
|
||||
}
|
||||
|
||||
float HONEYWELLABP2Sensor::get_pressure() { return this->last_pressure_; }
|
||||
|
||||
float HONEYWELLABP2Sensor::get_temperature() { return this->last_temperature_; }
|
||||
|
||||
void HONEYWELLABP2Sensor::loop() {
|
||||
if (this->measurement_running_) {
|
||||
if (this->is_measurement_ready()) {
|
||||
this->cancel_timeout("meas_timeout");
|
||||
|
||||
this->read_sensor_data();
|
||||
if (pressure_sensor_ != nullptr) {
|
||||
this->pressure_sensor_->publish_state(this->get_pressure());
|
||||
}
|
||||
if (temperature_sensor_ != nullptr) {
|
||||
this->temperature_sensor_->publish_state(this->get_temperature());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HONEYWELLABP2Sensor::update() {
|
||||
ESP_LOGV(TAG, "Update Honeywell ABP2 Sensor");
|
||||
|
||||
this->start_measurement();
|
||||
this->set_timeout("meas_timeout", 50, [this] { this->measurement_timeout(); });
|
||||
}
|
||||
|
||||
void HONEYWELLABP2Sensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, " Min Pressure Range: %0.1f", this->min_pressure_);
|
||||
ESP_LOGCONFIG(TAG, " Max Pressure Range: %0.1f", this->max_pressure_);
|
||||
if (this->transfer_function_ == ABP2_TRANS_FUNC_A) {
|
||||
ESP_LOGCONFIG(TAG, " Transfer function A");
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " Transfer function B");
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void HONEYWELLABP2Sensor::set_transfer_function(ABP2TRANFERFUNCTION transfer_function) {
|
||||
this->transfer_function_ = transfer_function;
|
||||
if (this->transfer_function_ == ABP2_TRANS_FUNC_B) {
|
||||
this->max_count_ = this->max_count_b_;
|
||||
this->min_count_ = this->min_count_b_;
|
||||
} else {
|
||||
this->max_count_ = this->max_count_a_;
|
||||
this->min_count_ = this->min_count_a_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace honeywellabp2_i2c
|
||||
} // namespace esphome
|
60
esphome/components/honeywellabp2_i2c/honeywellabp2.h
Normal file
60
esphome/components/honeywellabp2_i2c/honeywellabp2.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
// for Honeywell ABP sensor
|
||||
// adapting code from https://github.com/vwls/Honeywell_pressure_sensors
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace honeywellabp2_i2c {
|
||||
|
||||
enum ABP2TRANFERFUNCTION { ABP2_TRANS_FUNC_A = 0, ABP2_TRANS_FUNC_B = 1 };
|
||||
|
||||
class HONEYWELLABP2Sensor : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { this->pressure_sensor_ = pressure_sensor; };
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; };
|
||||
void loop() override;
|
||||
void update() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; };
|
||||
void dump_config() override;
|
||||
|
||||
void read_sensor_data();
|
||||
void start_measurement();
|
||||
bool is_measurement_ready();
|
||||
void measurement_timeout();
|
||||
|
||||
float get_pressure();
|
||||
float get_temperature();
|
||||
|
||||
void set_min_pressure(float min_pressure) { this->min_pressure_ = min_pressure; };
|
||||
void set_max_pressure(float max_pressure) { this->max_pressure_ = max_pressure; };
|
||||
void set_transfer_function(ABP2TRANFERFUNCTION transfer_function);
|
||||
|
||||
protected:
|
||||
float min_pressure_ = 0.0;
|
||||
float max_pressure_ = 0.0;
|
||||
ABP2TRANFERFUNCTION transfer_function_ = ABP2_TRANS_FUNC_A;
|
||||
|
||||
sensor::Sensor *pressure_sensor_{nullptr};
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
|
||||
const float max_count_a_ = 15099494.4; // (90% of 2^24 counts or 0xE66666)
|
||||
const float min_count_a_ = 1677721.6; // (10% of 2^24 counts or 0x19999A)
|
||||
const float max_count_b_ = 11744051.2; // (70% of 2^24 counts or 0xB33333)
|
||||
const float min_count_b_ = 5033164.8; // (30% of 2^24 counts or 0x4CCCCC)
|
||||
|
||||
float max_count_;
|
||||
float min_count_;
|
||||
bool measurement_running_ = false;
|
||||
|
||||
uint8_t raw_data_[7]; // holds output data
|
||||
uint8_t i2c_cmd_[3] = {0xAA, 0x00, 0x00}; // command to be sent
|
||||
float last_pressure_;
|
||||
float last_temperature_;
|
||||
};
|
||||
|
||||
} // namespace honeywellabp2_i2c
|
||||
} // namespace esphome
|
75
esphome/components/honeywellabp2_i2c/sensor.py
Normal file
75
esphome/components/honeywellabp2_i2c/sensor.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.components import i2c
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_PRESSURE,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
honeywellabp2_ns = cg.esphome_ns.namespace("honeywellabp2_i2c")
|
||||
|
||||
CONF_MIN_PRESSURE = "min_pressure"
|
||||
CONF_MAX_PRESSURE = "max_pressure"
|
||||
TRANSFER_FUNCTION = "transfer_function"
|
||||
ABP2TRANFERFUNCTION = honeywellabp2_ns.enum("ABP2TRANFERFUNCTION")
|
||||
TRANS_FUNC_OPTIONS = {
|
||||
"A": ABP2TRANFERFUNCTION.ABP2_TRANS_FUNC_A,
|
||||
"B": ABP2TRANFERFUNCTION.ABP2_TRANS_FUNC_B,
|
||||
}
|
||||
|
||||
HONEYWELLABP2Sensor = honeywellabp2_ns.class_(
|
||||
"HONEYWELLABP2Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HONEYWELLABP2Sensor),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
unit_of_measurement="Pa",
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_MIN_PRESSURE): cv.float_,
|
||||
cv.Required(CONF_MAX_PRESSURE): cv.float_,
|
||||
cv.Required(TRANSFER_FUNCTION): cv.enum(TRANS_FUNC_OPTIONS),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x28))
|
||||
)
|
||||
|
||||
|
||||
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 pressure_config := config.get(CONF_PRESSURE):
|
||||
sens = await sensor.new_sensor(pressure_config)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
cg.add(var.set_min_pressure(pressure_config[CONF_MIN_PRESSURE]))
|
||||
cg.add(var.set_max_pressure(pressure_config[CONF_MAX_PRESSURE]))
|
||||
cg.add(var.set_transfer_function(pressure_config[TRANSFER_FUNCTION]))
|
||||
|
||||
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||
sens = await sensor.new_sensor(temperature_config)
|
||||
cg.add(var.set_temperature_sensor(sens))
|
|
@ -139,17 +139,22 @@ def _process_base_package(config: dict) -> dict:
|
|||
) from e
|
||||
return packages
|
||||
|
||||
packages = {}
|
||||
packages = None
|
||||
error = ""
|
||||
|
||||
try:
|
||||
packages = get_packages(files)
|
||||
except cv.Invalid:
|
||||
if revert is not None:
|
||||
revert()
|
||||
packages = get_packages(files)
|
||||
finally:
|
||||
if packages is None:
|
||||
raise cv.Invalid("Failed to load packages")
|
||||
except cv.Invalid as e:
|
||||
error = e
|
||||
try:
|
||||
if revert is not None:
|
||||
revert()
|
||||
packages = get_packages(files)
|
||||
except cv.Invalid as er:
|
||||
error = er
|
||||
|
||||
if packages is None:
|
||||
raise cv.Invalid(f"Failed to load packages. {error}")
|
||||
|
||||
return {"packages": packages}
|
||||
|
||||
|
|
0
esphome/components/qwiic_pir/__init__.py
Normal file
0
esphome/components/qwiic_pir/__init__.py
Normal file
67
esphome/components/qwiic_pir/binary_sensor.py
Normal file
67
esphome/components/qwiic_pir/binary_sensor.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
from esphome import core
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, binary_sensor
|
||||
from esphome.const import (
|
||||
CONF_DEBOUNCE,
|
||||
DEVICE_CLASS_MOTION,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
CODEOWNERS = ["@kahrendt"]
|
||||
|
||||
qwiic_pir_ns = cg.esphome_ns.namespace("qwiic_pir")
|
||||
|
||||
DebounceMode = qwiic_pir_ns.enum("DebounceMode")
|
||||
DEBOUNCE_MODE_OPTIONS = {
|
||||
"RAW": DebounceMode.RAW_DEBOUNCE_MODE,
|
||||
"NATIVE": DebounceMode.NATIVE_DEBOUNCE_MODE,
|
||||
"HYBRID": DebounceMode.HYBRID_DEBOUNCE_MODE,
|
||||
}
|
||||
|
||||
CONF_DEBOUNCE_MODE = "debounce_mode"
|
||||
|
||||
QwiicPIRComponent = qwiic_pir_ns.class_(
|
||||
"QwiicPIRComponent", cg.Component, i2c.I2CDevice, binary_sensor.BinarySensor
|
||||
)
|
||||
|
||||
|
||||
def validate_no_debounce_unless_native(config):
|
||||
if CONF_DEBOUNCE in config:
|
||||
if config[CONF_DEBOUNCE_MODE] != "NATIVE":
|
||||
raise cv.Invalid("debounce can only be set if debounce_mode is NATIVE")
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
binary_sensor.binary_sensor_schema(
|
||||
QwiicPIRComponent,
|
||||
device_class=DEVICE_CLASS_MOTION,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_DEBOUNCE): cv.All(
|
||||
cv.time_period,
|
||||
cv.Range(max=core.TimePeriod(milliseconds=65535)),
|
||||
),
|
||||
cv.Optional(CONF_DEBOUNCE_MODE, default="HYBRID"): cv.enum(
|
||||
DEBOUNCE_MODE_OPTIONS, upper=True
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(i2c.i2c_device_schema(0x12)),
|
||||
validate_no_debounce_unless_native,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if debounce_time_setting := config.get(CONF_DEBOUNCE):
|
||||
cg.add(var.set_debounce_time(debounce_time_setting.total_milliseconds))
|
||||
else:
|
||||
cg.add(var.set_debounce_time(1)) # default to 1 ms if not configured
|
||||
cg.add(var.set_debounce_mode(config[CONF_DEBOUNCE_MODE]))
|
137
esphome/components/qwiic_pir/qwiic_pir.cpp
Normal file
137
esphome/components/qwiic_pir/qwiic_pir.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
#include "qwiic_pir.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace qwiic_pir {
|
||||
|
||||
static const char *const TAG = "qwiic_pir";
|
||||
|
||||
void QwiicPIRComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Qwiic PIR...");
|
||||
|
||||
// Verify I2C communcation by reading and verifying the chip ID
|
||||
uint8_t chip_id;
|
||||
|
||||
if (!this->read_byte(QWIIC_PIR_CHIP_ID, &chip_id)) {
|
||||
ESP_LOGE(TAG, "Failed to read the chip's ID");
|
||||
|
||||
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (chip_id != QWIIC_PIR_DEVICE_ID) {
|
||||
ESP_LOGE(TAG, "Unknown chip ID, is this a Qwiic PIR?");
|
||||
|
||||
this->error_code_ = ERROR_WRONG_CHIP_ID;
|
||||
this->mark_failed();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->write_byte_16(QWIIC_PIR_DEBOUNCE_TIME, this->debounce_time_)) {
|
||||
ESP_LOGE(TAG, "Failed to configure debounce time.");
|
||||
|
||||
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) {
|
||||
// Publish the starting raw state of the PIR sensor
|
||||
// If NATIVE mode, the binary_sensor state would be unknown until a motion event
|
||||
if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) {
|
||||
ESP_LOGE(TAG, "Failed to read initial sensor state.");
|
||||
|
||||
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this->publish_state(this->event_register_.raw_reading);
|
||||
}
|
||||
}
|
||||
|
||||
void QwiicPIRComponent::loop() {
|
||||
// Read Event Register
|
||||
if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) {
|
||||
ESP_LOGW(TAG, "Failed to communicate with sensor");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->debounce_mode_ == HYBRID_DEBOUNCE_MODE) {
|
||||
// Use a combination of the raw sensor reading and the device's event detection to determine state
|
||||
// - The device is hardcoded to use a debounce time of 1 ms in this mode
|
||||
// - Any event, even if it is object_removed, implies motion was active since the last loop, so publish true
|
||||
// - Use ESPHome's built-in filters for debouncing
|
||||
this->publish_state(this->event_register_.raw_reading || this->event_register_.event_available);
|
||||
|
||||
if (this->event_register_.event_available) {
|
||||
this->clear_events_();
|
||||
}
|
||||
} else if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) {
|
||||
// Uses the device's firmware to debounce the signal
|
||||
// - Follows the logic of SparkFun's example implementation:
|
||||
// https://github.com/sparkfun/SparkFun_Qwiic_PIR_Arduino_Library/blob/master/examples/Example2_PrintPIRStatus/Example2_PrintPIRStatus.ino
|
||||
// (accessed July 2023)
|
||||
// - Is unreliable at detecting an object being removed, especially at debounce rates even slightly large
|
||||
if (this->event_register_.event_available) {
|
||||
// If an object is detected, publish true
|
||||
if (this->event_register_.object_detected)
|
||||
this->publish_state(true);
|
||||
|
||||
// If an object has been removed, publish false
|
||||
if (this->event_register_.object_removed)
|
||||
this->publish_state(false);
|
||||
|
||||
this->clear_events_();
|
||||
}
|
||||
} else if (this->debounce_mode_ == RAW_DEBOUNCE_MODE) {
|
||||
// Publishes the raw PIR sensor reading with no further logic
|
||||
// - May miss a very short motion detection if the ESP's loop time is slow
|
||||
this->publish_state(this->event_register_.raw_reading);
|
||||
}
|
||||
}
|
||||
|
||||
void QwiicPIRComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Qwiic PIR:");
|
||||
|
||||
if (this->debounce_mode_ == RAW_DEBOUNCE_MODE) {
|
||||
ESP_LOGCONFIG(TAG, " Debounce Mode: RAW");
|
||||
} else if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) {
|
||||
ESP_LOGCONFIG(TAG, " Debounce Mode: NATIVE");
|
||||
ESP_LOGCONFIG(TAG, " Debounce Time: %ums", this->debounce_time_);
|
||||
} else if (this->debounce_mode_ == HYBRID_DEBOUNCE_MODE) {
|
||||
ESP_LOGCONFIG(TAG, " Debounce Mode: HYBRID");
|
||||
}
|
||||
|
||||
switch (this->error_code_) {
|
||||
case NONE:
|
||||
break;
|
||||
case ERROR_COMMUNICATION_FAILED:
|
||||
ESP_LOGE(TAG, " Communication with Qwiic PIR failed!");
|
||||
break;
|
||||
case ERROR_WRONG_CHIP_ID:
|
||||
ESP_LOGE(TAG, " Qwiic PIR has wrong chip ID - please verify you are using a Qwiic PIR");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, " Qwiic PIR error code %d", (int) this->error_code_);
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_I2C_DEVICE(this);
|
||||
LOG_BINARY_SENSOR(" ", "Qwiic PIR Binary Sensor", this);
|
||||
}
|
||||
|
||||
void QwiicPIRComponent::clear_events_() {
|
||||
// Clear event status register
|
||||
if (!this->write_byte(QWIIC_PIR_EVENT_STATUS, 0x00))
|
||||
ESP_LOGW(TAG, "Failed to clear events on sensor");
|
||||
}
|
||||
|
||||
} // namespace qwiic_pir
|
||||
} // namespace esphome
|
70
esphome/components/qwiic_pir/qwiic_pir.h
Normal file
70
esphome/components/qwiic_pir/qwiic_pir.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Adds support for Qwiic PIR motion sensors that communicate over an I2C bus.
|
||||
* These sensors use Sharp PIR motion sensors to detect motion. A firmware running on an ATTiny84 translates the digital
|
||||
* output to I2C communications.
|
||||
* ATTiny84 firmware: https://github.com/sparkfun/Qwiic_PIR (acccessed July 2023)
|
||||
* SparkFun's Arduino library: https://github.com/sparkfun/SparkFun_Qwiic_PIR_Arduino_Library (accessed July 2023)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace qwiic_pir {
|
||||
|
||||
// Qwiic PIR I2C Register Addresses
|
||||
enum {
|
||||
QWIIC_PIR_CHIP_ID = 0x00,
|
||||
QWIIC_PIR_EVENT_STATUS = 0x03,
|
||||
QWIIC_PIR_DEBOUNCE_TIME = 0x05, // uint16_t debounce time in milliseconds
|
||||
};
|
||||
|
||||
enum DebounceMode {
|
||||
RAW_DEBOUNCE_MODE,
|
||||
NATIVE_DEBOUNCE_MODE,
|
||||
HYBRID_DEBOUNCE_MODE,
|
||||
};
|
||||
|
||||
static const uint8_t QWIIC_PIR_DEVICE_ID = 0x72;
|
||||
|
||||
class QwiicPIRComponent : public Component, public i2c::I2CDevice, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
void set_debounce_time(uint16_t debounce_time) { this->debounce_time_ = debounce_time; }
|
||||
void set_debounce_mode(DebounceMode mode) { this->debounce_mode_ = mode; }
|
||||
|
||||
protected:
|
||||
uint16_t debounce_time_{};
|
||||
|
||||
DebounceMode debounce_mode_{};
|
||||
|
||||
enum ErrorCode {
|
||||
NONE = 0,
|
||||
ERROR_COMMUNICATION_FAILED,
|
||||
ERROR_WRONG_CHIP_ID,
|
||||
} error_code_{NONE};
|
||||
|
||||
union {
|
||||
struct {
|
||||
bool raw_reading : 1; // raw state of PIR sensor
|
||||
bool event_available : 1; // a debounced object has been detected or removed
|
||||
bool object_removed : 1; // a debounced object is no longer detected
|
||||
bool object_detected : 1; // a debounced object has been detected
|
||||
bool : 4;
|
||||
};
|
||||
uint8_t reg;
|
||||
} event_register_ = {.reg = 0};
|
||||
|
||||
void clear_events_();
|
||||
};
|
||||
|
||||
} // namespace qwiic_pir
|
||||
} // namespace esphome
|
|
@ -1,5 +1,6 @@
|
|||
#include "sen5x.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
|
@ -135,9 +136,12 @@ void SEN5XComponent::setup() {
|
|||
ESP_LOGD(TAG, "Firmware version %d", this->firmware_version_);
|
||||
|
||||
if (this->voc_sensor_ && this->store_baseline_) {
|
||||
// Hash with compilation time
|
||||
uint32_t combined_serial =
|
||||
encode_uint24(this->serial_number_[0], this->serial_number_[1], this->serial_number_[2]);
|
||||
// Hash with compilation time and serial number
|
||||
// This ensures the baseline storage is cleared after OTA
|
||||
uint32_t hash = fnv1_hash(App.get_compilation_time());
|
||||
// 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(combined_serial));
|
||||
this->pref_ = global_preferences->make_preference<Sen5xBaselines>(hash, true);
|
||||
|
||||
if (this->pref_.load(&this->voc_baselines_storage_)) {
|
||||
|
|
|
@ -16,6 +16,7 @@ from esphome.const import (
|
|||
CONF_FROM,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_IGNORE_OUT_OF_RANGE,
|
||||
CONF_ON_RAW_VALUE,
|
||||
CONF_ON_VALUE,
|
||||
CONF_ON_VALUE_RANGE,
|
||||
|
@ -688,6 +689,7 @@ CLAMP_SCHEMA = cv.All(
|
|||
{
|
||||
cv.Optional(CONF_MIN_VALUE, default="NaN"): cv.float_,
|
||||
cv.Optional(CONF_MAX_VALUE, default="NaN"): cv.float_,
|
||||
cv.Optional(CONF_IGNORE_OUT_OF_RANGE, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
validate_clamp,
|
||||
|
@ -700,6 +702,7 @@ async def clamp_filter_to_code(config, filter_id):
|
|||
filter_id,
|
||||
config[CONF_MIN_VALUE],
|
||||
config[CONF_MAX_VALUE],
|
||||
config[CONF_IGNORE_OUT_OF_RANGE],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -434,13 +434,25 @@ optional<float> CalibratePolynomialFilter::new_value(float value) {
|
|||
return res;
|
||||
}
|
||||
|
||||
ClampFilter::ClampFilter(float min, float max) : min_(min), max_(max) {}
|
||||
ClampFilter::ClampFilter(float min, float max, bool ignore_out_of_range)
|
||||
: min_(min), max_(max), ignore_out_of_range_(ignore_out_of_range) {}
|
||||
optional<float> ClampFilter::new_value(float value) {
|
||||
if (std::isfinite(value)) {
|
||||
if (std::isfinite(this->min_) && value < this->min_)
|
||||
return this->min_;
|
||||
if (std::isfinite(this->max_) && value > this->max_)
|
||||
return this->max_;
|
||||
if (std::isfinite(this->min_) && value < this->min_) {
|
||||
if (this->ignore_out_of_range_) {
|
||||
return {};
|
||||
} else {
|
||||
return this->min_;
|
||||
}
|
||||
}
|
||||
|
||||
if (std::isfinite(this->max_) && value > this->max_) {
|
||||
if (this->ignore_out_of_range_) {
|
||||
return {};
|
||||
} else {
|
||||
return this->max_;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -411,12 +411,13 @@ class CalibratePolynomialFilter : public Filter {
|
|||
|
||||
class ClampFilter : public Filter {
|
||||
public:
|
||||
ClampFilter(float min, float max);
|
||||
ClampFilter(float min, float max, bool ignore_out_of_range);
|
||||
optional<float> new_value(float value) override;
|
||||
|
||||
protected:
|
||||
float min_{NAN};
|
||||
float max_{NAN};
|
||||
bool ignore_out_of_range_;
|
||||
};
|
||||
|
||||
class RoundFilter : public Filter {
|
||||
|
|
|
@ -73,9 +73,10 @@ void SGP30Component::setup() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Hash with compilation time
|
||||
// Hash with compilation time and serial number
|
||||
// This ensures the baseline storage is cleared after OTA
|
||||
uint32_t hash = fnv1_hash(App.get_compilation_time());
|
||||
// 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<SGP30Baselines>(hash, true);
|
||||
|
||||
if (this->pref_.load(&this->baselines_storage_)) {
|
||||
|
|
|
@ -61,9 +61,10 @@ void SGP4xComponent::setup() {
|
|||
ESP_LOGD(TAG, "Product version: 0x%0X", uint16_t(this->featureset_ & 0x1FF));
|
||||
|
||||
if (this->store_baseline_) {
|
||||
// Hash with compilation time
|
||||
// Hash with compilation time and serial number
|
||||
// This ensures the baseline storage is cleared after OTA
|
||||
uint32_t hash = fnv1_hash(App.get_compilation_time());
|
||||
// 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<SGP4xBaselines>(hash, true);
|
||||
|
||||
if (this->pref_.load(&this->voc_baselines_storage_)) {
|
||||
|
|
|
@ -335,6 +335,7 @@ CONF_IDLE_LEVEL = "idle_level"
|
|||
CONF_IDLE_TIME = "idle_time"
|
||||
CONF_IF = "if"
|
||||
CONF_IGNORE_EFUSE_MAC_CRC = "ignore_efuse_mac_crc"
|
||||
CONF_IGNORE_OUT_OF_RANGE = "ignore_out_of_range"
|
||||
CONF_IGNORE_STRAPPING_WARNING = "ignore_strapping_warning"
|
||||
CONF_IIR_FILTER = "iir_filter"
|
||||
CONF_ILLUMINANCE = "illuminance"
|
||||
|
|
|
@ -750,6 +750,16 @@ sensor:
|
|||
temperature:
|
||||
name: Honeywell temperature
|
||||
cs_pin: GPIO5
|
||||
- platform: honeywellabp2_i2c
|
||||
pressure:
|
||||
name: Honeywell2 pressure
|
||||
min_pressure: 0
|
||||
max_pressure: 16000
|
||||
transfer_function: A
|
||||
temperature:
|
||||
name: Honeywell temperature
|
||||
i2c_id: i2c_bus
|
||||
address: 0x28
|
||||
- platform: hte501
|
||||
temperature:
|
||||
name: Office Temperature 2
|
||||
|
@ -1821,6 +1831,9 @@ binary_sensor:
|
|||
- platform: optolink
|
||||
name: Disturbance
|
||||
address: 0x0A82
|
||||
- platform: qwiic_pir
|
||||
i2c_id: i2c_bus
|
||||
name: "Qwiic PIR Motion Sensor"
|
||||
|
||||
pca9685:
|
||||
frequency: 500
|
||||
|
|
Loading…
Reference in a new issue