1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-15 15:18:16 +00:00

Merge branch 'dev' into defaults2

This commit is contained in:
tomaszduda23 2025-01-25 14:08:38 +01:00 committed by GitHub
commit adb3c8bb86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
78 changed files with 735 additions and 645 deletions

View File

@ -17,7 +17,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v9.0.0 - uses: actions/stale@v9.1.0
with: with:
days-before-pr-stale: 90 days-before-pr-stale: 90
days-before-pr-close: 7 days-before-pr-close: 7
@ -37,7 +37,7 @@ jobs:
close-issues: close-issues:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v9.0.0 - uses: actions/stale@v9.1.0
with: with:
days-before-pr-stale: -1 days-before-pr-stale: -1
days-before-pr-close: -1 days-before-pr-close: -1

View File

@ -133,6 +133,7 @@ esphome/components/ens160_i2c/* @latonita
esphome/components/ens160_spi/* @latonita esphome/components/ens160_spi/* @latonita
esphome/components/ens210/* @itn3rd77 esphome/components/ens210/* @itn3rd77
esphome/components/es7210/* @kahrendt esphome/components/es7210/* @kahrendt
esphome/components/es7243e/* @kbx81
esphome/components/es8156/* @kbx81 esphome/components/es8156/* @kbx81
esphome/components/es8311/* @kahrendt @kroimon esphome/components/es8311/* @kahrendt @kroimon
esphome/components/esp32/* @esphome/core esphome/components/esp32/* @esphome/core

View File

@ -9,8 +9,6 @@ static const char *const TAG = "ads1115";
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00; static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01; static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
static const uint8_t ADS1115_DATA_RATE_860_SPS = 0b111; // 3300_SPS for ADS1015
void ADS1115Component::setup() { void ADS1115Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADS1115..."); ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
uint16_t value; uint16_t value;
@ -43,9 +41,9 @@ void ADS1115Component::setup() {
config |= 0b0000000100000000; config |= 0b0000000100000000;
} }
// Set data rate - 860 samples per second (we're in singleshot mode) // Set data rate - 860 samples per second
// 0bxxxxxxxx100xxxxx // 0bxxxxxxxx100xxxxx
config |= ADS1115_DATA_RATE_860_SPS << 5; config |= ADS1115_860SPS << 5;
// Set comparator mode - hysteresis // Set comparator mode - hysteresis
// 0bxxxxxxxxxxx0xxxx // 0bxxxxxxxxxxx0xxxx
@ -77,7 +75,7 @@ void ADS1115Component::dump_config() {
} }
} }
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
ADS1115Resolution resolution) { ADS1115Resolution resolution, ADS1115Samplerate samplerate) {
uint16_t config = this->prev_config_; uint16_t config = this->prev_config_;
// Multiplexer // Multiplexer
// 0bxBBBxxxxxxxxxxxx // 0bxBBBxxxxxxxxxxxx
@ -89,6 +87,11 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
config &= 0b1111000111111111; config &= 0b1111000111111111;
config |= (gain & 0b111) << 9; config |= (gain & 0b111) << 9;
// Sample rate
// 0bxxxxxxxxBBBxxxxx
config &= 0b1111111100011111;
config |= (samplerate & 0b111) << 5;
if (!this->continuous_mode_) { if (!this->continuous_mode_) {
// Start conversion // Start conversion
config |= 0b1000000000000000; config |= 0b1000000000000000;
@ -101,8 +104,54 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
} }
this->prev_config_ = config; this->prev_config_ = config;
// about 1.2 ms with 860 samples per second // Delay calculated as: ceil((1000/SPS)+.5)
delay(2); if (resolution == ADS1015_12_BITS) {
switch (samplerate) {
case ADS1115_8SPS:
delay(9);
break;
case ADS1115_16SPS:
delay(5);
break;
case ADS1115_32SPS:
delay(3);
break;
case ADS1115_64SPS:
case ADS1115_128SPS:
delay(2);
break;
default:
delay(1);
break;
}
} else {
switch (samplerate) {
case ADS1115_8SPS:
delay(126); // NOLINT
break;
case ADS1115_16SPS:
delay(63); // NOLINT
break;
case ADS1115_32SPS:
delay(32);
break;
case ADS1115_64SPS:
delay(17);
break;
case ADS1115_128SPS:
delay(9);
break;
case ADS1115_250SPS:
delay(5);
break;
case ADS1115_475SPS:
delay(3);
break;
case ADS1115_860SPS:
delay(2);
break;
}
}
// in continuous mode, conversion will always be running, rely on the delay // in continuous mode, conversion will always be running, rely on the delay
// to ensure conversion is taking place with the correct settings // to ensure conversion is taking place with the correct settings

View File

@ -33,6 +33,17 @@ enum ADS1115Resolution {
ADS1015_12_BITS = 12, ADS1015_12_BITS = 12,
}; };
enum ADS1115Samplerate {
ADS1115_8SPS = 0b000,
ADS1115_16SPS = 0b001,
ADS1115_32SPS = 0b010,
ADS1115_64SPS = 0b011,
ADS1115_128SPS = 0b100,
ADS1115_250SPS = 0b101,
ADS1115_475SPS = 0b110,
ADS1115_860SPS = 0b111
};
class ADS1115Component : public Component, public i2c::I2CDevice { class ADS1115Component : public Component, public i2c::I2CDevice {
public: public:
void setup() override; void setup() override;
@ -42,7 +53,8 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; } void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
/// Helper method to request a measurement from a sensor. /// Helper method to request a measurement from a sensor.
float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution); float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution,
ADS1115Samplerate samplerate);
protected: protected:
uint16_t prev_config_{0}; uint16_t prev_config_{0};

View File

@ -5,6 +5,7 @@ from esphome.const import (
CONF_GAIN, CONF_GAIN,
CONF_MULTIPLEXER, CONF_MULTIPLEXER,
CONF_RESOLUTION, CONF_RESOLUTION,
CONF_SAMPLE_RATE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
@ -43,6 +44,17 @@ RESOLUTION = {
"12_BITS": ADS1115Resolution.ADS1015_12_BITS, "12_BITS": ADS1115Resolution.ADS1015_12_BITS,
} }
ADS1115Samplerate = ads1115_ns.enum("ADS1115Samplerate")
SAMPLERATE = {
"8": ADS1115Samplerate.ADS1115_8SPS,
"16": ADS1115Samplerate.ADS1115_16SPS,
"32": ADS1115Samplerate.ADS1115_32SPS,
"64": ADS1115Samplerate.ADS1115_64SPS,
"128": ADS1115Samplerate.ADS1115_128SPS,
"250": ADS1115Samplerate.ADS1115_250SPS,
"475": ADS1115Samplerate.ADS1115_475SPS,
"860": ADS1115Samplerate.ADS1115_860SPS,
}
ADS1115Sensor = ads1115_ns.class_( ADS1115Sensor = ads1115_ns.class_(
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler "ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
@ -64,6 +76,9 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum( cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
RESOLUTION, upper=True, space="_" RESOLUTION, upper=True, space="_"
), ),
cv.Optional(CONF_SAMPLE_RATE, default="860"): cv.enum(
SAMPLERATE, string=True
),
} }
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s"))
@ -79,3 +94,4 @@ async def to_code(config):
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
cg.add(var.set_gain(config[CONF_GAIN])) cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_resolution(config[CONF_RESOLUTION])) cg.add(var.set_resolution(config[CONF_RESOLUTION]))
cg.add(var.set_samplerate(config[CONF_SAMPLE_RATE]))

View File

@ -8,7 +8,7 @@ namespace ads1115 {
static const char *const TAG = "ads1115.sensor"; static const char *const TAG = "ads1115.sensor";
float ADS1115Sensor::sample() { float ADS1115Sensor::sample() {
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_); return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
} }
void ADS1115Sensor::update() { void ADS1115Sensor::update() {
@ -24,6 +24,7 @@ void ADS1115Sensor::dump_config() {
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_); ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_); ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_); ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
ESP_LOGCONFIG(TAG, " Sample rate: %u", this->samplerate_);
} }
} // namespace ads1115 } // namespace ads1115

View File

@ -21,6 +21,7 @@ class ADS1115Sensor : public sensor::Sensor,
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
void set_gain(ADS1115Gain gain) { this->gain_ = gain; } void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; } void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
void set_samplerate(ADS1115Samplerate samplerate) { this->samplerate_ = samplerate; }
float sample() override; float sample() override;
void dump_config() override; void dump_config() override;
@ -29,6 +30,7 @@ class ADS1115Sensor : public sensor::Sensor,
ADS1115Multiplexer multiplexer_; ADS1115Multiplexer multiplexer_;
ADS1115Gain gain_; ADS1115Gain gain_;
ADS1115Resolution resolution_; ADS1115Resolution resolution_;
ADS1115Samplerate samplerate_;
}; };
} // namespace ads1115 } // namespace ads1115

View File

@ -19,6 +19,7 @@ from .boards import BK72XX_BOARD_PINS, BK72XX_BOARDS
CODEOWNERS = ["@kuba2k2"] CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["libretiny"] AUTO_LOAD = ["libretiny"]
IS_TARGET_PLATFORM = True
COMPONENT_DATA = LibreTinyComponent( COMPONENT_DATA = LibreTinyComponent(
name=COMPONENT_BK72XX, name=COMPONENT_BK72XX,

View File

@ -1,3 +0,0 @@
import esphome.codegen as cg
custom_ns = cg.esphome_ns.namespace("custom")

View File

@ -1,31 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA
from .. import custom_ns
CustomBinarySensorConstructor = custom_ns.class_("CustomBinarySensorConstructor") CONFIG_SCHEMA = cv.invalid(
'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(
binary_sensor.binary_sensor_schema()
),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(binary_sensor.BinarySensorPtr),
)
rhs = CustomBinarySensorConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_BINARY_SENSORS]):
rhs = custom.Pget_binary_sensor(i)
await binary_sensor.register_binary_sensor(rhs, conf)

View File

@ -1,16 +0,0 @@
#include "custom_binary_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace custom {
static const char *const TAG = "custom.binary_sensor";
void CustomBinarySensorConstructor::dump_config() {
for (auto *child : this->binary_sensors_) {
LOG_BINARY_SENSOR("", "Custom Binary Sensor", child);
}
}
} // namespace custom
} // namespace esphome

View File

@ -1,26 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomBinarySensorConstructor : public Component {
public:
CustomBinarySensorConstructor(const std::function<std::vector<binary_sensor::BinarySensor *>()> &init) {
this->binary_sensors_ = init();
}
binary_sensor::BinarySensor *get_binary_sensor(int i) { return this->binary_sensors_[i]; }
void dump_config() override;
protected:
std::vector<binary_sensor::BinarySensor *> binary_sensors_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,30 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import climate
from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns
CustomClimateConstructor = custom_ns.class_("CustomClimateConstructor") CONFIG_SCHEMA = cv.invalid(
CONF_CLIMATES = "climates" 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomClimateConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(climate.Climate.operator("ptr")),
)
rhs = CustomClimateConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_CLIMATES]):
rhs = custom.Pget_climate(i)
await climate.register_climate(rhs, conf)

View File

@ -1,22 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/climate/climate.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomClimateConstructor {
public:
CustomClimateConstructor(const std::function<std::vector<climate::Climate *>()> &init) { this->climates_ = init(); }
climate::Climate *get_climate(int i) { return this->climates_[i]; }
protected:
std::vector<climate::Climate *> climates_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,30 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import cover
from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns
CustomCoverConstructor = custom_ns.class_("CustomCoverConstructor") CONFIG_SCHEMA = cv.invalid(
CONF_COVERS = "covers" 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomCoverConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(cover.Cover.operator("ptr")),
)
rhs = CustomCoverConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_COVERS]):
rhs = custom.Pget_cover(i)
await cover.register_cover(rhs, conf)

View File

@ -1,21 +0,0 @@
#pragma once
#include "esphome/components/cover/cover.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomCoverConstructor {
public:
CustomCoverConstructor(const std::function<std::vector<cover::Cover *>()> &init) { this->covers_ = init(); }
cover::Cover *get_cover(int i) { return this->covers_[i]; }
protected:
std::vector<cover::Cover *> covers_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,30 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import light
from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns
CustomLightOutputConstructor = custom_ns.class_("CustomLightOutputConstructor") CONFIG_SCHEMA = cv.invalid(
CONF_LIGHTS = "lights" 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(light.LightOutput.operator("ptr")),
)
rhs = CustomLightOutputConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_LIGHTS]):
rhs = custom.Pget_light(i)
await light.register_light(rhs, conf)

View File

@ -1,24 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/light/light_output.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomLightOutputConstructor {
public:
CustomLightOutputConstructor(const std::function<std::vector<light::LightOutput *>()> &init) {
this->outputs_ = init();
}
light::LightOutput *get_light(int i) { return this->outputs_[i]; }
protected:
std::vector<light::LightOutput *> outputs_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,61 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import output
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE, CONF_BINARY
from .. import custom_ns
CustomBinaryOutputConstructor = custom_ns.class_("CustomBinaryOutputConstructor") CONFIG_SCHEMA = cv.invalid(
CustomFloatOutputConstructor = custom_ns.class_("CustomFloatOutputConstructor") 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONF_FLOAT = "float"
CONFIG_SCHEMA = cv.typed_schema(
{
CONF_BINARY: cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_OUTPUTS): cv.ensure_list(
output.BINARY_OUTPUT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(output.BinaryOutput),
}
)
),
}
),
CONF_FLOAT: cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_OUTPUTS): cv.ensure_list(
output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(output.FloatOutput),
}
)
),
}
),
},
lower=True,
) )
async def to_code(config):
type = config[CONF_TYPE]
if type == "binary":
ret_type = output.BinaryOutputPtr
klass = CustomBinaryOutputConstructor
else:
ret_type = output.FloatOutputPtr
klass = CustomFloatOutputConstructor
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(ret_type)
)
rhs = klass(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_OUTPUTS]):
out = cg.Pvariable(conf[CONF_ID], custom.get_output(i))
await output.register_output(out, conf)

View File

@ -1,37 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/output/binary_output.h"
#include "esphome/components/output/float_output.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomBinaryOutputConstructor {
public:
CustomBinaryOutputConstructor(const std::function<std::vector<output::BinaryOutput *>()> &init) {
this->outputs_ = init();
}
output::BinaryOutput *get_output(int i) { return this->outputs_[i]; }
protected:
std::vector<output::BinaryOutput *> outputs_;
};
class CustomFloatOutputConstructor {
public:
CustomFloatOutputConstructor(const std::function<std::vector<output::FloatOutput *>()> &init) {
this->outputs_ = init();
}
output::FloatOutput *get_output(int i) { return this->outputs_[i]; }
protected:
std::vector<output::FloatOutput *> outputs_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,27 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS
from .. import custom_ns
CustomSensorConstructor = custom_ns.class_("CustomSensorConstructor") CONFIG_SCHEMA = cv.invalid(
'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.sensor_schema()),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(sensor.SensorPtr)
)
rhs = CustomSensorConstructor(template_)
var = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_SENSORS]):
sens = cg.Pvariable(conf[CONF_ID], var.get_sensor(i))
await sensor.register_sensor(sens, conf)

View File

@ -1,16 +0,0 @@
#include "custom_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace custom {
static const char *const TAG = "custom.sensor";
void CustomSensorConstructor::dump_config() {
for (auto *child : this->sensors_) {
LOG_SENSOR("", "Custom Sensor", child);
}
}
} // namespace custom
} // namespace esphome

View File

@ -1,24 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomSensorConstructor : public Component {
public:
CustomSensorConstructor(const std::function<std::vector<sensor::Sensor *>()> &init) { this->sensors_ = init(); }
sensor::Sensor *get_sensor(int i) { return this->sensors_[i]; }
void dump_config() override;
protected:
std::vector<sensor::Sensor *> sensors_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,27 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import switch
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES
from .. import custom_ns
CustomSwitchConstructor = custom_ns.class_("CustomSwitchConstructor") CONFIG_SCHEMA = cv.invalid(
'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SWITCHES): cv.ensure_list(switch.switch_schema(switch.Switch)),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(switch.SwitchPtr)
)
rhs = CustomSwitchConstructor(template_)
var = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_SWITCHES]):
switch_ = cg.Pvariable(conf[CONF_ID], var.get_switch(i))
await switch.register_switch(switch_, conf)

View File

@ -1,16 +0,0 @@
#include "custom_switch.h"
#include "esphome/core/log.h"
namespace esphome {
namespace custom {
static const char *const TAG = "custom.switch";
void CustomSwitchConstructor::dump_config() {
for (auto *child : this->switches_) {
LOG_SWITCH("", "Custom Switch", child);
}
}
} // namespace custom
} // namespace esphome

View File

@ -1,24 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/switch/switch.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomSwitchConstructor : public Component {
public:
CustomSwitchConstructor(const std::function<std::vector<switch_::Switch *>()> &init) { this->switches_ = init(); }
switch_::Switch *get_switch(int i) { return this->switches_[i]; }
void dump_config() override;
protected:
std::vector<switch_::Switch *> switches_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,32 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS
from .. import custom_ns
CustomTextSensorConstructor = custom_ns.class_("CustomTextSensorConstructor") CONFIG_SCHEMA = cv.invalid(
'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_TEXT_SENSORS): cv.ensure_list(
text_sensor.text_sensor_schema()
),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(text_sensor.TextSensorPtr),
)
rhs = CustomTextSensorConstructor(template_)
var = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_TEXT_SENSORS]):
text = cg.Pvariable(conf[CONF_ID], var.get_text_sensor(i))
await text_sensor.register_text_sensor(text, conf)

View File

@ -1,16 +0,0 @@
#include "custom_text_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace custom {
static const char *const TAG = "custom.text_sensor";
void CustomTextSensorConstructor::dump_config() {
for (auto *child : this->text_sensors_) {
LOG_TEXT_SENSOR("", "Custom Text Sensor", child);
}
}
} // namespace custom
} // namespace esphome

View File

@ -1,26 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/text_sensor/text_sensor.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomTextSensorConstructor : public Component {
public:
CustomTextSensorConstructor(const std::function<std::vector<text_sensor::TextSensor *>()> &init) {
this->text_sensors_ = init();
}
text_sensor::TextSensor *get_text_sensor(int i) { return this->text_sensors_[i]; }
void dump_config() override;
protected:
std::vector<text_sensor::TextSensor *> text_sensors_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,31 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_LAMBDA
custom_component_ns = cg.esphome_ns.namespace("custom_component")
CustomComponentConstructor = custom_component_ns.class_("CustomComponentConstructor")
MULTI_CONF = True MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema(
{ CONFIG_SCHEMA = cv.invalid(
cv.GenerateID(): cv.declare_id(CustomComponentConstructor), 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_COMPONENTS): cv.ensure_list(
cv.Schema({cv.GenerateID(): cv.declare_id(cg.Component)}).extend(
cv.COMPONENT_SCHEMA
)
),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(cg.ComponentPtr)
)
rhs = CustomComponentConstructor(template_)
var = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config.get(CONF_COMPONENTS, [])):
comp = cg.Pvariable(conf[CONF_ID], var.get_component(i))
await cg.register_component(comp, conf)

View File

@ -1,28 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/application.h"
#include <vector>
namespace esphome {
namespace custom_component {
class CustomComponentConstructor {
public:
CustomComponentConstructor(const std::function<std::vector<Component *>()> &init) {
this->components_ = init();
for (auto *comp : this->components_) {
App.register_component(comp);
}
}
Component *get_component(int i) const { return this->components_[i]; }
protected:
std::vector<Component *> components_;
};
} // namespace custom_component
} // namespace esphome

View File

@ -35,8 +35,8 @@ void DebugComponent::log_partition_info_() {
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
while (it != NULL) { while (it != NULL) {
const esp_partition_t *partition = esp_partition_get(it); const esp_partition_t *partition = esp_partition_get(it);
ESP_LOGCONFIG(TAG, " %-12s %-4d %-8d 0x%08X 0x%08X", partition->label, partition->type, partition->subtype, ESP_LOGCONFIG(TAG, " %-12s %-4d %-8d 0x%08" PRIX32 " 0x%08" PRIX32, partition->label, partition->type,
partition->address, partition->size); partition->subtype, partition->address, partition->size);
it = esp_partition_next(it); it = esp_partition_next(it);
} }
esp_partition_iterator_release(it); esp_partition_iterator_release(it);

View File

View File

@ -0,0 +1,34 @@
import esphome.codegen as cg
from esphome.components import i2c
from esphome.components.audio_adc import AudioAdc
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MIC_GAIN
CODEOWNERS = ["@kbx81"]
DEPENDENCIES = ["i2c"]
es7243e_ns = cg.esphome_ns.namespace("es7243e")
ES7243E = es7243e_ns.class_("ES7243E", AudioAdc, cg.Component, i2c.I2CDevice)
ES7243E_MIC_GAINS = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 34.5, 36, 37.5]
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ES7243E),
cv.Optional(CONF_MIC_GAIN, default="24db"): cv.All(
cv.decibel, cv.one_of(*ES7243E_MIC_GAINS)
),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x10))
)
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)
cg.add(var.set_mic_gain(config[CONF_MIC_GAIN]))

View File

@ -0,0 +1,125 @@
#include "es7243e.h"
#include "es7243e_const.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace es7243e {
static const char *const TAG = "es7243e";
// Mark the component as failed; use only in setup
#define ES7243E_ERROR_FAILED(func) \
if (!(func)) { \
this->mark_failed(); \
return; \
}
// Return false; use outside of setup
#define ES7243E_ERROR_CHECK(func) \
if (!(func)) { \
return false; \
}
void ES7243E::dump_config() {
ESP_LOGCONFIG(TAG, "ES7243E audio ADC:");
if (this->is_failed()) {
ESP_LOGE(TAG, " Failed to initialize");
return;
}
}
void ES7243E::setup() {
ESP_LOGCONFIG(TAG, "Setting up ES7243E...");
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x02));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x01));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x1E));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG02, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG03, 0x20));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0D, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG05, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG06, 0x03)); // SCLK=MCLK/4
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG07, 0x00)); // LRCK=MCLK/256
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG08, 0xFF)); // LRCK=MCLK/256
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG09, 0xCA));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_SDP_REG0A, 0x85));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_SDP_REG0B, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0E, 0xBF));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0F, 0x80));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG14, 0x0C));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG15, 0x0C));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG17, 0x02));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG18, 0x26));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG19, 0x77));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1A, 0xF4));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1B, 0x66));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1C, 0x44));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1E, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1F, 0x0C));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG20, 0x1A)); // PGA gain +30dB
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG21, 0x1A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x3F));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG17, 0x01));
ES7243E_ERROR_FAILED(this->configure_mic_gain_());
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x3F));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x00));
this->setup_complete_ = true;
}
bool ES7243E::set_mic_gain(float mic_gain) {
this->mic_gain_ = clamp<float>(mic_gain, 0, 37.5);
if (this->setup_complete_) {
return this->configure_mic_gain_();
}
return true;
}
bool ES7243E::configure_mic_gain_() {
auto regv = this->es7243e_gain_reg_value_(this->mic_gain_);
ES7243E_ERROR_CHECK(this->write_byte(ES7243E_ANALOG_REG20, 0x10 | regv));
ES7243E_ERROR_CHECK(this->write_byte(ES7243E_ANALOG_REG21, 0x10 | regv));
return true;
}
uint8_t ES7243E::es7243e_gain_reg_value_(float mic_gain) {
// reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB
mic_gain += 0.5;
if (mic_gain <= 33.0) {
return (uint8_t) mic_gain / 3;
}
if (mic_gain < 36.0) {
return 12;
}
if (mic_gain < 37.0) {
return 13;
}
return 14;
}
} // namespace es7243e
} // namespace esphome

View File

@ -0,0 +1,37 @@
#pragma once
#include "esphome/components/audio_adc/audio_adc.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
namespace esphome {
namespace es7243e {
class ES7243E : public audio_adc::AudioAdc, public Component, public i2c::I2CDevice {
/* Class for configuring an ES7243E ADC for microphone input.
* Based on code from:
* - https://github.com/espressif/esp-adf/ (accessed 20250116)
*/
public:
void setup() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void dump_config() override;
bool set_mic_gain(float mic_gain) override;
float mic_gain() override { return this->mic_gain_; };
protected:
/// @brief Convert floating point mic gain value to register value
/// @param mic_gain Gain value to convert
/// @return Corresponding register value for specified gain
uint8_t es7243e_gain_reg_value_(float mic_gain);
bool configure_mic_gain_();
bool setup_complete_{false};
float mic_gain_{0};
};
} // namespace es7243e
} // namespace esphome

View File

@ -0,0 +1,54 @@
#pragma once
#include <cinttypes>
namespace esphome {
namespace es7243e {
// ES7243E register addresses
static const uint8_t ES7243E_RESET_REG00 = 0x00; // Reset control
static const uint8_t ES7243E_CLOCK_MGR_REG01 = 0x01; // MCLK/BCLK/ADCCLK/Analog clocks on/off
static const uint8_t ES7243E_CLOCK_MGR_REG02 = 0x02; // MCLK & BCLK configuration, source selection
static const uint8_t ES7243E_CLOCK_MGR_REG03 = 0x03; // ADC Over-sample rate control
static const uint8_t ES7243E_CLOCK_MGR_REG04 = 0x04; // Pre-divide/Pre-multiplication
static const uint8_t ES7243E_CLOCK_MGR_REG05 = 0x05; // CF/DSP clock divider
static const uint8_t ES7243E_CLOCK_MGR_REG06 = 0x06; // BCLK divider at master mode
static const uint8_t ES7243E_CLOCK_MGR_REG07 = 0x07; // BCLK/LRCK/SDOUT tri-state control/LRCK divider bit 11->8
static const uint8_t ES7243E_CLOCK_MGR_REG08 = 0x08; // Master LRCK divider bit 7 to bit 0
static const uint8_t ES7243E_CLOCK_MGR_REG09 = 0x09; // SEL S1/Timer for S1
static const uint8_t ES7243E_SDP_REG0A = 0x0A; // SEL S3/Timer for S3
static const uint8_t ES7243E_SDP_REG0B = 0x0B; // SDP out mute control/I2S/left-justify case/word length/format
static const uint8_t ES7243E_SDP_REG0C = 0x0C; // NFS flag at slot0/LSB/TDM mode selection
static const uint8_t ES7243E_ADC_CTRL_REG0D = 0x0D; // data mux/pol. inv./ram clear on lrck/mclk active/gain scale up
static const uint8_t ES7243E_ADC_CTRL_REG0E = 0x0E; // volume control
static const uint8_t ES7243E_ADC_CTRL_REG0F = 0x0F; // offset freeze/auto level control/automute control/VC ramp rate
static const uint8_t ES7243E_ADC_CTRL_REG10 = 0x10; // automute noise gate/detection
static const uint8_t ES7243E_ADC_CTRL_REG11 = 0x11; // automute SDP control/out gain select
static const uint8_t ES7243E_ADC_CTRL_REG12 = 0x12; // controls for automute PDN_PGA/MOD/reset/digital circuit
static const uint8_t ES7243E_ADC_CTRL_REG13 = 0x13; // ALC rate selection/ALC target level
static const uint8_t ES7243E_ADC_CTRL_REG14 = 0x14; // ADCHPF stage1 coeff
static const uint8_t ES7243E_ADC_CTRL_REG15 = 0x15; // ADCHPF stage2 coeff
static const uint8_t ES7243E_ANALOG_REG16 = 0x16; // power-down/reset
static const uint8_t ES7243E_ANALOG_REG17 = 0x17; // VMIDSEL
static const uint8_t ES7243E_ANALOG_REG18 = 0x18; // ADC/ADCFL bias
static const uint8_t ES7243E_ANALOG_REG19 = 0x19; // PGA1/PGA2 bias
static const uint8_t ES7243E_ANALOG_REG1A = 0x1A; // ADCI1/ADCI23 bias
static const uint8_t ES7243E_ANALOG_REG1B = 0x1B; // ADCSM/ADCCM bias
static const uint8_t ES7243E_ANALOG_REG1C = 0x1C; // ADCVRP/ADCCPP bias
static const uint8_t ES7243E_ANALOG_REG1D = 0x1D; // low power bits
static const uint8_t ES7243E_ANALOG_REG1E = 0x1E; // low power bits
static const uint8_t ES7243E_ANALOG_REG1F = 0x1F; // ADC_DMIC_ON/REFSEL/VX2OFF/VX1SEL/VMIDLVL
static const uint8_t ES7243E_ANALOG_REG20 = 0x20; // select MIC1 as PGA1 input/PGA1 gain
static const uint8_t ES7243E_ANALOG_REG21 = 0x21; // select MIC2 as PGA1 input/PGA2 gain
static const uint8_t ES7243E_TEST_MODE_REGF7 = 0xF7;
static const uint8_t ES7243E_TEST_MODE_REGF8 = 0xF8;
static const uint8_t ES7243E_TEST_MODE_REGF9 = 0xF9;
static const uint8_t ES7243E_I2C_CONF_REGFA = 0xFA; // I2C signals retime/reset registers to default
static const uint8_t ES7243E_FLAG_REGFC = 0xFC; // CSM flag/ADC automute flag (RO)
static const uint8_t ES7243E_CHIP_ID1_REGFD = 0xFD; // chip ID 1, reads 0x7A (RO)
static const uint8_t ES7243E_CHIP_ID2_REGFE = 0xFE; // chip ID 2, reads 0x43 (RO)
static const uint8_t ES7243E_CHIP_VERSION_REGFF = 0xFF; // chip version, reads 0x00 (RO)
} // namespace es7243e
} // namespace esphome

View File

@ -64,6 +64,7 @@ from .gpio import esp32_pin_to_code # noqa
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["preferences"] AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
CONF_RELEASE = "release" CONF_RELEASE = "release"

View File

@ -34,6 +34,7 @@ from .gpio import PinInitialState, add_pin_initial_states_array
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["preferences"] AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
def set_core_data(config): def set_core_data(config):

View File

@ -17,6 +17,7 @@ from .gpio import host_pin_to_code # noqa
CODEOWNERS = ["@esphome/core", "@clydebarrow"] CODEOWNERS = ["@esphome/core", "@clydebarrow"]
AUTO_LOAD = ["network", "preferences"] AUTO_LOAD = ["network", "preferences"]
IS_TARGET_PLATFORM = True
def set_core_data(config): def set_core_data(config):

View File

@ -47,6 +47,7 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@kuba2k2"] CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["preferences"] AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
def _detect_variant(value): def _detect_variant(value):

View File

@ -186,6 +186,8 @@ CONFIG_SCHEMA = cv.All(
esp32_s3_idf=USB_SERIAL_JTAG, esp32_s3_idf=USB_SERIAL_JTAG,
esp32_c3_arduino=USB_CDC, esp32_c3_arduino=USB_CDC,
esp32_c3_idf=USB_SERIAL_JTAG, esp32_c3_idf=USB_SERIAL_JTAG,
esp32_c6_arduino=USB_CDC,
esp32_c6_idf=USB_SERIAL_JTAG,
rp2040=USB_CDC, rp2040=USB_CDC,
bk72xx=DEFAULT, bk72xx=DEFAULT,
rtl87xx=DEFAULT, rtl87xx=DEFAULT,

View File

@ -72,9 +72,9 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
// max_temp // max_temp
root[MQTT_MAX_TEMP] = traits.get_visual_max_temperature(); root[MQTT_MAX_TEMP] = traits.get_visual_max_temperature();
// target_temp_step // target_temp_step
root[MQTT_TARGET_TEMPERATURE_STEP] = traits.get_visual_target_temperature_step(); root[MQTT_TARGET_TEMPERATURE_STEP] = roundf(traits.get_visual_target_temperature_step() * 10) * 0.1;
// current_temp_step // current_temp_step
root[MQTT_CURRENT_TEMPERATURE_STEP] = traits.get_visual_current_temperature_step(); root[MQTT_CURRENT_TEMPERATURE_STEP] = roundf(traits.get_visual_current_temperature_step() * 10) * 0.1;
// temperature units are always coerced to Celsius internally // temperature units are always coerced to Celsius internally
root[MQTT_TEMPERATURE_UNIT] = "C"; root[MQTT_TEMPERATURE_UNIT] = "C";

View File

@ -52,6 +52,14 @@ class Format:
pass pass
class BMPFormat(Format):
def __init__(self):
super().__init__("BMP")
def actions(self):
cg.add_define("USE_ONLINE_IMAGE_BMP_SUPPORT")
class PNGFormat(Format): class PNGFormat(Format):
def __init__(self): def __init__(self):
super().__init__("PNG") super().__init__("PNG")
@ -62,7 +70,13 @@ class PNGFormat(Format):
# New formats can be added here. # New formats can be added here.
IMAGE_FORMATS = {x.image_type: x for x in (PNGFormat(),)} IMAGE_FORMATS = {
x.image_type: x
for x in (
BMPFormat(),
PNGFormat(),
)
}
OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_) OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_)

View File

@ -0,0 +1,101 @@
#include "bmp_image.h"
#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT
#include "esphome/components/display/display.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace online_image {
static const char *const TAG = "online_image.bmp";
int HOT BmpDecoder::decode(uint8_t *buffer, size_t size) {
size_t index = 0;
if (this->current_index_ == 0 && index == 0 && size > 14) {
/**
* BMP file format:
* 0-1: Signature (BM)
* 2-5: File size
* 6-9: Reserved
* 10-13: Pixel data offset
*
* Integer values are stored in little-endian format.
*/
// Check if the file is a BMP image
if (buffer[0] != 'B' || buffer[1] != 'M') {
ESP_LOGE(TAG, "Not a BMP file");
return DECODE_ERROR_INVALID_TYPE;
}
this->download_size_ = encode_uint32(buffer[5], buffer[4], buffer[3], buffer[2]);
this->data_offset_ = encode_uint32(buffer[13], buffer[12], buffer[11], buffer[10]);
this->current_index_ = 14;
index = 14;
}
if (this->current_index_ == 14 && index == 14 && size > this->data_offset_) {
/**
* BMP DIB header:
* 14-17: DIB header size
* 18-21: Image width
* 22-25: Image height
* 26-27: Number of color planes
* 28-29: Bits per pixel
* 30-33: Compression method
* 34-37: Image data size
* 38-41: Horizontal resolution
* 42-45: Vertical resolution
* 46-49: Number of colors in the color table
*/
this->width_ = encode_uint32(buffer[21], buffer[20], buffer[19], buffer[18]);
this->height_ = encode_uint32(buffer[25], buffer[24], buffer[23], buffer[22]);
this->bits_per_pixel_ = encode_uint16(buffer[29], buffer[28]);
this->compression_method_ = encode_uint32(buffer[33], buffer[32], buffer[31], buffer[30]);
this->image_data_size_ = encode_uint32(buffer[37], buffer[36], buffer[35], buffer[34]);
this->color_table_entries_ = encode_uint32(buffer[49], buffer[48], buffer[47], buffer[46]);
switch (this->bits_per_pixel_) {
case 1:
this->width_bytes_ = (this->width_ % 8 == 0) ? (this->width_ / 8) : (this->width_ / 8 + 1);
break;
default:
ESP_LOGE(TAG, "Unsupported bits per pixel: %d", this->bits_per_pixel_);
return DECODE_ERROR_UNSUPPORTED_FORMAT;
}
if (this->compression_method_ != 0) {
ESP_LOGE(TAG, "Unsupported compression method: %d", this->compression_method_);
return DECODE_ERROR_UNSUPPORTED_FORMAT;
}
if (!this->set_size(this->width_, this->height_)) {
return DECODE_ERROR_OUT_OF_MEMORY;
}
this->current_index_ = this->data_offset_;
index = this->data_offset_;
}
while (index < size) {
size_t paint_index = this->current_index_ - this->data_offset_;
uint8_t current_byte = buffer[index];
for (uint8_t i = 0; i < 8; i++) {
size_t x = (paint_index * 8) % this->width_ + i;
size_t y = (this->height_ - 1) - (paint_index / this->width_bytes_);
Color c = (current_byte & (1 << (7 - i))) ? display::COLOR_ON : display::COLOR_OFF;
this->draw(x, y, 1, 1, c);
}
this->current_index_++;
index++;
}
this->decoded_bytes_ += size;
return size;
};
} // namespace online_image
} // namespace esphome
#endif // USE_ONLINE_IMAGE_BMP_SUPPORT

View File

@ -0,0 +1,40 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT
#include "image_decoder.h"
namespace esphome {
namespace online_image {
/**
* @brief Image decoder specialization for PNG images.
*/
class BmpDecoder : public ImageDecoder {
public:
/**
* @brief Construct a new BMP Decoder object.
*
* @param display The image to decode the stream into.
*/
BmpDecoder(OnlineImage *image) : ImageDecoder(image) {}
int HOT decode(uint8_t *buffer, size_t size) override;
protected:
size_t current_index_{0};
ssize_t width_{0};
ssize_t height_{0};
uint16_t bits_per_pixel_{0};
uint32_t compression_method_{0};
uint32_t image_data_size_{0};
uint32_t color_table_entries_{0};
size_t width_bytes_{0};
size_t data_offset_{0};
};
} // namespace online_image
} // namespace esphome
#endif // USE_ONLINE_IMAGE_BMP_SUPPORT

View File

@ -8,10 +8,11 @@ namespace online_image {
static const char *const TAG = "online_image.decoder"; static const char *const TAG = "online_image.decoder";
void ImageDecoder::set_size(int width, int height) { bool ImageDecoder::set_size(int width, int height) {
this->image_->resize_(width, height); bool resized = this->image_->resize_(width, height);
this->x_scale_ = static_cast<double>(this->image_->buffer_width_) / width; this->x_scale_ = static_cast<double>(this->image_->buffer_width_) / width;
this->y_scale_ = static_cast<double>(this->image_->buffer_height_) / height; this->y_scale_ = static_cast<double>(this->image_->buffer_height_) / height;
return resized;
} }
void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) {

View File

@ -4,6 +4,12 @@
namespace esphome { namespace esphome {
namespace online_image { namespace online_image {
enum DecodeError : int {
DECODE_ERROR_INVALID_TYPE = -1,
DECODE_ERROR_UNSUPPORTED_FORMAT = -2,
DECODE_ERROR_OUT_OF_MEMORY = -3,
};
class OnlineImage; class OnlineImage;
/** /**
@ -24,7 +30,7 @@ class ImageDecoder {
* *
* @param download_size The total number of bytes that need to be downloaded for the image. * @param download_size The total number of bytes that need to be downloaded for the image.
*/ */
virtual void prepare(uint32_t download_size) { this->download_size_ = download_size; } virtual void prepare(size_t download_size) { this->download_size_ = download_size; }
/** /**
* @brief Decode a part of the image. It will try reading from the buffer. * @brief Decode a part of the image. It will try reading from the buffer.
@ -45,8 +51,9 @@ class ImageDecoder {
* *
* @param width The image's width. * @param width The image's width.
* @param height The image's height. * @param height The image's height.
* @return true if the image was resized, false otherwise.
*/ */
void set_size(int width, int height); bool set_size(int width, int height);
/** /**
* @brief Fill a rectangle on the display_buffer using the defined color. * @brief Fill a rectangle on the display_buffer using the defined color.
@ -68,8 +75,8 @@ class ImageDecoder {
OnlineImage *image_; OnlineImage *image_;
// Initializing to 1, to ensure it is distinguishable from initial "decoded_bytes_". // Initializing to 1, to ensure it is distinguishable from initial "decoded_bytes_".
// Will be overwritten anyway once the download size is known. // Will be overwritten anyway once the download size is known.
uint32_t download_size_ = 1; size_t download_size_ = 1;
uint32_t decoded_bytes_ = 0; size_t decoded_bytes_ = 0;
double x_scale_ = 1.0; double x_scale_ = 1.0;
double y_scale_ = 1.0; double y_scale_ = 1.0;
}; };

View File

@ -6,6 +6,9 @@ static const char *const TAG = "online_image";
#include "image_decoder.h" #include "image_decoder.h"
#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT
#include "bmp_image.h"
#endif
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
#include "png_image.h" #include "png_image.h"
#endif #endif
@ -118,9 +121,14 @@ void OnlineImage::update() {
ESP_LOGD(TAG, "Starting download"); ESP_LOGD(TAG, "Starting download");
size_t total_size = this->downloader_->content_length; size_t total_size = this->downloader_->content_length;
#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT
if (this->format_ == ImageFormat::BMP) {
this->decoder_ = make_unique<BmpDecoder>(this);
}
#endif // ONLINE_IMAGE_BMP_SUPPORT
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
if (this->format_ == ImageFormat::PNG) { if (this->format_ == ImageFormat::PNG) {
this->decoder_ = esphome::make_unique<PngDecoder>(this); this->decoder_ = make_unique<PngDecoder>(this);
} }
#endif // ONLINE_IMAGE_PNG_SUPPORT #endif // ONLINE_IMAGE_PNG_SUPPORT

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include "esphome/components/http_request/http_request.h"
#include "esphome/components/image/image.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/components/http_request/http_request.h"
#include "esphome/components/image/image.h"
#include "image_decoder.h" #include "image_decoder.h"
@ -27,6 +27,8 @@ enum ImageFormat {
JPEG, JPEG,
/** PNG format. */ /** PNG format. */
PNG, PNG,
/** BMP format. */
BMP,
}; };
/** /**
@ -146,7 +148,7 @@ class OnlineImage : public PollingComponent,
*/ */
int buffer_height_; int buffer_height_;
friend void ImageDecoder::set_size(int width, int height); friend bool ImageDecoder::set_size(int width, int height);
friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color); friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color);
}; };

View File

@ -41,7 +41,7 @@ static void draw_callback(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, ui
decoder->draw(x, y, w, h, color); decoder->draw(x, y, w, h, color);
} }
void PngDecoder::prepare(uint32_t download_size) { void PngDecoder::prepare(size_t download_size) {
ImageDecoder::prepare(download_size); ImageDecoder::prepare(download_size);
pngle_set_user_data(this->pngle_, this); pngle_set_user_data(this->pngle_, this);
pngle_set_init_callback(this->pngle_, init_callback); pngle_set_init_callback(this->pngle_, init_callback);
@ -51,7 +51,7 @@ void PngDecoder::prepare(uint32_t download_size) {
int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { int HOT PngDecoder::decode(uint8_t *buffer, size_t size) {
if (!this->pngle_) { if (!this->pngle_) {
ESP_LOGE(TAG, "PNG decoder engine not initialized!"); ESP_LOGE(TAG, "PNG decoder engine not initialized!");
return -1; return DECODE_ERROR_OUT_OF_MEMORY;
} }
if (size < 256 && size < this->download_size_ - this->decoded_bytes_) { if (size < 256 && size < this->download_size_ - this->decoded_bytes_) {
ESP_LOGD(TAG, "Waiting for data"); ESP_LOGD(TAG, "Waiting for data");

View File

@ -21,7 +21,7 @@ class PngDecoder : public ImageDecoder {
PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {} PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {}
~PngDecoder() override { pngle_destroy(this->pngle_); } ~PngDecoder() override { pngle_destroy(this->pngle_); }
void prepare(uint32_t download_size) override; void prepare(size_t download_size) override;
int HOT decode(uint8_t *buffer, size_t size) override; int HOT decode(uint8_t *buffer, size_t size) override;
protected: protected:

View File

@ -75,6 +75,8 @@ void PulseMeterSensor::loop() {
case MeterState::RUNNING: { case MeterState::RUNNING: {
uint32_t delta_us = this->get_->last_detected_edge_us_ - this->last_processed_edge_us_; uint32_t delta_us = this->get_->last_detected_edge_us_ - this->last_processed_edge_us_;
float pulse_width_us = delta_us / float(this->get_->count_); float pulse_width_us = delta_us / float(this->get_->count_);
ESP_LOGV(TAG, "New pulse, delta: %" PRIu32 " µs, count: %" PRIu32 ", width: %.5f µs", delta_us,
this->get_->count_, pulse_width_us);
this->publish_state((60.0f * 1000000.0f) / pulse_width_us); this->publish_state((60.0f * 1000000.0f) / pulse_width_us);
} break; } break;
} }

View File

@ -27,6 +27,7 @@ from .gpio import rp2040_pin_to_code # noqa
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
AUTO_LOAD = ["preferences"] AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
def set_core_data(config): def set_core_data(config):

View File

@ -19,6 +19,8 @@ from .boards import RTL87XX_BOARD_PINS, RTL87XX_BOARDS
CODEOWNERS = ["@kuba2k2"] CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["libretiny"] AUTO_LOAD = ["libretiny"]
IS_TARGET_PLATFORM = True
COMPONENT_DATA = LibreTinyComponent( COMPONENT_DATA = LibreTinyComponent(
name=COMPONENT_RTL87XX, name=COMPONENT_RTL87XX,

View File

@ -0,0 +1,25 @@
import esphome.codegen as cg
from esphome.components import binary_sensor
import esphome.config_validation as cv
from esphome.const import (
DEVICE_CLASS_OCCUPANCY,
CONF_HAS_TARGET,
)
from . import CONF_MR60BHA2_ID, MR60BHA2Component
DEPENDENCIES = ["seeed_mr60bha2"]
CONFIG_SCHEMA = {
cv.GenerateID(CONF_MR60BHA2_ID): cv.use_id(MR60BHA2Component),
cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_OCCUPANCY, icon="mdi:motion-sensor"
),
}
async def to_code(config):
mr60bha2_component = await cg.get_variable(config[CONF_MR60BHA2_ID])
if has_target_config := config.get(CONF_HAS_TARGET):
sens = await binary_sensor.new_binary_sensor(has_target_config)
cg.add(mr60bha2_component.set_has_target_binary_sensor(sens))

View File

@ -1,6 +1,7 @@
#include "seeed_mr60bha2.h" #include "seeed_mr60bha2.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <cinttypes>
#include <utility> #include <utility>
namespace esphome { namespace esphome {
@ -12,10 +13,14 @@ static const char *const TAG = "seeed_mr60bha2";
// items in an easy-to-read format, including the configuration key-value pairs. // items in an easy-to-read format, including the configuration key-value pairs.
void MR60BHA2Component::dump_config() { void MR60BHA2Component::dump_config() {
ESP_LOGCONFIG(TAG, "MR60BHA2:"); ESP_LOGCONFIG(TAG, "MR60BHA2:");
#ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "People Exist Binary Sensor", this->has_target_binary_sensor_);
#endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
LOG_SENSOR(" ", "Breath Rate Sensor", this->breath_rate_sensor_); LOG_SENSOR(" ", "Breath Rate Sensor", this->breath_rate_sensor_);
LOG_SENSOR(" ", "Heart Rate Sensor", this->heart_rate_sensor_); LOG_SENSOR(" ", "Heart Rate Sensor", this->heart_rate_sensor_);
LOG_SENSOR(" ", "Distance Sensor", this->distance_sensor_); LOG_SENSOR(" ", "Distance Sensor", this->distance_sensor_);
LOG_SENSOR(" ", "Target Number Sensor", this->num_targets_sensor_);
#endif #endif
} }
@ -94,7 +99,8 @@ bool MR60BHA2Component::validate_message_() {
uint16_t frame_type = encode_uint16(data[5], data[6]); uint16_t frame_type = encode_uint16(data[5], data[6]);
if (frame_type != BREATH_RATE_TYPE_BUFFER && frame_type != HEART_RATE_TYPE_BUFFER && if (frame_type != BREATH_RATE_TYPE_BUFFER && frame_type != HEART_RATE_TYPE_BUFFER &&
frame_type != DISTANCE_TYPE_BUFFER) { frame_type != DISTANCE_TYPE_BUFFER && frame_type != PEOPLE_EXIST_TYPE_BUFFER &&
frame_type != PRINT_CLOUD_BUFFER) {
return false; return false;
} }
@ -144,6 +150,18 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c
} }
} }
break; break;
case PEOPLE_EXIST_TYPE_BUFFER:
if (this->has_target_binary_sensor_ != nullptr && length >= 2) {
uint16_t has_target_int = encode_uint16(data[1], data[0]);
this->has_target_binary_sensor_->publish_state(has_target_int);
if (has_target_int == 0) {
this->breath_rate_sensor_->publish_state(0.0);
this->heart_rate_sensor_->publish_state(0.0);
this->distance_sensor_->publish_state(0.0);
this->num_targets_sensor_->publish_state(0);
}
}
break;
case HEART_RATE_TYPE_BUFFER: case HEART_RATE_TYPE_BUFFER:
if (this->heart_rate_sensor_ != nullptr && length >= 4) { if (this->heart_rate_sensor_ != nullptr && length >= 4) {
uint32_t current_heart_rate_int = encode_uint32(data[3], data[2], data[1], data[0]); uint32_t current_heart_rate_int = encode_uint32(data[3], data[2], data[1], data[0]);
@ -155,7 +173,7 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c
} }
break; break;
case DISTANCE_TYPE_BUFFER: case DISTANCE_TYPE_BUFFER:
if (!data[0]) { if (data[0] != 0) {
if (this->distance_sensor_ != nullptr && length >= 8) { if (this->distance_sensor_ != nullptr && length >= 8) {
uint32_t current_distance_int = encode_uint32(data[7], data[6], data[5], data[4]); uint32_t current_distance_int = encode_uint32(data[7], data[6], data[5], data[4]);
float distance_float; float distance_float;
@ -164,6 +182,12 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c
} }
} }
break; break;
case PRINT_CLOUD_BUFFER:
if (this->num_targets_sensor_ != nullptr && length >= 4) {
uint32_t current_num_targets_int = encode_uint32(data[3], data[2], data[1], data[0]);
this->num_targets_sensor_->publish_state(current_num_targets_int);
}
break;
default: default:
break; break;
} }

View File

@ -1,6 +1,9 @@
#pragma once #pragma once
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#endif #endif
@ -12,37 +15,23 @@
namespace esphome { namespace esphome {
namespace seeed_mr60bha2 { namespace seeed_mr60bha2 {
static const uint8_t DATA_BUF_MAX_SIZE = 12;
static const uint8_t FRAME_BUF_MAX_SIZE = 21;
static const uint8_t LEN_TO_HEAD_CKSUM = 8;
static const uint8_t LEN_TO_DATA_FRAME = 9;
static const uint8_t FRAME_HEADER_BUFFER = 0x01; static const uint8_t FRAME_HEADER_BUFFER = 0x01;
static const uint16_t BREATH_RATE_TYPE_BUFFER = 0x0A14; static const uint16_t BREATH_RATE_TYPE_BUFFER = 0x0A14;
static const uint16_t PEOPLE_EXIST_TYPE_BUFFER = 0x0F09;
static const uint16_t HEART_RATE_TYPE_BUFFER = 0x0A15; static const uint16_t HEART_RATE_TYPE_BUFFER = 0x0A15;
static const uint16_t DISTANCE_TYPE_BUFFER = 0x0A16; static const uint16_t DISTANCE_TYPE_BUFFER = 0x0A16;
static const uint16_t PRINT_CLOUD_BUFFER = 0x0A04;
enum FrameLocation {
LOCATE_FRAME_HEADER,
LOCATE_ID_FRAME1,
LOCATE_ID_FRAME2,
LOCATE_LENGTH_FRAME_H,
LOCATE_LENGTH_FRAME_L,
LOCATE_TYPE_FRAME1,
LOCATE_TYPE_FRAME2,
LOCATE_HEAD_CKSUM_FRAME, // Header checksum: [from the first byte to the previous byte of the HEAD_CKSUM bit]
LOCATE_DATA_FRAME,
LOCATE_DATA_CKSUM_FRAME, // Data checksum: [from the first to the previous byte of the DATA_CKSUM bit]
LOCATE_PROCESS_FRAME,
};
class MR60BHA2Component : public Component, class MR60BHA2Component : public Component,
public uart::UARTDevice { // The class name must be the name defined by text_sensor.py public uart::UARTDevice { // The class name must be the name defined by text_sensor.py
#ifdef USE_BINARY_SENSOR
SUB_BINARY_SENSOR(has_target);
#endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
SUB_SENSOR(breath_rate); SUB_SENSOR(breath_rate);
SUB_SENSOR(heart_rate); SUB_SENSOR(heart_rate);
SUB_SENSOR(distance); SUB_SENSOR(distance);
SUB_SENSOR(num_targets);
#endif #endif
public: public:

View File

@ -7,6 +7,7 @@ from esphome.const import (
ICON_HEART_PULSE, ICON_HEART_PULSE,
ICON_PULSE, ICON_PULSE,
ICON_SIGNAL, ICON_SIGNAL,
ICON_COUNTER,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_BEATS_PER_MINUTE, UNIT_BEATS_PER_MINUTE,
UNIT_CENTIMETER, UNIT_CENTIMETER,
@ -18,12 +19,13 @@ DEPENDENCIES = ["seeed_mr60bha2"]
CONF_BREATH_RATE = "breath_rate" CONF_BREATH_RATE = "breath_rate"
CONF_HEART_RATE = "heart_rate" CONF_HEART_RATE = "heart_rate"
CONF_NUM_TARGETS = "num_targets"
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(CONF_MR60BHA2_ID): cv.use_id(MR60BHA2Component), cv.GenerateID(CONF_MR60BHA2_ID): cv.use_id(MR60BHA2Component),
cv.Optional(CONF_BREATH_RATE): sensor.sensor_schema( cv.Optional(CONF_BREATH_RATE): sensor.sensor_schema(
accuracy_decimals=2, accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
icon=ICON_PULSE, icon=ICON_PULSE,
), ),
@ -40,6 +42,9 @@ CONFIG_SCHEMA = cv.Schema(
accuracy_decimals=2, accuracy_decimals=2,
icon=ICON_SIGNAL, icon=ICON_SIGNAL,
), ),
cv.Optional(CONF_NUM_TARGETS): sensor.sensor_schema(
icon=ICON_COUNTER,
),
} }
) )
@ -55,3 +60,6 @@ async def to_code(config):
if distance_config := config.get(CONF_DISTANCE): if distance_config := config.get(CONF_DISTANCE):
sens = await sensor.new_sensor(distance_config) sens = await sensor.new_sensor(distance_config)
cg.add(mr60bha2_component.set_distance_sensor(sens)) cg.add(mr60bha2_component.set_distance_sensor(sens))
if num_targets_config := config.get(CONF_NUM_TARGETS):
sens = await sensor.new_sensor(num_targets_config)
cg.add(mr60bha2_component.set_num_targets_sensor(sens))

View File

@ -88,7 +88,7 @@ void SPIDelegateBitBash::write(uint16_t data, size_t num_bits) { this->transfer_
uint16_t SPIDelegateBitBash::transfer_(uint16_t data, size_t num_bits) { uint16_t SPIDelegateBitBash::transfer_(uint16_t data, size_t num_bits) {
// Clock starts out at idle level // Clock starts out at idle level
this->clk_pin_->digital_write(clock_polarity_); this->clk_pin_->digital_write(clock_polarity_);
uint8_t out_data = 0; uint16_t out_data = 0;
for (uint8_t i = 0; i != num_bits; i++) { for (uint8_t i = 0; i != num_bits; i++) {
uint8_t shift; uint8_t shift;

View File

@ -22,7 +22,6 @@ from esphome.const import (
CONF_PACKAGES, CONF_PACKAGES,
CONF_PLATFORM, CONF_PLATFORM,
CONF_SUBSTITUTIONS, CONF_SUBSTITUTIONS,
TARGET_PLATFORMS,
) )
from esphome.core import CORE, DocumentRange, EsphomeError from esphome.core import CORE, DocumentRange, EsphomeError
import esphome.core.config as core_config import esphome.core.config as core_config
@ -833,7 +832,7 @@ def validate_config(
result[CONF_ESPHOME] = config[CONF_ESPHOME] result[CONF_ESPHOME] = config[CONF_ESPHOME]
result.add_output_path([CONF_ESPHOME], CONF_ESPHOME) result.add_output_path([CONF_ESPHOME], CONF_ESPHOME)
try: try:
core_config.preload_core_config(config, result) target_platform = core_config.preload_core_config(config, result)
except vol.Invalid as err: except vol.Invalid as err:
result.add_error(err) result.add_error(err)
return result return result
@ -845,9 +844,9 @@ def validate_config(
cv.All(cv.version_number, cv.validate_esphome_version)(min_version) cv.All(cv.version_number, cv.validate_esphome_version)(min_version)
# First run platform validation steps # First run platform validation steps
for key in TARGET_PLATFORMS: result.add_validation_step(
if key in config: LoadValidationStep(target_platform, config[target_platform])
result.add_validation_step(LoadValidationStep(key, config[key])) )
result.run_validation_steps() result.run_validation_steps()
if result.errors: if result.errors:

View File

@ -15,15 +15,6 @@ PLATFORM_LIBRETINY_OLDSTYLE = "libretiny"
PLATFORM_RP2040 = "rp2040" PLATFORM_RP2040 = "rp2040"
PLATFORM_RTL87XX = "rtl87xx" PLATFORM_RTL87XX = "rtl87xx"
TARGET_PLATFORMS = [
PLATFORM_BK72XX,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_HOST,
PLATFORM_LIBRETINY_OLDSTYLE,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
]
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"} HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}

View File

@ -1,6 +1,7 @@
import logging import logging
import multiprocessing import multiprocessing
import os import os
from pathlib import Path
from esphome import automation from esphome import automation
import esphome.codegen as cg import esphome.codegen as cg
@ -28,7 +29,6 @@ from esphome.const import (
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_VERSION, CONF_VERSION,
KEY_CORE, KEY_CORE,
TARGET_PLATFORMS,
__version__ as ESPHOME_VERSION, __version__ as ESPHOME_VERSION,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
@ -174,7 +174,31 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema(
) )
def preload_core_config(config, result): def _is_target_platform(name):
from esphome.loader import get_component
try:
if get_component(name, True).is_target_platform:
return True
except KeyError:
pass
return False
def _list_target_platforms():
target_platforms = []
root = Path(__file__).parents[1]
for path in (root / "components").iterdir():
if not path.is_dir():
continue
if not (path / "__init__.py").is_file():
continue
if _is_target_platform(path.name):
target_platforms += [path.name]
return target_platforms
def preload_core_config(config, result) -> str:
with cv.prepend_path(CONF_ESPHOME): with cv.prepend_path(CONF_ESPHOME):
conf = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) conf = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME])
@ -187,12 +211,16 @@ def preload_core_config(config, result):
conf[CONF_BUILD_PATH] = os.path.join(build_path, CORE.name) conf[CONF_BUILD_PATH] = os.path.join(build_path, CORE.name)
CORE.build_path = CORE.relative_internal_path(conf[CONF_BUILD_PATH]) CORE.build_path = CORE.relative_internal_path(conf[CONF_BUILD_PATH])
target_platforms = [key for key in TARGET_PLATFORMS if key in config] target_platforms = []
for domain, _ in config.items():
if _is_target_platform(domain):
target_platforms += [domain]
if not target_platforms: if not target_platforms:
raise cv.Invalid( raise cv.Invalid(
"Platform missing. You must include one of the available platform keys: " "Platform missing. You must include one of the available platform keys: "
+ ", ".join(TARGET_PLATFORMS), + ", ".join(_list_target_platforms()),
[CONF_ESPHOME], [CONF_ESPHOME],
) )
if len(target_platforms) > 1: if len(target_platforms) > 1:
@ -202,6 +230,7 @@ def preload_core_config(config, result):
) )
config[CONF_ESPHOME] = conf config[CONF_ESPHOME] = conf
return target_platforms[0]
def include_file(path, basename): def include_file(path, basename):

View File

@ -60,6 +60,7 @@
#define USE_NETWORK #define USE_NETWORK
#define USE_NEXTION_TFT_UPLOAD #define USE_NEXTION_TFT_UPLOAD
#define USE_NUMBER #define USE_NUMBER
#define USE_ONLINE_IMAGE_BMP_SUPPORT
#define USE_ONLINE_IMAGE_PNG_SUPPORT #define USE_ONLINE_IMAGE_PNG_SUPPORT
#define USE_OTA #define USE_OTA
#define USE_OTA_PASSWORD #define USE_OTA_PASSWORD

View File

@ -52,6 +52,10 @@ class ComponentManifest:
def is_platform_component(self) -> bool: def is_platform_component(self) -> bool:
return getattr(self.module, "IS_PLATFORM_COMPONENT", False) return getattr(self.module, "IS_PLATFORM_COMPONENT", False)
@property
def is_target_platform(self) -> bool:
return getattr(self.module, "IS_TARGET_PLATFORM", False)
@property @property
def config_schema(self) -> Optional[Any]: def config_schema(self) -> Optional[Any]:
return getattr(self.module, "CONFIG_SCHEMA", None) return getattr(self.module, "CONFIG_SCHEMA", None)
@ -169,13 +173,15 @@ def install_custom_components_meta_finder():
install_meta_finder(custom_components_dir) install_meta_finder(custom_components_dir)
def _lookup_module(domain): def _lookup_module(domain, exception):
if domain in _COMPONENT_CACHE: if domain in _COMPONENT_CACHE:
return _COMPONENT_CACHE[domain] return _COMPONENT_CACHE[domain]
try: try:
module = importlib.import_module(f"esphome.components.{domain}") module = importlib.import_module(f"esphome.components.{domain}")
except ImportError as e: except ImportError as e:
if exception:
raise
if "No module named" in str(e): if "No module named" in str(e):
_LOGGER.info( _LOGGER.info(
"Unable to import component %s: %s", domain, str(e), exc_info=False "Unable to import component %s: %s", domain, str(e), exc_info=False
@ -184,6 +190,8 @@ def _lookup_module(domain):
_LOGGER.error("Unable to import component %s:", domain, exc_info=True) _LOGGER.error("Unable to import component %s:", domain, exc_info=True)
return None return None
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
if exception:
raise
_LOGGER.error("Unable to load component %s:", domain, exc_info=True) _LOGGER.error("Unable to load component %s:", domain, exc_info=True)
return None return None
@ -192,14 +200,14 @@ def _lookup_module(domain):
return manif return manif
def get_component(domain): def get_component(domain, exception=False):
assert "." not in domain assert "." not in domain
return _lookup_module(domain) return _lookup_module(domain, exception)
def get_platform(domain, platform): def get_platform(domain, platform):
full = f"{platform}.{domain}" full = f"{platform}.{domain}"
return _lookup_module(full) return _lookup_module(full, False)
_COMPONENT_CACHE = {} _COMPONENT_CACHE = {}

View File

@ -10,4 +10,5 @@ sensor:
- platform: ads1115 - platform: ads1115
multiplexer: A0_A1 multiplexer: A0_A1
gain: 1.024 gain: 1.024
sample_rate: 128
id: ads1115_sensor id: ads1115_sensor

View File

@ -10,4 +10,5 @@ sensor:
- platform: ads1115 - platform: ads1115
multiplexer: A0_A1 multiplexer: A0_A1
gain: 1.024 gain: 1.024
sample_rate: 128
id: ads1115_sensor id: ads1115_sensor

View File

@ -10,4 +10,5 @@ sensor:
- platform: ads1115 - platform: ads1115
multiplexer: A0_A1 multiplexer: A0_A1
gain: 1.024 gain: 1.024
sample_rate: 128
id: ads1115_sensor id: ads1115_sensor

View File

@ -10,4 +10,5 @@ sensor:
- platform: ads1115 - platform: ads1115
multiplexer: A0_A1 multiplexer: A0_A1
gain: 1.024 gain: 1.024
sample_rate: 128
id: ads1115_sensor id: ads1115_sensor

View File

@ -10,4 +10,5 @@ sensor:
- platform: ads1115 - platform: ads1115
multiplexer: A0_A1 multiplexer: A0_A1
gain: 1.024 gain: 1.024
sample_rate: 128
id: ads1115_sensor id: ads1115_sensor

View File

@ -10,4 +10,5 @@ sensor:
- platform: ads1115 - platform: ads1115
multiplexer: A0_A1 multiplexer: A0_A1
gain: 1.024 gain: 1.024
sample_rate: 128
id: ads1115_sensor id: ads1115_sensor

View File

@ -0,0 +1,14 @@
esphome:
on_boot:
then:
- audio_adc.set_mic_gain: 0db
- audio_adc.set_mic_gain: !lambda 'return 4;'
i2c:
- id: i2c_es7243e
scl: ${scl_pin}
sda: ${sda_pin}
audio_adc:
- platform: es7243e
id: es7243e_adc

View File

@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO16
sda_pin: GPIO17
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO5
sda_pin: GPIO4
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
scl_pin: GPIO16
sda_pin: GPIO17
<<: !include common.yaml

View File

@ -26,6 +26,10 @@ online_image:
format: PNG format: PNG
type: RGB type: RGB
transparency: chroma_key transparency: chroma_key
- id: online_binary_bmp
url: https://samples-files.com/samples/images/bmp/480-360-sample.bmp
format: BMP
type: BINARY
# Check the set_url action # Check the set_url action
esphome: esphome:

View File

@ -9,6 +9,11 @@ uart:
seeed_mr60bha2: seeed_mr60bha2:
id: my_seeed_mr60bha2 id: my_seeed_mr60bha2
binary_sensor:
- platform: seeed_mr60bha2
has_target:
name: "Person Information"
sensor: sensor:
- platform: seeed_mr60bha2 - platform: seeed_mr60bha2
breath_rate: breath_rate:
@ -17,3 +22,5 @@ sensor:
name: "Real-time heart rate" name: "Real-time heart rate"
distance: distance:
name: "Distance to detection object" name: "Distance to detection object"
num_targets:
name: "Target Number"