mirror of
https://github.com/esphome/esphome.git
synced 2025-01-30 23:02:14 +01:00
Merge branch 'dev' into vornado-ir
This commit is contained in:
commit
db852fcd39
4
.github/actions/build-image/action.yaml
vendored
4
.github/actions/build-image/action.yaml
vendored
@ -46,7 +46,7 @@ runs:
|
||||
|
||||
- name: Build and push to ghcr by digest
|
||||
id: build-ghcr
|
||||
uses: docker/build-push-action@v6.11.0
|
||||
uses: docker/build-push-action@v6.12.0
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: false
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
@ -72,7 +72,7 @@ runs:
|
||||
|
||||
- name: Build and push to dockerhub by digest
|
||||
id: build-dockerhub
|
||||
uses: docker/build-push-action@v6.11.0
|
||||
uses: docker/build-push-action@v6.12.0
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: false
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
|
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9.0.0
|
||||
- uses: actions/stale@v9.1.0
|
||||
with:
|
||||
days-before-pr-stale: 90
|
||||
days-before-pr-close: 7
|
||||
@ -37,7 +37,7 @@ jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9.0.0
|
||||
- uses: actions/stale@v9.1.0
|
||||
with:
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
|
@ -11,14 +11,6 @@ repos:
|
||||
args: [--fix]
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 24.4.2
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
- --safe
|
||||
- --quiet
|
||||
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.1.0
|
||||
hooks:
|
||||
@ -53,6 +45,6 @@ repos:
|
||||
hooks:
|
||||
- id: pylint
|
||||
name: pylint
|
||||
entry: script/run-in-env.sh pylint
|
||||
language: script
|
||||
entry: python script/run-in-env pylint
|
||||
language: system
|
||||
types: [python]
|
||||
|
@ -49,6 +49,7 @@ esphome/components/atc_mithermometer/* @ahpohl
|
||||
esphome/components/atm90e26/* @danieltwagner
|
||||
esphome/components/atm90e32/* @circuitsetup @descipher
|
||||
esphome/components/audio/* @kahrendt
|
||||
esphome/components/audio_adc/* @kbx81
|
||||
esphome/components/audio_dac/* @kbx81
|
||||
esphome/components/axs15231/* @clydebarrow
|
||||
esphome/components/b_parasite/* @rbaron
|
||||
@ -131,6 +132,9 @@ esphome/components/ens160_base/* @latonita @vincentscode
|
||||
esphome/components/ens160_i2c/* @latonita
|
||||
esphome/components/ens160_spi/* @latonita
|
||||
esphome/components/ens210/* @itn3rd77
|
||||
esphome/components/es7210/* @kahrendt
|
||||
esphome/components/es7243e/* @kbx81
|
||||
esphome/components/es8156/* @kbx81
|
||||
esphome/components/es8311/* @kahrendt @kroimon
|
||||
esphome/components/esp32/* @esphome/core
|
||||
esphome/components/esp32_ble/* @Rapsssito @jesserockz
|
||||
@ -338,7 +342,6 @@ esphome/components/radon_eye_rd200/* @jeffeb3
|
||||
esphome/components/rc522/* @glmnet
|
||||
esphome/components/rc522_i2c/* @glmnet
|
||||
esphome/components/rc522_spi/* @glmnet
|
||||
esphome/components/resistance_sampler/* @jesserockz
|
||||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/rf_bridge/* @jesserockz
|
||||
esphome/components/rgbct/* @jesserockz
|
||||
|
41
esphome/components/audio_adc/__init__.py
Normal file
41
esphome/components/audio_adc/__init__.py
Normal file
@ -0,0 +1,41 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_MIC_GAIN
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
audio_adc_ns = cg.esphome_ns.namespace("audio_adc")
|
||||
AudioAdc = audio_adc_ns.class_("AudioAdc")
|
||||
|
||||
SetMicGainAction = audio_adc_ns.class_("SetMicGainAction", automation.Action)
|
||||
|
||||
|
||||
SET_MIC_GAIN_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(AudioAdc),
|
||||
cv.Required(CONF_MIC_GAIN): cv.templatable(cv.decibel),
|
||||
},
|
||||
key=CONF_MIC_GAIN,
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"audio_adc.set_mic_gain", SetMicGainAction, SET_MIC_GAIN_ACTION_SCHEMA
|
||||
)
|
||||
async def audio_adc_set_mic_gain_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config.get(CONF_MIC_GAIN), args, float)
|
||||
cg.add(var.set_mic_gain(template_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_AUDIO_ADC")
|
||||
cg.add_global(audio_adc_ns.using)
|
17
esphome/components/audio_adc/audio_adc.h
Normal file
17
esphome/components/audio_adc/audio_adc.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace audio_adc {
|
||||
|
||||
class AudioAdc {
|
||||
public:
|
||||
virtual bool set_mic_gain(float mic_gain) = 0;
|
||||
|
||||
virtual float mic_gain() = 0;
|
||||
};
|
||||
|
||||
} // namespace audio_adc
|
||||
} // namespace esphome
|
23
esphome/components/audio_adc/automation.h
Normal file
23
esphome/components/audio_adc/automation.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "audio_adc.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace audio_adc {
|
||||
|
||||
template<typename... Ts> class SetMicGainAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit SetMicGainAction(AudioAdc *audio_adc) : audio_adc_(audio_adc) {}
|
||||
|
||||
TEMPLATABLE_VALUE(float, mic_gain)
|
||||
|
||||
void play(Ts... x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); }
|
||||
|
||||
protected:
|
||||
AudioAdc *audio_adc_;
|
||||
};
|
||||
|
||||
} // namespace audio_adc
|
||||
} // namespace esphome
|
@ -1,3 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
custom_ns = cg.esphome_ns.namespace("custom")
|
@ -1,31 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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.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()
|
||||
),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -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
|
@ -1,30 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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")
|
||||
CONF_CLIMATES = "climates"
|
||||
|
||||
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),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -1,30 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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")
|
||||
CONF_COVERS = "covers"
|
||||
|
||||
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),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -1,30 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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")
|
||||
CONF_LIGHTS = "lights"
|
||||
|
||||
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),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -1,61 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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")
|
||||
CustomFloatOutputConstructor = custom_ns.class_("CustomFloatOutputConstructor")
|
||||
|
||||
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,
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -1,27 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.sensor_schema()),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -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
|
@ -1,27 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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.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)),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -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
|
@ -1,32 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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.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()
|
||||
),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -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
|
@ -1,31 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
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
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CustomComponentConstructor),
|
||||
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
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -298,6 +298,12 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
|
||||
if (this->cell_16_voltage_sensor_) {
|
||||
this->cell_16_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
|
||||
}
|
||||
if (this->cell_17_voltage_sensor_) {
|
||||
this->cell_17_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
|
||||
}
|
||||
if (this->cell_18_voltage_sensor_) {
|
||||
this->cell_18_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -54,6 +54,8 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
|
||||
SUB_SENSOR(cell_14_voltage)
|
||||
SUB_SENSOR(cell_15_voltage)
|
||||
SUB_SENSOR(cell_16_voltage)
|
||||
SUB_SENSOR(cell_17_voltage)
|
||||
SUB_SENSOR(cell_18_voltage)
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
|
@ -52,6 +52,8 @@ CONF_CELL_13_VOLTAGE = "cell_13_voltage"
|
||||
CONF_CELL_14_VOLTAGE = "cell_14_voltage"
|
||||
CONF_CELL_15_VOLTAGE = "cell_15_voltage"
|
||||
CONF_CELL_16_VOLTAGE = "cell_16_voltage"
|
||||
CONF_CELL_17_VOLTAGE = "cell_17_voltage"
|
||||
CONF_CELL_18_VOLTAGE = "cell_18_voltage"
|
||||
ICON_CURRENT_DC = "mdi:current-dc"
|
||||
ICON_BATTERY_OUTLINE = "mdi:battery-outline"
|
||||
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
|
||||
@ -92,6 +94,8 @@ TYPES = [
|
||||
CONF_CELL_14_VOLTAGE,
|
||||
CONF_CELL_15_VOLTAGE,
|
||||
CONF_CELL_16_VOLTAGE,
|
||||
CONF_CELL_17_VOLTAGE,
|
||||
CONF_CELL_18_VOLTAGE,
|
||||
]
|
||||
|
||||
CELL_VOLTAGE_SCHEMA = sensor.sensor_schema(
|
||||
@ -212,6 +216,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_CELL_14_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_15_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_16_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_17_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_18_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
0
esphome/components/es7210/__init__.py
Normal file
0
esphome/components/es7210/__init__.py
Normal file
51
esphome/components/es7210/audio_adc.py
Normal file
51
esphome/components/es7210/audio_adc.py
Normal file
@ -0,0 +1,51 @@
|
||||
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_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE
|
||||
|
||||
CODEOWNERS = ["@kahrendt"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
es7210_ns = cg.esphome_ns.namespace("es7210")
|
||||
ES7210 = es7210_ns.class_("ES7210", AudioAdc, cg.Component, i2c.I2CDevice)
|
||||
|
||||
|
||||
es7210_bits_per_sample = es7210_ns.enum("ES7210BitsPerSample")
|
||||
ES7210_BITS_PER_SAMPLE_ENUM = {
|
||||
16: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_16,
|
||||
24: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_24,
|
||||
32: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_32,
|
||||
}
|
||||
|
||||
|
||||
ES7210_MIC_GAINS = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 34.5, 36, 37.5]
|
||||
|
||||
_validate_bits = cv.float_with_unit("bits", "bit")
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ES7210),
|
||||
cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All(
|
||||
_validate_bits, cv.enum(ES7210_BITS_PER_SAMPLE_ENUM)
|
||||
),
|
||||
cv.Optional(CONF_MIC_GAIN, default="24db"): cv.All(
|
||||
cv.decibel, cv.one_of(*ES7210_MIC_GAINS)
|
||||
),
|
||||
cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(i2c.i2c_device_schema(0x40))
|
||||
)
|
||||
|
||||
|
||||
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_bits_per_sample(config[CONF_BITS_PER_SAMPLE]))
|
||||
cg.add(var.set_mic_gain(config[CONF_MIC_GAIN]))
|
||||
cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
|
228
esphome/components/es7210/es7210.cpp
Normal file
228
esphome/components/es7210/es7210.cpp
Normal file
@ -0,0 +1,228 @@
|
||||
#include "es7210.h"
|
||||
#include "es7210_const.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace es7210 {
|
||||
|
||||
static const char *const TAG = "es7210";
|
||||
|
||||
static const size_t MCLK_DIV_FRE = 256;
|
||||
|
||||
// Mark the component as failed; use only in setup
|
||||
#define ES7210_ERROR_FAILED(func) \
|
||||
if (!(func)) { \
|
||||
this->mark_failed(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
// Return false; use outside of setup
|
||||
#define ES7210_ERROR_CHECK(func) \
|
||||
if (!(func)) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
void ES7210::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ES7210 audio ADC:");
|
||||
ESP_LOGCONFIG(TAG, " Bits Per Sample: %" PRIu8, this->bits_per_sample_);
|
||||
ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_rate_);
|
||||
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, " Failed to initialize");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ES7210::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ES7210...");
|
||||
|
||||
// Software reset
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0xff));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x32));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_CLOCK_OFF_REG01, 0x3f));
|
||||
|
||||
// Set initialization time when device powers up
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL0_REG09, 0x30));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL1_REG0A, 0x30));
|
||||
|
||||
// Configure HFP for all ADC channels
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF2_REG23, 0x2a));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF1_REG22, 0x0a));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF2_REG20, 0x0a));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF1_REG21, 0x2a));
|
||||
|
||||
// Secondary I2S mode settings
|
||||
ES7210_ERROR_FAILED(this->es7210_update_reg_bit_(ES7210_MODE_CONFIG_REG08, 0x01, 0x00));
|
||||
|
||||
// Configure analog power
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ANALOG_REG40, 0xC3));
|
||||
|
||||
// Set mic bias
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_BIAS_REG41, 0x70));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_BIAS_REG42, 0x70));
|
||||
|
||||
// Configure I2S settings, sample rate, and microphone gains
|
||||
ES7210_ERROR_FAILED(this->configure_i2s_format_());
|
||||
ES7210_ERROR_FAILED(this->configure_sample_rate_());
|
||||
ES7210_ERROR_FAILED(this->configure_mic_gain_());
|
||||
|
||||
// Power on mics 1 through 4
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC1_POWER_REG47, 0x08));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC2_POWER_REG48, 0x08));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC3_POWER_REG49, 0x08));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC4_POWER_REG4A, 0x08));
|
||||
|
||||
// Power down DLL
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_POWER_DOWN_REG06, 0x04));
|
||||
|
||||
// Power on MIC1-4 bias & ADC1-4 & PGA1-4 Power
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x0F));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x0F));
|
||||
|
||||
// Enable device
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x71));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x41));
|
||||
|
||||
this->setup_complete_ = true;
|
||||
}
|
||||
|
||||
bool ES7210::set_mic_gain(float mic_gain) {
|
||||
this->mic_gain_ = clamp<float>(mic_gain, ES7210_MIC_GAIN_MIN, ES7210_MIC_GAIN_MAX);
|
||||
if (this->setup_complete_) {
|
||||
return this->configure_mic_gain_();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ES7210::configure_sample_rate_() {
|
||||
int mclk_fre = this->sample_rate_ * MCLK_DIV_FRE;
|
||||
int coeff = -1;
|
||||
|
||||
for (int i = 0; i < (sizeof(ES7210_COEFFICIENTS) / sizeof(ES7210_COEFFICIENTS[0])); ++i) {
|
||||
if (ES7210_COEFFICIENTS[i].lrclk == this->sample_rate_ && ES7210_COEFFICIENTS[i].mclk == mclk_fre)
|
||||
coeff = i;
|
||||
}
|
||||
|
||||
if (coeff >= 0) {
|
||||
// Set adc_div & doubler & dll
|
||||
uint8_t regv;
|
||||
ES7210_ERROR_CHECK(this->read_byte(ES7210_MAINCLK_REG02, ®v));
|
||||
regv = regv & 0x00;
|
||||
regv |= ES7210_COEFFICIENTS[coeff].adc_div;
|
||||
regv |= ES7210_COEFFICIENTS[coeff].doubler << 6;
|
||||
regv |= ES7210_COEFFICIENTS[coeff].dll << 7;
|
||||
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_MAINCLK_REG02, regv));
|
||||
|
||||
// Set osr
|
||||
regv = ES7210_COEFFICIENTS[coeff].osr;
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_OSR_REG07, regv));
|
||||
// Set lrck
|
||||
regv = ES7210_COEFFICIENTS[coeff].lrck_h;
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_LRCK_DIVH_REG04, regv));
|
||||
regv = ES7210_COEFFICIENTS[coeff].lrck_l;
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_LRCK_DIVL_REG05, regv));
|
||||
} else {
|
||||
// Invalid sample frequency
|
||||
ESP_LOGE(TAG, "Invalid sample rate");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ES7210::configure_mic_gain_() {
|
||||
auto regv = this->es7210_gain_reg_value_(this->mic_gain_);
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00));
|
||||
}
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0xff));
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0xff));
|
||||
|
||||
// Configure mic 1
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00));
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x10, 0x10));
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x0f, regv));
|
||||
|
||||
// Configure mic 2
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00));
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x10, 0x10));
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x0f, regv));
|
||||
|
||||
// Configure mic 3
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00));
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x10, 0x10));
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x0f, regv));
|
||||
|
||||
// Configure mic 4
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00));
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x10, 0x10));
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x0f, regv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t ES7210::es7210_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;
|
||||
}
|
||||
|
||||
bool ES7210::configure_i2s_format_() {
|
||||
// Configure bits per sample
|
||||
uint8_t reg_val = 0;
|
||||
switch (this->bits_per_sample_) {
|
||||
case ES7210_BITS_PER_SAMPLE_16:
|
||||
reg_val = 0x60;
|
||||
break;
|
||||
case ES7210_BITS_PER_SAMPLE_18:
|
||||
reg_val = 0x40;
|
||||
break;
|
||||
case ES7210_BITS_PER_SAMPLE_20:
|
||||
reg_val = 0x20;
|
||||
break;
|
||||
case ES7210_BITS_PER_SAMPLE_24:
|
||||
reg_val = 0x00;
|
||||
break;
|
||||
case ES7210_BITS_PER_SAMPLE_32:
|
||||
reg_val = 0x80;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE1_REG11, reg_val));
|
||||
|
||||
if (this->enable_tdm_) {
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE2_REG12, 0x02));
|
||||
} else {
|
||||
// Microphones 1 and 2 output on SDOUT1, microphones 3 and 4 output on SDOUT2
|
||||
ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE2_REG12, 0x00));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ES7210::es7210_update_reg_bit_(uint8_t reg_addr, uint8_t update_bits, uint8_t data) {
|
||||
uint8_t regv;
|
||||
ES7210_ERROR_CHECK(this->read_byte(reg_addr, ®v));
|
||||
regv = (regv & (~update_bits)) | (update_bits & data);
|
||||
return this->write_byte(reg_addr, regv);
|
||||
}
|
||||
|
||||
} // namespace es7210
|
||||
} // namespace esphome
|
62
esphome/components/es7210/es7210.h
Normal file
62
esphome/components/es7210/es7210.h
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/audio_adc/audio_adc.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include "es7210_const.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace es7210 {
|
||||
|
||||
enum ES7210BitsPerSample : uint8_t {
|
||||
ES7210_BITS_PER_SAMPLE_16 = 16,
|
||||
ES7210_BITS_PER_SAMPLE_18 = 18,
|
||||
ES7210_BITS_PER_SAMPLE_20 = 20,
|
||||
ES7210_BITS_PER_SAMPLE_24 = 24,
|
||||
ES7210_BITS_PER_SAMPLE_32 = 32,
|
||||
};
|
||||
|
||||
class ES7210 : public audio_adc::AudioAdc, public Component, public i2c::I2CDevice {
|
||||
/* Class for configuring an ES7210 ADC for microphone input.
|
||||
* Based on code from:
|
||||
* - https://github.com/espressif/esp-bsp/ (accessed 20241219)
|
||||
* - https://github.com/espressif/esp-adf/ (accessed 20241219)
|
||||
*/
|
||||
public:
|
||||
void setup() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void dump_config() override;
|
||||
|
||||
void set_bits_per_sample(ES7210BitsPerSample bits_per_sample) { this->bits_per_sample_ = bits_per_sample; }
|
||||
bool set_mic_gain(float mic_gain) override;
|
||||
void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; }
|
||||
|
||||
float mic_gain() override { return this->mic_gain_; };
|
||||
|
||||
protected:
|
||||
/// @brief Updates an I2C registry address by modifying the current state
|
||||
/// @param reg_addr I2C register address
|
||||
/// @param update_bits Mask of allowed bits to be modified
|
||||
/// @param data Bit values to be written
|
||||
/// @return True if successful, false otherwise
|
||||
bool es7210_update_reg_bit_(uint8_t reg_addr, uint8_t update_bits, uint8_t data);
|
||||
|
||||
/// @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 es7210_gain_reg_value_(float mic_gain);
|
||||
|
||||
bool configure_i2s_format_();
|
||||
bool configure_mic_gain_();
|
||||
bool configure_sample_rate_();
|
||||
|
||||
bool setup_complete_{false};
|
||||
bool enable_tdm_{false}; // TDM is unsupported in ESPHome as of version 2024.12
|
||||
float mic_gain_{0};
|
||||
ES7210BitsPerSample bits_per_sample_{ES7210_BITS_PER_SAMPLE_16};
|
||||
uint32_t sample_rate_{0};
|
||||
};
|
||||
|
||||
} // namespace es7210
|
||||
} // namespace esphome
|
129
esphome/components/es7210/es7210_const.h
Normal file
129
esphome/components/es7210/es7210_const.h
Normal file
@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace es7210 {
|
||||
|
||||
// ES7210 register addresses
|
||||
static const uint8_t ES7210_RESET_REG00 = 0x00; /* Reset control */
|
||||
static const uint8_t ES7210_CLOCK_OFF_REG01 = 0x01; /* Used to turn off the ADC clock */
|
||||
static const uint8_t ES7210_MAINCLK_REG02 = 0x02; /* Set ADC clock frequency division */
|
||||
|
||||
static const uint8_t ES7210_MASTER_CLK_REG03 = 0x03; /* MCLK source $ SCLK division */
|
||||
static const uint8_t ES7210_LRCK_DIVH_REG04 = 0x04; /* lrck_divh */
|
||||
static const uint8_t ES7210_LRCK_DIVL_REG05 = 0x05; /* lrck_divl */
|
||||
static const uint8_t ES7210_POWER_DOWN_REG06 = 0x06; /* power down */
|
||||
static const uint8_t ES7210_OSR_REG07 = 0x07;
|
||||
static const uint8_t ES7210_MODE_CONFIG_REG08 = 0x08; /* Set primary/secondary & channels */
|
||||
static const uint8_t ES7210_TIME_CONTROL0_REG09 = 0x09; /* Set Chip intial state period*/
|
||||
static const uint8_t ES7210_TIME_CONTROL1_REG0A = 0x0A; /* Set Power up state period */
|
||||
static const uint8_t ES7210_SDP_INTERFACE1_REG11 = 0x11; /* Set sample & fmt */
|
||||
static const uint8_t ES7210_SDP_INTERFACE2_REG12 = 0x12; /* Pins state */
|
||||
static const uint8_t ES7210_ADC_AUTOMUTE_REG13 = 0x13; /* Set mute */
|
||||
static const uint8_t ES7210_ADC34_MUTERANGE_REG14 = 0x14; /* Set mute range */
|
||||
static const uint8_t ES7210_ADC12_MUTERANGE_REG15 = 0x15; /* Set mute range */
|
||||
static const uint8_t ES7210_ADC34_HPF2_REG20 = 0x20; /* HPF */
|
||||
static const uint8_t ES7210_ADC34_HPF1_REG21 = 0x21; /* HPF */
|
||||
static const uint8_t ES7210_ADC12_HPF1_REG22 = 0x22; /* HPF */
|
||||
static const uint8_t ES7210_ADC12_HPF2_REG23 = 0x23; /* HPF */
|
||||
static const uint8_t ES7210_ANALOG_REG40 = 0x40; /* ANALOG Power */
|
||||
static const uint8_t ES7210_MIC12_BIAS_REG41 = 0x41;
|
||||
static const uint8_t ES7210_MIC34_BIAS_REG42 = 0x42;
|
||||
static const uint8_t ES7210_MIC1_GAIN_REG43 = 0x43;
|
||||
static const uint8_t ES7210_MIC2_GAIN_REG44 = 0x44;
|
||||
static const uint8_t ES7210_MIC3_GAIN_REG45 = 0x45;
|
||||
static const uint8_t ES7210_MIC4_GAIN_REG46 = 0x46;
|
||||
static const uint8_t ES7210_MIC1_POWER_REG47 = 0x47;
|
||||
static const uint8_t ES7210_MIC2_POWER_REG48 = 0x48;
|
||||
static const uint8_t ES7210_MIC3_POWER_REG49 = 0x49;
|
||||
static const uint8_t ES7210_MIC4_POWER_REG4A = 0x4A;
|
||||
static const uint8_t ES7210_MIC12_POWER_REG4B = 0x4B; /* MICBias & ADC & PGA Power */
|
||||
static const uint8_t ES7210_MIC34_POWER_REG4C = 0x4C;
|
||||
|
||||
/*
|
||||
* Clock coefficient structure
|
||||
*/
|
||||
struct ES7210Coefficient {
|
||||
uint32_t mclk; // mclk frequency
|
||||
uint32_t lrclk;
|
||||
uint8_t ss_ds;
|
||||
uint8_t adc_div;
|
||||
uint8_t dll; // dll_bypass
|
||||
uint8_t doubler; // doubler_enable
|
||||
uint8_t osr; // adc osr
|
||||
uint8_t mclk_src; // sselect mclk source
|
||||
uint8_t lrck_h; // High 4 bits of lrck
|
||||
uint8_t lrck_l; // Low 8 bits of lrck
|
||||
};
|
||||
|
||||
/* Codec hifi mclk clock divider coefficients
|
||||
* MEMBER REG
|
||||
* mclk: 0x03
|
||||
* lrck: standard
|
||||
* ss_ds: --
|
||||
* adc_div: 0x02
|
||||
* dll: 0x06
|
||||
* doubler: 0x02
|
||||
* osr: 0x07
|
||||
* mclk_src: 0x03
|
||||
* lrckh: 0x04
|
||||
* lrckl: 0x05
|
||||
*/
|
||||
static const ES7210Coefficient ES7210_COEFFICIENTS[] = {
|
||||
// mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl
|
||||
/* 8k */
|
||||
{12288000, 8000, 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00},
|
||||
{16384000, 8000, 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00},
|
||||
{19200000, 8000, 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60},
|
||||
{4096000, 8000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
|
||||
|
||||
/* 12k */
|
||||
{12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
|
||||
{19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40},
|
||||
|
||||
/* 16k */
|
||||
{4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80},
|
||||
{16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
|
||||
{12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
|
||||
/* 24k */
|
||||
{12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
{19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20},
|
||||
|
||||
/* 32k */
|
||||
{12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80},
|
||||
{16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
{19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
|
||||
/* 48k */
|
||||
{12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90},
|
||||
|
||||
/* 64k */
|
||||
{16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
|
||||
|
||||
/* 96k */
|
||||
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
|
||||
{19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8},
|
||||
};
|
||||
|
||||
static const float ES7210_MIC_GAIN_MIN = 0.0;
|
||||
static const float ES7210_MIC_GAIN_MAX = 37.5;
|
||||
|
||||
} // namespace es7210
|
||||
} // namespace esphome
|
0
esphome/components/es7243e/__init__.py
Normal file
0
esphome/components/es7243e/__init__.py
Normal file
34
esphome/components/es7243e/audio_adc.py
Normal file
34
esphome/components/es7243e/audio_adc.py
Normal 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]))
|
125
esphome/components/es7243e/es7243e.cpp
Normal file
125
esphome/components/es7243e/es7243e.cpp
Normal 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
|
37
esphome/components/es7243e/es7243e.h
Normal file
37
esphome/components/es7243e/es7243e.h
Normal 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
|
54
esphome/components/es7243e/es7243e_const.h
Normal file
54
esphome/components/es7243e/es7243e_const.h
Normal 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
|
0
esphome/components/es8156/__init__.py
Normal file
0
esphome/components/es8156/__init__.py
Normal file
27
esphome/components/es8156/audio_dac.py
Normal file
27
esphome/components/es8156/audio_dac.py
Normal file
@ -0,0 +1,27 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
from esphome.components.audio_dac import AudioDac
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
es8156_ns = cg.esphome_ns.namespace("es8156")
|
||||
ES8156 = es8156_ns.class_("ES8156", AudioDac, cg.Component, i2c.I2CDevice)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ES8156),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(i2c.i2c_device_schema(0x08))
|
||||
)
|
||||
|
||||
|
||||
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)
|
87
esphome/components/es8156/es8156.cpp
Normal file
87
esphome/components/es8156/es8156.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include "es8156.h"
|
||||
#include "es8156_const.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace es8156 {
|
||||
|
||||
static const char *const TAG = "es8156";
|
||||
|
||||
// Mark the component as failed; use only in setup
|
||||
#define ES8156_ERROR_FAILED(func) \
|
||||
if (!(func)) { \
|
||||
this->mark_failed(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
void ES8156::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ES8156...");
|
||||
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG02_SCLK_MODE, 0x04));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG20_ANALOG_SYS1, 0x2A));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG21_ANALOG_SYS2, 0x3C));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG22_ANALOG_SYS3, 0x00));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG24_ANALOG_LP, 0x07));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG23_ANALOG_SYS4, 0x00));
|
||||
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0A_TIME_CONTROL1, 0x01));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0B_TIME_CONTROL2, 0x01));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG11_DAC_SDP, 0x00));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG19_EQ_CONTROL1, 0x20));
|
||||
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0D_P2S_CONTROL, 0x14));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG09_MISC_CONTROL2, 0x00));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG18_MISC_CONTROL3, 0x00));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG08_CLOCK_ON_OFF, 0x3F));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG00_RESET, 0x02));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG00_RESET, 0x03));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG25_ANALOG_SYS5, 0x20));
|
||||
}
|
||||
|
||||
void ES8156::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ES8156 Audio Codec:");
|
||||
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGCONFIG(TAG, " Failed to initialize");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool ES8156::set_volume(float volume) {
|
||||
volume = clamp(volume, 0.0f, 1.0f);
|
||||
uint8_t reg = remap<uint8_t, float>(volume, 0.0f, 1.0f, 0, 255);
|
||||
ESP_LOGV(TAG, "Setting ES8156_REG14_VOLUME_CONTROL to %u (volume: %f)", reg, volume);
|
||||
return this->write_byte(ES8156_REG14_VOLUME_CONTROL, reg);
|
||||
}
|
||||
|
||||
float ES8156::volume() {
|
||||
uint8_t reg;
|
||||
this->read_byte(ES8156_REG14_VOLUME_CONTROL, ®);
|
||||
return remap<float, uint8_t>(reg, 0, 255, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
bool ES8156::set_mute_state_(bool mute_state) {
|
||||
uint8_t reg13;
|
||||
|
||||
this->is_muted_ = mute_state;
|
||||
|
||||
if (!this->read_byte(ES8156_REG13_DAC_MUTE, ®13)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Read ES8156_REG13_DAC_MUTE: %u", reg13);
|
||||
|
||||
if (mute_state) {
|
||||
reg13 |= BIT(1) | BIT(2);
|
||||
} else {
|
||||
reg13 &= ~(BIT(1) | BIT(2));
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Setting ES8156_REG13_DAC_MUTE to %u (muted: %s)", reg13, YESNO(mute_state));
|
||||
return this->write_byte(ES8156_REG13_DAC_MUTE, reg13);
|
||||
}
|
||||
|
||||
} // namespace es8156
|
||||
} // namespace esphome
|
51
esphome/components/es8156/es8156.h
Normal file
51
esphome/components/es8156/es8156.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/audio_dac/audio_dac.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace es8156 {
|
||||
|
||||
class ES8156 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
/////////////////////////
|
||||
// Component overrides //
|
||||
/////////////////////////
|
||||
|
||||
void setup() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void dump_config() override;
|
||||
|
||||
////////////////////////
|
||||
// AudioDac overrides //
|
||||
////////////////////////
|
||||
|
||||
/// @brief Writes the volume out to the DAC
|
||||
/// @param volume floating point between 0.0 and 1.0
|
||||
/// @return True if successful and false otherwise
|
||||
bool set_volume(float volume) override;
|
||||
|
||||
/// @brief Gets the current volume out from the DAC
|
||||
/// @return floating point between 0.0 and 1.0
|
||||
float volume() override;
|
||||
|
||||
/// @brief Disables mute for audio out
|
||||
/// @return True if successful and false otherwise
|
||||
bool set_mute_off() override { return this->set_mute_state_(false); }
|
||||
|
||||
/// @brief Enables mute for audio out
|
||||
/// @return True if successful and false otherwise
|
||||
bool set_mute_on() override { return this->set_mute_state_(true); }
|
||||
|
||||
bool is_muted() override { return this->is_muted_; }
|
||||
|
||||
protected:
|
||||
/// @brief Mutes or unmutes the DAC audio out
|
||||
/// @param mute_state True to mute, false to unmute
|
||||
/// @return True if successful and false otherwise
|
||||
bool set_mute_state_(bool mute_state);
|
||||
};
|
||||
|
||||
} // namespace es8156
|
||||
} // namespace esphome
|
68
esphome/components/es8156/es8156_const.h
Normal file
68
esphome/components/es8156/es8156_const.h
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "es8156.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace es8156 {
|
||||
|
||||
/* ES8156 register addresses */
|
||||
/*
|
||||
* RESET Control
|
||||
*/
|
||||
static const uint8_t ES8156_REG00_RESET = 0x00;
|
||||
/*
|
||||
* Clock Managerment
|
||||
*/
|
||||
static const uint8_t ES8156_REG01_MAINCLOCK_CTL = 0x01;
|
||||
static const uint8_t ES8156_REG02_SCLK_MODE = 0x02;
|
||||
static const uint8_t ES8156_REG03_LRCLK_DIV_H = 0x03;
|
||||
static const uint8_t ES8156_REG04_LRCLK_DIV_L = 0x04;
|
||||
static const uint8_t ES8156_REG05_SCLK_DIV = 0x05;
|
||||
static const uint8_t ES8156_REG06_NFS_CONFIG = 0x06;
|
||||
static const uint8_t ES8156_REG07_MISC_CONTROL1 = 0x07;
|
||||
static const uint8_t ES8156_REG08_CLOCK_ON_OFF = 0x08;
|
||||
static const uint8_t ES8156_REG09_MISC_CONTROL2 = 0x09;
|
||||
static const uint8_t ES8156_REG0A_TIME_CONTROL1 = 0x0a;
|
||||
static const uint8_t ES8156_REG0B_TIME_CONTROL2 = 0x0b;
|
||||
/*
|
||||
* System Control
|
||||
*/
|
||||
static const uint8_t ES8156_REG0C_CHIP_STATUS = 0x0c;
|
||||
static const uint8_t ES8156_REG0D_P2S_CONTROL = 0x0d;
|
||||
static const uint8_t ES8156_REG10_DAC_OSR_COUNTER = 0x10;
|
||||
/*
|
||||
* SDP Control
|
||||
*/
|
||||
static const uint8_t ES8156_REG11_DAC_SDP = 0x11;
|
||||
static const uint8_t ES8156_REG12_AUTOMUTE_SET = 0x12;
|
||||
static const uint8_t ES8156_REG13_DAC_MUTE = 0x13;
|
||||
static const uint8_t ES8156_REG14_VOLUME_CONTROL = 0x14;
|
||||
|
||||
/*
|
||||
* ALC Control
|
||||
*/
|
||||
static const uint8_t ES8156_REG15_ALC_CONFIG1 = 0x15;
|
||||
static const uint8_t ES8156_REG16_ALC_CONFIG2 = 0x16;
|
||||
static const uint8_t ES8156_REG17_ALC_CONFIG3 = 0x17;
|
||||
static const uint8_t ES8156_REG18_MISC_CONTROL3 = 0x18;
|
||||
static const uint8_t ES8156_REG19_EQ_CONTROL1 = 0x19;
|
||||
static const uint8_t ES8156_REG1A_EQ_CONTROL2 = 0x1a;
|
||||
/*
|
||||
* Analog System Control
|
||||
*/
|
||||
static const uint8_t ES8156_REG20_ANALOG_SYS1 = 0x20;
|
||||
static const uint8_t ES8156_REG21_ANALOG_SYS2 = 0x21;
|
||||
static const uint8_t ES8156_REG22_ANALOG_SYS3 = 0x22;
|
||||
static const uint8_t ES8156_REG23_ANALOG_SYS4 = 0x23;
|
||||
static const uint8_t ES8156_REG24_ANALOG_LP = 0x24;
|
||||
static const uint8_t ES8156_REG25_ANALOG_SYS5 = 0x25;
|
||||
/*
|
||||
* Chip Information
|
||||
*/
|
||||
static const uint8_t ES8156_REGFC_I2C_PAGESEL = 0xFC;
|
||||
static const uint8_t ES8156_REGFD_CHIPID1 = 0xFD;
|
||||
static const uint8_t ES8156_REGFE_CHIPID0 = 0xFE;
|
||||
static const uint8_t ES8156_REGFF_CHIP_VERSION = 0xFF;
|
||||
|
||||
} // namespace es8156
|
||||
} // namespace esphome
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
from esphome.components.audio_dac import AudioDac
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_SAMPLE_RATE
|
||||
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE
|
||||
|
||||
CODEOWNERS = ["@kroimon", "@kahrendt"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@ -10,7 +10,6 @@ DEPENDENCIES = ["i2c"]
|
||||
es8311_ns = cg.esphome_ns.namespace("es8311")
|
||||
ES8311 = es8311_ns.class_("ES8311", AudioDac, cg.Component, i2c.I2CDevice)
|
||||
|
||||
CONF_MIC_GAIN = "mic_gain"
|
||||
CONF_USE_MCLK = "use_mclk"
|
||||
CONF_USE_MICROPHONE = "use_microphone"
|
||||
|
||||
|
@ -52,7 +52,12 @@ void ESP32TouchComponent::setup() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
touch_pad_set_measurement_clock_cycles(this->meas_cycle_);
|
||||
touch_pad_set_measurement_interval(this->sleep_cycle_);
|
||||
#else
|
||||
touch_pad_set_meas_time(this->sleep_cycle_, this->meas_cycle_);
|
||||
#endif
|
||||
touch_pad_set_voltage(this->high_voltage_reference_, this->low_voltage_reference_, this->voltage_attenuation_);
|
||||
|
||||
for (auto *child : this->children_) {
|
||||
|
@ -8,11 +8,13 @@ namespace event {
|
||||
static const char *const TAG = "event";
|
||||
|
||||
void Event::trigger(const std::string &event_type) {
|
||||
if (types_.find(event_type) == types_.end()) {
|
||||
auto found = types_.find(event_type);
|
||||
if (found == types_.end()) {
|
||||
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str());
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), event_type.c_str());
|
||||
last_event_type = &(*found);
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), last_event_type->c_str());
|
||||
this->event_callback_.call(event_type);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ namespace event {
|
||||
|
||||
class Event : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
const std::string *last_event_type;
|
||||
|
||||
void trigger(const std::string &event_type);
|
||||
void set_event_types(const std::set<std::string> &event_types) { this->types_ = event_types; }
|
||||
std::set<std::string> get_event_types() const { return this->types_; }
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "esp_crt_bundle.h"
|
||||
#endif
|
||||
|
||||
#include "esp_task_wdt.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
@ -117,11 +119,11 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
container->content_length = esp_http_client_fetch_headers(client);
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
container->status_code = esp_http_client_get_status_code(client);
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
if (is_success(container->status_code)) {
|
||||
container->duration_ms = millis() - start;
|
||||
return container;
|
||||
@ -151,11 +153,11 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
container->content_length = esp_http_client_fetch_headers(client);
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
container->status_code = esp_http_client_get_status_code(client);
|
||||
App.feed_wdt();
|
||||
container->feed_wdt();
|
||||
if (is_success(container->status_code)) {
|
||||
container->duration_ms = millis() - start;
|
||||
return container;
|
||||
@ -185,8 +187,9 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
App.feed_wdt();
|
||||
this->feed_wdt();
|
||||
int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize);
|
||||
this->feed_wdt();
|
||||
this->bytes_read_ += read_len;
|
||||
|
||||
this->duration_ms += (millis() - start);
|
||||
@ -201,6 +204,13 @@ void HttpContainerIDF::end() {
|
||||
esp_http_client_cleanup(this->client_);
|
||||
}
|
||||
|
||||
void HttpContainerIDF::feed_wdt() {
|
||||
// Tests to see if the executing task has a watchdog timer attached
|
||||
if (esp_task_wdt_status(nullptr) == ESP_OK) {
|
||||
App.feed_wdt();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
||||
|
||||
|
@ -18,6 +18,9 @@ class HttpContainerIDF : public HttpContainer {
|
||||
int read(uint8_t *buf, size_t max_len) override;
|
||||
void end() override;
|
||||
|
||||
/// @brief Feeds the watchdog timer if the executing task has one attached
|
||||
void feed_wdt();
|
||||
|
||||
protected:
|
||||
esp_http_client_handle_t client_;
|
||||
};
|
||||
|
@ -9,6 +9,13 @@
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
// The update function runs in a task only on ESP32s.
|
||||
#ifdef USE_ESP32
|
||||
#define UPDATE_RETURN vTaskDelete(nullptr) // Delete the current update task
|
||||
#else
|
||||
#define UPDATE_RETURN return
|
||||
#endif
|
||||
|
||||
static const char *const TAG = "http_request.update";
|
||||
|
||||
static const size_t MAX_READ_SIZE = 256;
|
||||
@ -29,113 +36,131 @@ void HttpRequestUpdate::setup() {
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::update() {
|
||||
auto container = this->request_parent_->get(this->source_url_);
|
||||
#ifdef USE_ESP32
|
||||
xTaskCreate(HttpRequestUpdate::update_task, "update_task", 8192, (void *) this, 1, &this->update_task_handle_);
|
||||
#else
|
||||
this->update_task(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::update_task(void *params) {
|
||||
HttpRequestUpdate *this_update = (HttpRequestUpdate *) params;
|
||||
|
||||
auto container = this_update->request_parent_->get(this_update->source_url_);
|
||||
|
||||
if (container == nullptr || container->status_code != HTTP_STATUS_OK) {
|
||||
std::string msg = str_sprintf("Failed to fetch manifest from %s", this->source_url_.c_str());
|
||||
this->status_set_error(msg.c_str());
|
||||
return;
|
||||
std::string msg = str_sprintf("Failed to fetch manifest from %s", this_update->source_url_.c_str());
|
||||
this_update->status_set_error(msg.c_str());
|
||||
UPDATE_RETURN;
|
||||
}
|
||||
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
uint8_t *data = allocator.allocate(container->content_length);
|
||||
if (data == nullptr) {
|
||||
std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length);
|
||||
this->status_set_error(msg.c_str());
|
||||
this_update->status_set_error(msg.c_str());
|
||||
container->end();
|
||||
return;
|
||||
UPDATE_RETURN;
|
||||
}
|
||||
|
||||
size_t read_index = 0;
|
||||
while (container->get_bytes_read() < container->content_length) {
|
||||
int read_bytes = container->read(data + read_index, MAX_READ_SIZE);
|
||||
|
||||
App.feed_wdt();
|
||||
yield();
|
||||
|
||||
read_index += read_bytes;
|
||||
}
|
||||
|
||||
std::string response((char *) data, read_index);
|
||||
allocator.deallocate(data, container->content_length);
|
||||
bool valid = false;
|
||||
{ // Ensures the response string falls out of scope and deallocates before the task ends
|
||||
std::string response((char *) data, read_index);
|
||||
allocator.deallocate(data, container->content_length);
|
||||
|
||||
container->end();
|
||||
container->end();
|
||||
container.reset(); // Release ownership of the container's shared_ptr
|
||||
|
||||
bool valid = json::parse_json(response, [this](JsonObject root) -> bool {
|
||||
if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
this->update_info_.title = root["name"].as<std::string>();
|
||||
this->update_info_.latest_version = root["version"].as<std::string>();
|
||||
|
||||
for (auto build : root["builds"].as<JsonArray>()) {
|
||||
if (!build.containsKey("chipFamily")) {
|
||||
valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
|
||||
if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
if (build["chipFamily"] == ESPHOME_VARIANT) {
|
||||
if (!build.containsKey("ota")) {
|
||||
this_update->update_info_.title = root["name"].as<std::string>();
|
||||
this_update->update_info_.latest_version = root["version"].as<std::string>();
|
||||
|
||||
for (auto build : root["builds"].as<JsonArray>()) {
|
||||
if (!build.containsKey("chipFamily")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
auto ota = build["ota"];
|
||||
if (!ota.containsKey("path") || !ota.containsKey("md5")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
if (build["chipFamily"] == ESPHOME_VARIANT) {
|
||||
if (!build.containsKey("ota")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
auto ota = build["ota"];
|
||||
if (!ota.containsKey("path") || !ota.containsKey("md5")) {
|
||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||
return false;
|
||||
}
|
||||
this_update->update_info_.firmware_url = ota["path"].as<std::string>();
|
||||
this_update->update_info_.md5 = ota["md5"].as<std::string>();
|
||||
|
||||
if (ota.containsKey("summary"))
|
||||
this_update->update_info_.summary = ota["summary"].as<std::string>();
|
||||
if (ota.containsKey("release_url"))
|
||||
this_update->update_info_.release_url = ota["release_url"].as<std::string>();
|
||||
|
||||
return true;
|
||||
}
|
||||
this->update_info_.firmware_url = ota["path"].as<std::string>();
|
||||
this->update_info_.md5 = ota["md5"].as<std::string>();
|
||||
|
||||
if (ota.containsKey("summary"))
|
||||
this->update_info_.summary = ota["summary"].as<std::string>();
|
||||
if (ota.containsKey("release_url"))
|
||||
this->update_info_.release_url = ota["release_url"].as<std::string>();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
std::string msg = str_sprintf("Failed to parse JSON from %s", this->source_url_.c_str());
|
||||
this->status_set_error(msg.c_str());
|
||||
return;
|
||||
std::string msg = str_sprintf("Failed to parse JSON from %s", this_update->source_url_.c_str());
|
||||
this_update->status_set_error(msg.c_str());
|
||||
UPDATE_RETURN;
|
||||
}
|
||||
|
||||
// Merge source_url_ and this->update_info_.firmware_url
|
||||
if (this->update_info_.firmware_url.find("http") == std::string::npos) {
|
||||
std::string path = this->update_info_.firmware_url;
|
||||
// Merge source_url_ and this_update->update_info_.firmware_url
|
||||
if (this_update->update_info_.firmware_url.find("http") == std::string::npos) {
|
||||
std::string path = this_update->update_info_.firmware_url;
|
||||
if (path[0] == '/') {
|
||||
std::string domain = this->source_url_.substr(0, this->source_url_.find('/', 8));
|
||||
this->update_info_.firmware_url = domain + path;
|
||||
std::string domain = this_update->source_url_.substr(0, this_update->source_url_.find('/', 8));
|
||||
this_update->update_info_.firmware_url = domain + path;
|
||||
} else {
|
||||
std::string domain = this->source_url_.substr(0, this->source_url_.rfind('/') + 1);
|
||||
this->update_info_.firmware_url = domain + path;
|
||||
std::string domain = this_update->source_url_.substr(0, this_update->source_url_.rfind('/') + 1);
|
||||
this_update->update_info_.firmware_url = domain + path;
|
||||
}
|
||||
}
|
||||
|
||||
std::string current_version;
|
||||
{ // Ensures the current version string falls out of scope and deallocates before the task ends
|
||||
std::string current_version;
|
||||
#ifdef ESPHOME_PROJECT_VERSION
|
||||
current_version = ESPHOME_PROJECT_VERSION;
|
||||
current_version = ESPHOME_PROJECT_VERSION;
|
||||
#else
|
||||
current_version = ESPHOME_VERSION;
|
||||
current_version = ESPHOME_VERSION;
|
||||
#endif
|
||||
|
||||
this->update_info_.current_version = current_version;
|
||||
|
||||
if (this->update_info_.latest_version.empty() || this->update_info_.latest_version == update_info_.current_version) {
|
||||
this->state_ = update::UPDATE_STATE_NO_UPDATE;
|
||||
} else {
|
||||
this->state_ = update::UPDATE_STATE_AVAILABLE;
|
||||
this_update->update_info_.current_version = current_version;
|
||||
}
|
||||
|
||||
this->update_info_.has_progress = false;
|
||||
this->update_info_.progress = 0.0f;
|
||||
if (this_update->update_info_.latest_version.empty() ||
|
||||
this_update->update_info_.latest_version == this_update->update_info_.current_version) {
|
||||
this_update->state_ = update::UPDATE_STATE_NO_UPDATE;
|
||||
} else {
|
||||
this_update->state_ = update::UPDATE_STATE_AVAILABLE;
|
||||
}
|
||||
|
||||
this->status_clear_error();
|
||||
this->publish_state();
|
||||
this_update->update_info_.has_progress = false;
|
||||
this_update->update_info_.progress = 0.0f;
|
||||
|
||||
this_update->status_clear_error();
|
||||
this_update->publish_state();
|
||||
|
||||
UPDATE_RETURN;
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::perform(bool force) {
|
||||
|
@ -7,6 +7,10 @@
|
||||
#include "esphome/components/http_request/ota/ota_http_request.h"
|
||||
#include "esphome/components/update/update_entity.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
@ -29,6 +33,11 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent {
|
||||
HttpRequestComponent *request_parent_;
|
||||
OtaHttpRequestComponent *ota_parent_;
|
||||
std::string source_url_;
|
||||
|
||||
static void update_task(void *params);
|
||||
#ifdef USE_ESP32
|
||||
TaskHandle_t update_task_handle_{nullptr};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace http_request
|
||||
|
@ -1,9 +1,12 @@
|
||||
import logging
|
||||
|
||||
from esphome import core, pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import display, spi
|
||||
from esphome.components.display import validate_rotation
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_AUTO_CLEAR_ENABLED,
|
||||
CONF_COLOR_ORDER,
|
||||
CONF_COLOR_PALETTE,
|
||||
CONF_DC_PIN,
|
||||
@ -27,17 +30,12 @@ from esphome.const import (
|
||||
CONF_WIDTH,
|
||||
)
|
||||
from esphome.core import CORE, HexInt
|
||||
from esphome.final_validate import full_config
|
||||
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
||||
|
||||
def AUTO_LOAD():
|
||||
if CORE.is_esp32:
|
||||
return ["psram"]
|
||||
return []
|
||||
|
||||
|
||||
CODEOWNERS = ["@nielsnl68", "@clydebarrow"]
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ili9xxx_ns = cg.esphome_ns.namespace("ili9xxx")
|
||||
ILI9XXXDisplay = ili9xxx_ns.class_(
|
||||
@ -84,7 +82,7 @@ COLOR_ORDERS = {
|
||||
"BGR": ColorOrder.COLOR_ORDER_BGR,
|
||||
}
|
||||
|
||||
COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
|
||||
COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE", "8BIT", upper=True)
|
||||
|
||||
CONF_LED_PIN = "led_pin"
|
||||
CONF_COLOR_PALETTE_IMAGES = "color_palette_images"
|
||||
@ -195,9 +193,27 @@ CONFIG_SCHEMA = cv.All(
|
||||
_validate,
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema(
|
||||
"ili9xxx", require_miso=False, require_mosi=True
|
||||
)
|
||||
|
||||
def final_validate(config):
|
||||
global_config = full_config.get()
|
||||
# Ideally would calculate buffer size here, but that info is not available on the Python side
|
||||
needs_buffer = (
|
||||
CONF_LAMBDA in config or CONF_PAGES in config or config[CONF_AUTO_CLEAR_ENABLED]
|
||||
)
|
||||
if (
|
||||
CORE.is_esp32
|
||||
and config[CONF_COLOR_PALETTE] == "NONE"
|
||||
and "psram" not in global_config
|
||||
and needs_buffer
|
||||
):
|
||||
LOGGER.info("Consider enabling PSRAM if available for the display buffer")
|
||||
|
||||
return spi.final_validate_device_schema(
|
||||
"ili9xxx", require_miso=False, require_mosi=True
|
||||
)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
@ -283,6 +299,8 @@ async def to_code(config):
|
||||
palette = converted.getpalette()
|
||||
assert len(palette) == 256 * 3
|
||||
rhs = palette
|
||||
elif config[CONF_COLOR_PALETTE] == "8BIT":
|
||||
cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8))
|
||||
else:
|
||||
cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_16))
|
||||
|
||||
|
@ -66,12 +66,9 @@ void ILI9XXXDisplay::setup() {
|
||||
void ILI9XXXDisplay::alloc_buffer_() {
|
||||
if (this->buffer_color_mode_ == BITS_16) {
|
||||
this->init_internal_(this->get_buffer_length_() * 2);
|
||||
if (this->buffer_ != nullptr) {
|
||||
return;
|
||||
}
|
||||
this->buffer_color_mode_ = BITS_8;
|
||||
} else {
|
||||
this->init_internal_(this->get_buffer_length_());
|
||||
}
|
||||
this->init_internal_(this->get_buffer_length_());
|
||||
if (this->buffer_ == nullptr) {
|
||||
this->mark_failed();
|
||||
}
|
||||
|
@ -98,7 +98,8 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
||||
protected:
|
||||
inline bool check_buffer_() {
|
||||
if (this->buffer_ == nullptr) {
|
||||
this->alloc_buffer_();
|
||||
if (!this->is_failed())
|
||||
this->alloc_buffer_();
|
||||
return !this->is_failed();
|
||||
}
|
||||
return true;
|
||||
|
@ -82,11 +82,13 @@ class ImageEncoder:
|
||||
self.dither = dither
|
||||
self.index = 0
|
||||
self.invert_alpha = invert_alpha
|
||||
self.path = ""
|
||||
|
||||
def convert(self, image):
|
||||
def convert(self, image, path):
|
||||
"""
|
||||
Convert the image format
|
||||
:param image: Input image
|
||||
:param path: Path to the image file
|
||||
:return: converted image
|
||||
"""
|
||||
return image
|
||||
@ -103,6 +105,16 @@ class ImageEncoder:
|
||||
"""
|
||||
|
||||
|
||||
def is_alpha_only(image: Image):
|
||||
"""
|
||||
Check if an image (assumed to be RGBA) is only alpha
|
||||
"""
|
||||
# Any alpha data?
|
||||
if image.split()[-1].getextrema()[0] == 0xFF:
|
||||
return False
|
||||
return all(b.getextrema()[1] == 0 for b in image.split()[:-1])
|
||||
|
||||
|
||||
class ImageBinary(ImageEncoder):
|
||||
allow_config = {CONF_OPAQUE, CONF_INVERT_ALPHA, CONF_CHROMA_KEY}
|
||||
|
||||
@ -111,7 +123,9 @@ class ImageBinary(ImageEncoder):
|
||||
super().__init__(self.width8, height, transparency, dither, invert_alpha)
|
||||
self.bitno = 0
|
||||
|
||||
def convert(self, image):
|
||||
def convert(self, image, path):
|
||||
if is_alpha_only(image):
|
||||
image = image.split()[-1]
|
||||
return image.convert("1", dither=self.dither)
|
||||
|
||||
def encode(self, pixel):
|
||||
@ -136,7 +150,16 @@ class ImageBinary(ImageEncoder):
|
||||
class ImageGrayscale(ImageEncoder):
|
||||
allow_config = {CONF_ALPHA_CHANNEL, CONF_CHROMA_KEY, CONF_INVERT_ALPHA, CONF_OPAQUE}
|
||||
|
||||
def convert(self, image):
|
||||
def convert(self, image, path):
|
||||
if is_alpha_only(image):
|
||||
if self.transparency != CONF_ALPHA_CHANNEL:
|
||||
_LOGGER.warning(
|
||||
"Grayscale image %s is alpha only, but transparency is set to %s",
|
||||
path,
|
||||
self.transparency,
|
||||
)
|
||||
self.transparency = CONF_ALPHA_CHANNEL
|
||||
image = image.split()[-1]
|
||||
return image.convert("LA")
|
||||
|
||||
def encode(self, pixel):
|
||||
@ -166,7 +189,7 @@ class ImageRGB565(ImageEncoder):
|
||||
invert_alpha,
|
||||
)
|
||||
|
||||
def convert(self, image):
|
||||
def convert(self, image, path):
|
||||
return image.convert("RGBA")
|
||||
|
||||
def encode(self, pixel):
|
||||
@ -204,7 +227,7 @@ class ImageRGB(ImageEncoder):
|
||||
invert_alpha,
|
||||
)
|
||||
|
||||
def convert(self, image):
|
||||
def convert(self, image, path):
|
||||
return image.convert("RGBA")
|
||||
|
||||
def encode(self, pixel):
|
||||
@ -259,7 +282,7 @@ IMAGE_TYPE = {
|
||||
|
||||
TransparencyType = image_ns.enum("TransparencyType")
|
||||
|
||||
CONF_USE_TRANSPARENCY = "use_transparency"
|
||||
CONF_TRANSPARENCY = "transparency"
|
||||
|
||||
# If the MDI file cannot be downloaded within this time, abort.
|
||||
IMAGE_DOWNLOAD_TIMEOUT = 30 # seconds
|
||||
@ -308,7 +331,7 @@ def is_svg_file(file):
|
||||
if not file:
|
||||
return False
|
||||
with open(file, "rb") as f:
|
||||
return "<svg " in str(f.read(1024))
|
||||
return "<svg" in str(f.read(1024))
|
||||
|
||||
|
||||
def validate_cairosvg_installed():
|
||||
@ -394,7 +417,7 @@ def validate_type(image_types):
|
||||
|
||||
def validate_settings(value):
|
||||
type = value[CONF_TYPE]
|
||||
transparency = value[CONF_USE_TRANSPARENCY].lower()
|
||||
transparency = value[CONF_TRANSPARENCY].lower()
|
||||
allow_config = IMAGE_TYPE[type].allow_config
|
||||
if transparency not in allow_config:
|
||||
raise cv.Invalid(
|
||||
@ -435,9 +458,7 @@ BASE_SCHEMA = cv.Schema(
|
||||
IMAGE_SCHEMA = BASE_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_TYPE): validate_type(IMAGE_TYPE),
|
||||
cv.Optional(
|
||||
CONF_USE_TRANSPARENCY, default=CONF_OPAQUE
|
||||
): validate_transparency(),
|
||||
cv.Optional(CONF_TRANSPARENCY, default=CONF_OPAQUE): validate_transparency(),
|
||||
}
|
||||
)
|
||||
|
||||
@ -453,7 +474,7 @@ def typed_image_schema(image_type):
|
||||
BASE_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_USE_TRANSPARENCY, default=t
|
||||
CONF_TRANSPARENCY, default=t
|
||||
): validate_transparency((t,)),
|
||||
cv.Optional(CONF_TYPE, default=image_type): validate_type(
|
||||
(image_type,)
|
||||
@ -471,7 +492,7 @@ def typed_image_schema(image_type):
|
||||
BASE_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_USE_TRANSPARENCY, default=CONF_OPAQUE
|
||||
CONF_TRANSPARENCY, default=CONF_OPAQUE
|
||||
): validate_transparency(),
|
||||
cv.Optional(CONF_TYPE, default=image_type): validate_type(
|
||||
(image_type,)
|
||||
@ -533,7 +554,7 @@ async def write_image(config, all_frames=False):
|
||||
else Image.Dither.FLOYDSTEINBERG
|
||||
)
|
||||
type = config[CONF_TYPE]
|
||||
transparency = config[CONF_USE_TRANSPARENCY]
|
||||
transparency = config[CONF_TRANSPARENCY]
|
||||
invert_alpha = config[CONF_INVERT_ALPHA]
|
||||
frame_count = 1
|
||||
if all_frames:
|
||||
@ -548,7 +569,7 @@ async def write_image(config, all_frames=False):
|
||||
encoder = IMAGE_TYPE[type](width, total_rows, transparency, dither, invert_alpha)
|
||||
for frame_index in range(frame_count):
|
||||
image.seek(frame_index)
|
||||
pixels = encoder.convert(image.resize((width, height))).getdata()
|
||||
pixels = encoder.convert(image.resize((width, height)), path).getdata()
|
||||
for row in range(height):
|
||||
for col in range(width):
|
||||
encoder.encode(pixels[row * width + col])
|
||||
@ -557,7 +578,7 @@ async def write_image(config, all_frames=False):
|
||||
rhs = [HexInt(x) for x in encoder.data]
|
||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||
image_type = get_image_type_enum(type)
|
||||
trans_value = get_transparency_enum(transparency)
|
||||
trans_value = get_transparency_enum(encoder.transparency)
|
||||
|
||||
return prog_arr, width, height, image_type, trans_value, frame_count
|
||||
|
||||
|
@ -22,10 +22,27 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color
|
||||
case IMAGE_TYPE_GRAYSCALE:
|
||||
for (int img_x = 0; img_x < width_; img_x++) {
|
||||
for (int img_y = 0; img_y < height_; img_y++) {
|
||||
auto color = this->get_grayscale_pixel_(img_x, img_y);
|
||||
if (color.w >= 0x80) {
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
const uint32_t pos = (img_x + img_y * this->width_);
|
||||
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
|
||||
Color color = Color(gray, gray, gray, 0xFF);
|
||||
switch (this->transparency_) {
|
||||
case TRANSPARENCY_CHROMA_KEY:
|
||||
if (gray == 1) {
|
||||
continue; // skip drawing
|
||||
}
|
||||
break;
|
||||
case TRANSPARENCY_ALPHA_CHANNEL: {
|
||||
auto on = (float) gray / 255.0f;
|
||||
auto off = 1.0f - on;
|
||||
// blend color_on and color_off
|
||||
color = Color(color_on.r * on + color_off.r * off, color_on.g * on + color_off.g * off,
|
||||
color_on.b * on + color_off.b * off, 0xFF);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -179,8 +196,16 @@ Color Image::get_rgb565_pixel_(int x, int y) const {
|
||||
Color Image::get_grayscale_pixel_(int x, int y) const {
|
||||
const uint32_t pos = (x + y * this->width_);
|
||||
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
|
||||
uint8_t alpha = (gray == 1 && this->transparency_ == TRANSPARENCY_CHROMA_KEY) ? 0 : 0xFF;
|
||||
return Color(gray, gray, gray, alpha);
|
||||
switch (this->transparency_) {
|
||||
case TRANSPARENCY_CHROMA_KEY:
|
||||
if (gray == 1)
|
||||
return Color(0, 0, 0, 0);
|
||||
return Color(gray, gray, gray, 0xFF);
|
||||
case TRANSPARENCY_ALPHA_CHANNEL:
|
||||
return Color(0, 0, 0, gray);
|
||||
default:
|
||||
return Color(gray, gray, gray, 0xFF);
|
||||
}
|
||||
}
|
||||
int Image::get_width() const { return this->width_; }
|
||||
int Image::get_height() const { return this->height_; }
|
||||
|
@ -15,6 +15,7 @@ from .defines import (
|
||||
CONF_FREEZE,
|
||||
CONF_LVGL_ID,
|
||||
CONF_SHOW_SNOW,
|
||||
PARTS,
|
||||
literal,
|
||||
)
|
||||
from .lv_validation import lv_bool, lv_color, lv_image, opacity
|
||||
@ -33,7 +34,7 @@ from .lvcode import (
|
||||
lvgl_comp,
|
||||
static_cast,
|
||||
)
|
||||
from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA
|
||||
from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA, base_update_schema
|
||||
from .types import (
|
||||
LV_STATE,
|
||||
LvglAction,
|
||||
@ -41,6 +42,7 @@ from .types import (
|
||||
ObjUpdateAction,
|
||||
lv_disp_t,
|
||||
lv_group_t,
|
||||
lv_obj_base_t,
|
||||
lv_obj_t,
|
||||
lv_pseudo_button_t,
|
||||
)
|
||||
@ -336,3 +338,14 @@ async def widget_focus(config, action_id, template_arg, args):
|
||||
lv.group_focus_freeze(group, True)
|
||||
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"lvgl.widget.update", ObjUpdateAction, base_update_schema(lv_obj_base_t, PARTS)
|
||||
)
|
||||
async def obj_update_to_code(config, action_id, template_arg, args):
|
||||
async def do_update(widget: Widget):
|
||||
await set_obj_properties(widget, config)
|
||||
|
||||
widgets = await get_widgets(config[CONF_ID])
|
||||
return await action_to_code(widgets, do_update, action_id, template_arg, args)
|
||||
|
@ -501,9 +501,7 @@ size_t lv_millis(void) { return esphome::millis(); }
|
||||
void *lv_custom_mem_alloc(size_t size) {
|
||||
auto *ptr = malloc(size); // NOLINT
|
||||
if (ptr == nullptr) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size);
|
||||
#endif
|
||||
ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
@ -520,30 +518,22 @@ void *lv_custom_mem_alloc(size_t size) {
|
||||
ptr = heap_caps_malloc(size, cap_bits);
|
||||
}
|
||||
if (ptr == nullptr) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size);
|
||||
#endif
|
||||
ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size);
|
||||
return nullptr;
|
||||
}
|
||||
#ifdef ESPHOME_LOG_HAS_VERBOSE
|
||||
esphome::ESP_LOGV(esphome::lvgl::TAG, "allocate %zu - > %p", size, ptr);
|
||||
#endif
|
||||
ESP_LOGV(esphome::lvgl::TAG, "allocate %zu - > %p", size, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void lv_custom_mem_free(void *ptr) {
|
||||
#ifdef ESPHOME_LOG_HAS_VERBOSE
|
||||
esphome::ESP_LOGV(esphome::lvgl::TAG, "free %p", ptr);
|
||||
#endif
|
||||
ESP_LOGV(esphome::lvgl::TAG, "free %p", ptr);
|
||||
if (ptr == nullptr)
|
||||
return;
|
||||
heap_caps_free(ptr);
|
||||
}
|
||||
|
||||
void *lv_custom_mem_realloc(void *ptr, size_t size) {
|
||||
#ifdef ESPHOME_LOG_HAS_VERBOSE
|
||||
esphome::ESP_LOGV(esphome::lvgl::TAG, "realloc %p: %zu", ptr, size);
|
||||
#endif
|
||||
ESP_LOGV(esphome::lvgl::TAG, "realloc %p: %zu", ptr, size);
|
||||
return heap_caps_realloc(ptr, size, cap_bits);
|
||||
}
|
||||
#endif
|
||||
|
@ -199,13 +199,12 @@ FLAG_SCHEMA = cv.Schema({cv.Optional(flag): lvalid.lv_bool for flag in df.OBJ_FL
|
||||
FLAG_LIST = cv.ensure_list(df.LvConstant("LV_OBJ_FLAG_", *df.OBJ_FLAGS).one_of)
|
||||
|
||||
|
||||
def part_schema(widget_type: WidgetType):
|
||||
def part_schema(parts):
|
||||
"""
|
||||
Generate a schema for the various parts (e.g. main:, indicator:) of a widget type
|
||||
:param widget_type: The type of widget to generate for
|
||||
:return:
|
||||
:param parts: The parts to include in the schema
|
||||
:return: The schema
|
||||
"""
|
||||
parts = widget_type.parts
|
||||
return cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}).extend(
|
||||
STATE_SCHEMA
|
||||
)
|
||||
@ -228,9 +227,15 @@ def automation_schema(typ: LvType):
|
||||
}
|
||||
|
||||
|
||||
def create_modify_schema(widget_type):
|
||||
def base_update_schema(widget_type, parts):
|
||||
"""
|
||||
Create a schema for updating a widgets style properties, states and flags
|
||||
:param widget_type: The type of the ID
|
||||
:param parts: The allowable parts to specify
|
||||
:return:
|
||||
"""
|
||||
return (
|
||||
part_schema(widget_type)
|
||||
part_schema(parts)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.ensure_list(
|
||||
@ -245,7 +250,12 @@ def create_modify_schema(widget_type):
|
||||
}
|
||||
)
|
||||
.extend(FLAG_SCHEMA)
|
||||
.extend(widget_type.modify_schema)
|
||||
)
|
||||
|
||||
|
||||
def create_modify_schema(widget_type):
|
||||
return base_update_schema(widget_type.w_type, widget_type.parts).extend(
|
||||
widget_type.modify_schema
|
||||
)
|
||||
|
||||
|
||||
@ -256,7 +266,7 @@ def obj_schema(widget_type: WidgetType):
|
||||
:return:
|
||||
"""
|
||||
return (
|
||||
part_schema(widget_type)
|
||||
part_schema(widget_type.parts)
|
||||
.extend(FLAG_SCHEMA)
|
||||
.extend(LAYOUT_SCHEMA)
|
||||
.extend(ALIGN_TO_SCHEMA)
|
||||
@ -341,7 +351,6 @@ FLEX_OBJ_SCHEMA = {
|
||||
cv.Optional(df.CONF_FLEX_GROW): cv.int_,
|
||||
}
|
||||
|
||||
|
||||
DISP_BG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(df.CONF_DISP_BG_IMAGE): cv.Any(
|
||||
|
@ -37,7 +37,7 @@ DROPDOWN_BASE_SCHEMA = cv.Schema(
|
||||
cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int,
|
||||
cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text,
|
||||
cv.Optional(CONF_DIR, default="BOTTOM"): DIRECTIONS.one_of,
|
||||
cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec),
|
||||
cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec.parts),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -16,6 +16,11 @@ KEYBOARD_SCHEMA = {
|
||||
cv.Optional(CONF_TEXTAREA): cv.use_id(lv_textarea_t),
|
||||
}
|
||||
|
||||
KEYBOARD_MODIFY_SCHEMA = {
|
||||
cv.Optional(CONF_MODE): KEYBOARD_MODES.one_of,
|
||||
cv.Optional(CONF_TEXTAREA): cv.use_id(lv_textarea_t),
|
||||
}
|
||||
|
||||
lv_keyboard_t = LvType(
|
||||
"LvKeyboardType",
|
||||
parents=(KeyProvider, LvCompound),
|
||||
@ -32,6 +37,7 @@ class KeyboardType(WidgetType):
|
||||
lv_keyboard_t,
|
||||
(CONF_MAIN, CONF_ITEMS),
|
||||
KEYBOARD_SCHEMA,
|
||||
modify_schema=KEYBOARD_MODIFY_SCHEMA,
|
||||
)
|
||||
|
||||
def get_uses(self):
|
||||
@ -41,7 +47,8 @@ class KeyboardType(WidgetType):
|
||||
lvgl_components_required.add("KEY_LISTENER")
|
||||
lvgl_components_required.add(CONF_KEYBOARD)
|
||||
add_lv_use("btnmatrix")
|
||||
await w.set_property(CONF_MODE, await KEYBOARD_MODES.process(config[CONF_MODE]))
|
||||
if mode := config.get(CONF_MODE):
|
||||
await w.set_property(CONF_MODE, await KEYBOARD_MODES.process(mode))
|
||||
if ta := await get_widgets(config, CONF_TEXTAREA):
|
||||
await w.set_property(CONF_TEXTAREA, ta[0].obj)
|
||||
|
||||
|
@ -51,7 +51,7 @@ MSGBOX_SCHEMA = container_schema(
|
||||
cv.Required(CONF_TITLE): STYLED_TEXT_SCHEMA,
|
||||
cv.Optional(CONF_BODY, default=""): STYLED_TEXT_SCHEMA,
|
||||
cv.Optional(CONF_BUTTONS): cv.ensure_list(BUTTONMATRIX_BUTTON_SCHEMA),
|
||||
cv.Optional(CONF_BUTTON_STYLE): part_schema(buttonmatrix_spec),
|
||||
cv.Optional(CONF_BUTTON_STYLE): part_schema(buttonmatrix_spec.parts),
|
||||
cv.Optional(CONF_CLOSE_BUTTON, default=True): lv_bool,
|
||||
cv.GenerateID(CONF_BUTTON_TEXT_LIST_ID): cv.declare_id(char_ptr),
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
from esphome import automation
|
||||
|
||||
from ..automation import update_to_code
|
||||
from ..defines import CONF_MAIN, CONF_OBJ, CONF_SCROLLBAR
|
||||
from ..schemas import create_modify_schema
|
||||
from ..types import ObjUpdateAction, WidgetType, lv_obj_t
|
||||
from ..types import WidgetType, lv_obj_t
|
||||
|
||||
|
||||
class ObjType(WidgetType):
|
||||
@ -21,10 +17,3 @@ class ObjType(WidgetType):
|
||||
|
||||
|
||||
obj_spec = ObjType()
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"lvgl.widget.update", ObjUpdateAction, create_modify_schema(obj_spec)
|
||||
)
|
||||
async def obj_update_to_code(config, action_id, template_arg, args):
|
||||
return await update_to_code(config, action_id, template_arg, args)
|
||||
|
@ -38,7 +38,7 @@ TABVIEW_SCHEMA = cv.Schema(
|
||||
},
|
||||
)
|
||||
),
|
||||
cv.Optional(CONF_TAB_STYLE): part_schema(buttonmatrix_spec),
|
||||
cv.Optional(CONF_TAB_STYLE): part_schema(buttonmatrix_spec.parts),
|
||||
cv.Optional(CONF_POSITION, default="top"): DIRECTIONS.one_of,
|
||||
cv.Optional(CONF_SIZE, default="10%"): size,
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ from math import log
|
||||
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor, resistance_sampler
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_CALIBRATION,
|
||||
CONF_REFERENCE_RESISTANCE,
|
||||
@ -15,8 +15,6 @@ from esphome.const import (
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["resistance_sampler"]
|
||||
|
||||
ntc_ns = cg.esphome_ns.namespace("ntc")
|
||||
NTC = ntc_ns.class_("NTC", cg.Component, sensor.Sensor)
|
||||
|
||||
@ -126,7 +124,7 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_SENSOR): cv.use_id(resistance_sampler.ResistanceSampler),
|
||||
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
||||
cv.Required(CONF_CALIBRATION): process_calibration,
|
||||
}
|
||||
)
|
||||
|
@ -5,7 +5,7 @@ import esphome.codegen as cg
|
||||
from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent
|
||||
from esphome.components.image import (
|
||||
CONF_INVERT_ALPHA,
|
||||
CONF_USE_TRANSPARENCY,
|
||||
CONF_TRANSPARENCY,
|
||||
IMAGE_SCHEMA,
|
||||
Image_,
|
||||
get_image_type_enum,
|
||||
@ -168,7 +168,7 @@ async def to_code(config):
|
||||
|
||||
url = config[CONF_URL]
|
||||
width, height = config.get(CONF_RESIZE, (0, 0))
|
||||
transparent = get_transparency_enum(config[CONF_USE_TRANSPARENCY])
|
||||
transparent = get_transparency_enum(config[CONF_TRANSPARENCY])
|
||||
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
|
@ -100,7 +100,7 @@ class DownloadBuffer {
|
||||
void reset() { this->unread_ = 0; }
|
||||
|
||||
protected:
|
||||
ExternalRAMAllocator<uint8_t> allocator_;
|
||||
RAMAllocator<uint8_t> allocator_{};
|
||||
uint8_t *buffer_;
|
||||
size_t size_;
|
||||
/** Total number of downloaded bytes not yet read. */
|
||||
|
@ -83,8 +83,7 @@ class OnlineImage : public PollingComponent,
|
||||
protected:
|
||||
bool validate_url_(const std::string &url);
|
||||
|
||||
using Allocator = ExternalRAMAllocator<uint8_t>;
|
||||
Allocator allocator_{Allocator::Flags::ALLOW_FAILURE};
|
||||
RAMAllocator<uint8_t> allocator_{};
|
||||
|
||||
uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_); }
|
||||
int get_buffer_size_(int width, int height) const { return (this->get_bpp() * width + 7u) / 8u * height; }
|
||||
|
@ -59,6 +59,24 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) {
|
||||
this->text_sensor_row_(stream, obj, area, node, friendly_name);
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
this->number_type_(stream);
|
||||
for (auto *obj : App.get_numbers())
|
||||
this->number_row_(stream, obj, area, node, friendly_name);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
this->select_type_(stream);
|
||||
for (auto *obj : App.get_selects())
|
||||
this->select_row_(stream, obj, area, node, friendly_name);
|
||||
#endif
|
||||
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
this->media_player_type_(stream);
|
||||
for (auto *obj : App.get_media_players())
|
||||
this->media_player_row_(stream, obj, area, node, friendly_name);
|
||||
#endif
|
||||
|
||||
req->send(stream);
|
||||
}
|
||||
|
||||
@ -511,6 +529,156 @@ void PrometheusHandler::text_sensor_row_(AsyncResponseStream *stream, text_senso
|
||||
}
|
||||
#endif
|
||||
|
||||
// Type-specific implementation
|
||||
#ifdef USE_NUMBER
|
||||
void PrometheusHandler::number_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_number_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_number_failed gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::number_row_(AsyncResponseStream *stream, number::Number *obj, std::string &area,
|
||||
std::string &node, std::string &friendly_name) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
return;
|
||||
if (!std::isnan(obj->state)) {
|
||||
// We have a valid value, output this value
|
||||
stream->print(F("esphome_number_failed{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\"} 0\n"));
|
||||
// Data itself
|
||||
stream->print(F("esphome_number_value{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\"} "));
|
||||
stream->print(obj->state);
|
||||
stream->print(F("\n"));
|
||||
} else {
|
||||
// Invalid state
|
||||
stream->print(F("esphome_number_failed{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\"} 1\n"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
void PrometheusHandler::select_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_select_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_select_failed gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::select_row_(AsyncResponseStream *stream, select::Select *obj, std::string &area,
|
||||
std::string &node, std::string &friendly_name) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
return;
|
||||
if (obj->has_state()) {
|
||||
// We have a valid value, output this value
|
||||
stream->print(F("esphome_select_failed{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\"} 0\n"));
|
||||
// Data itself
|
||||
stream->print(F("esphome_select_value{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\",value=\""));
|
||||
stream->print(obj->state.c_str());
|
||||
stream->print(F("\"} "));
|
||||
stream->print(F("1.0"));
|
||||
stream->print(F("\n"));
|
||||
} else {
|
||||
// Invalid state
|
||||
stream->print(F("esphome_select_failed{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\"} 1\n"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
void PrometheusHandler::media_player_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_media_player_state_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_media_player_volume gauge\n"));
|
||||
stream->print(F("#TYPE esphome_media_player_is_muted gauge\n"));
|
||||
stream->print(F("#TYPE esphome_media_player_failed gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::media_player_row_(AsyncResponseStream *stream, media_player::MediaPlayer *obj,
|
||||
std::string &area, std::string &node, std::string &friendly_name) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
return;
|
||||
stream->print(F("esphome_media_player_failed{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\"} 0\n"));
|
||||
// Data itself
|
||||
stream->print(F("esphome_media_player_state_value{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\",value=\""));
|
||||
stream->print(media_player::media_player_state_to_string(obj->state));
|
||||
stream->print(F("\"} "));
|
||||
stream->print(F("1.0"));
|
||||
stream->print(F("\n"));
|
||||
stream->print(F("esphome_media_player_volume{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\"} "));
|
||||
stream->print(obj->volume);
|
||||
stream->print(F("\n"));
|
||||
stream->print(F("esphome_media_player_is_muted{id=\""));
|
||||
stream->print(relabel_id_(obj).c_str());
|
||||
add_area_label_(stream, area);
|
||||
add_node_label_(stream, node);
|
||||
add_friendly_name_label_(stream, friendly_name);
|
||||
stream->print(F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(F("\"} "));
|
||||
if (obj->is_muted()) {
|
||||
stream->print(F("1.0"));
|
||||
} else {
|
||||
stream->print(F("0.0"));
|
||||
}
|
||||
stream->print(F("\n"));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace prometheus
|
||||
} // namespace esphome
|
||||
#endif
|
||||
|
@ -128,6 +128,30 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
|
||||
std::string &friendly_name);
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
/// Return the type for prometheus
|
||||
void number_type_(AsyncResponseStream *stream);
|
||||
/// Return the sensor state as prometheus data point
|
||||
void number_row_(AsyncResponseStream *stream, number::Number *obj, std::string &area, std::string &node,
|
||||
std::string &friendly_name);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
/// Return the type for prometheus
|
||||
void select_type_(AsyncResponseStream *stream);
|
||||
/// Return the select state as prometheus data point
|
||||
void select_row_(AsyncResponseStream *stream, select::Select *obj, std::string &area, std::string &node,
|
||||
std::string &friendly_name);
|
||||
#endif
|
||||
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
/// Return the type for prometheus
|
||||
void media_player_type_(AsyncResponseStream *stream);
|
||||
/// Return the select state as prometheus data point
|
||||
void media_player_row_(AsyncResponseStream *stream, media_player::MediaPlayer *obj, std::string &area,
|
||||
std::string &node, std::string &friendly_name);
|
||||
#endif
|
||||
|
||||
web_server_base::WebServerBase *base_;
|
||||
bool include_internal_{false};
|
||||
std::map<EntityBase *, std::string> relabel_map_id_;
|
||||
|
@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/resistance_sampler/resistance_sampler.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace resistance {
|
||||
@ -12,7 +11,7 @@ enum ResistanceConfiguration {
|
||||
DOWNSTREAM,
|
||||
};
|
||||
|
||||
class ResistanceSensor : public Component, public sensor::Sensor, resistance_sampler::ResistanceSampler {
|
||||
class ResistanceSensor : public Component, public sensor::Sensor {
|
||||
public:
|
||||
void set_sensor(Sensor *sensor) { sensor_ = sensor; }
|
||||
void set_configuration(ResistanceConfiguration configuration) { configuration_ = configuration; }
|
||||
|
@ -1,6 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, resistance_sampler
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_REFERENCE_VOLTAGE,
|
||||
CONF_SENSOR,
|
||||
@ -9,15 +9,8 @@ from esphome.const import (
|
||||
ICON_FLASH,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["resistance_sampler"]
|
||||
|
||||
resistance_ns = cg.esphome_ns.namespace("resistance")
|
||||
ResistanceSensor = resistance_ns.class_(
|
||||
"ResistanceSensor",
|
||||
cg.Component,
|
||||
sensor.Sensor,
|
||||
resistance_sampler.ResistanceSampler,
|
||||
)
|
||||
ResistanceSensor = resistance_ns.class_("ResistanceSensor", cg.Component, sensor.Sensor)
|
||||
|
||||
CONF_CONFIGURATION = "configuration"
|
||||
CONF_RESISTOR = "resistor"
|
||||
|
@ -1,6 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
resistance_sampler_ns = cg.esphome_ns.namespace("resistance_sampler")
|
||||
ResistanceSampler = resistance_sampler_ns.class_("ResistanceSampler")
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace esphome {
|
||||
namespace resistance_sampler {
|
||||
|
||||
/// Abstract interface to mark components that provide resistance values.
|
||||
class ResistanceSampler {};
|
||||
|
||||
} // namespace resistance_sampler
|
||||
} // namespace esphome
|
25
esphome/components/seeed_mr60bha2/binary_sensor.py
Normal file
25
esphome/components/seeed_mr60bha2/binary_sensor.py
Normal 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))
|
@ -1,6 +1,7 @@
|
||||
#include "seeed_mr60bha2.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <utility>
|
||||
|
||||
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.
|
||||
void MR60BHA2Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MR60BHA2:");
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
LOG_BINARY_SENSOR(" ", "People Exist Binary Sensor", this->has_target_binary_sensor_);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "Breath Rate Sensor", this->breath_rate_sensor_);
|
||||
LOG_SENSOR(" ", "Heart Rate Sensor", this->heart_rate_sensor_);
|
||||
LOG_SENSOR(" ", "Distance Sensor", this->distance_sensor_);
|
||||
LOG_SENSOR(" ", "Target Number Sensor", this->num_targets_sensor_);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -94,7 +99,8 @@ bool MR60BHA2Component::validate_message_() {
|
||||
uint16_t frame_type = encode_uint16(data[5], data[6]);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -144,6 +150,18 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c
|
||||
}
|
||||
}
|
||||
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:
|
||||
if (this->heart_rate_sensor_ != nullptr && length >= 4) {
|
||||
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;
|
||||
case DISTANCE_TYPE_BUFFER:
|
||||
if (!data[0]) {
|
||||
if (data[0] != 0) {
|
||||
if (this->distance_sensor_ != nullptr && length >= 8) {
|
||||
uint32_t current_distance_int = encode_uint32(data[7], data[6], data[5], data[4]);
|
||||
float distance_float;
|
||||
@ -164,6 +182,12 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c
|
||||
}
|
||||
}
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
@ -12,37 +15,23 @@
|
||||
|
||||
namespace esphome {
|
||||
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 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 DISTANCE_TYPE_BUFFER = 0x0A16;
|
||||
|
||||
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,
|
||||
};
|
||||
static const uint16_t PRINT_CLOUD_BUFFER = 0x0A04;
|
||||
|
||||
class MR60BHA2Component : public Component,
|
||||
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
|
||||
SUB_SENSOR(breath_rate);
|
||||
SUB_SENSOR(heart_rate);
|
||||
SUB_SENSOR(distance);
|
||||
SUB_SENSOR(num_targets);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
@ -7,6 +7,7 @@ from esphome.const import (
|
||||
ICON_HEART_PULSE,
|
||||
ICON_PULSE,
|
||||
ICON_SIGNAL,
|
||||
ICON_COUNTER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_BEATS_PER_MINUTE,
|
||||
UNIT_CENTIMETER,
|
||||
@ -18,12 +19,13 @@ DEPENDENCIES = ["seeed_mr60bha2"]
|
||||
|
||||
CONF_BREATH_RATE = "breath_rate"
|
||||
CONF_HEART_RATE = "heart_rate"
|
||||
CONF_NUM_TARGETS = "num_targets"
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_MR60BHA2_ID): cv.use_id(MR60BHA2Component),
|
||||
cv.Optional(CONF_BREATH_RATE): sensor.sensor_schema(
|
||||
accuracy_decimals=2,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
icon=ICON_PULSE,
|
||||
),
|
||||
@ -40,6 +42,9 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
accuracy_decimals=2,
|
||||
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):
|
||||
sens = await sensor.new_sensor(distance_config)
|
||||
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))
|
||||
|
@ -7,6 +7,10 @@ namespace spi {
|
||||
|
||||
const char *const TAG = "spi";
|
||||
|
||||
SPIDelegate *const SPIDelegate::NULL_DELEGATE = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
new SPIDelegateDummy();
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=48040
|
||||
|
||||
bool SPIDelegate::is_ready() { return true; }
|
||||
|
||||
GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@ -75,6 +79,8 @@ void SPIComponent::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
void SPIDelegateDummy::begin_transaction() { ESP_LOGE(TAG, "SPIDevice not initialised - did you call spi_setup()?"); }
|
||||
|
||||
uint8_t SPIDelegateBitBash::transfer(uint8_t data) { return this->transfer_(data, 8); }
|
||||
|
||||
void SPIDelegateBitBash::write(uint16_t data, size_t num_bits) { this->transfer_(data, num_bits); }
|
||||
|
@ -163,6 +163,8 @@ class Utility {
|
||||
}
|
||||
};
|
||||
|
||||
class SPIDelegateDummy;
|
||||
|
||||
// represents a device attached to an SPI bus, with a defined clock rate, mode and bit order. On Arduino this is
|
||||
// a thin wrapper over SPIClass.
|
||||
class SPIDelegate {
|
||||
@ -248,6 +250,21 @@ class SPIDelegate {
|
||||
uint32_t data_rate_{1000000};
|
||||
SPIMode mode_{MODE0};
|
||||
GPIOPin *cs_pin_{NullPin::NULL_PIN};
|
||||
static SPIDelegate *const NULL_DELEGATE; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
};
|
||||
|
||||
/**
|
||||
* A dummy SPIDelegate that complains if it's used.
|
||||
*/
|
||||
|
||||
class SPIDelegateDummy : public SPIDelegate {
|
||||
public:
|
||||
SPIDelegateDummy() = default;
|
||||
|
||||
uint8_t transfer(uint8_t data) override { return 0; }
|
||||
void end_transaction() override{};
|
||||
|
||||
void begin_transaction() override;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -365,7 +382,7 @@ class SPIClient {
|
||||
|
||||
virtual void spi_teardown() {
|
||||
this->parent_->unregister_device(this);
|
||||
this->delegate_ = nullptr;
|
||||
this->delegate_ = SPIDelegate::NULL_DELEGATE;
|
||||
}
|
||||
|
||||
bool spi_is_ready() { return this->delegate_->is_ready(); }
|
||||
@ -376,7 +393,7 @@ class SPIClient {
|
||||
uint32_t data_rate_{1000000};
|
||||
SPIComponent *parent_{nullptr};
|
||||
GPIOPin *cs_{nullptr};
|
||||
SPIDelegate *delegate_{nullptr};
|
||||
SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,14 +1,14 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, time
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_TIME_ID,
|
||||
DEVICE_CLASS_DURATION,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_TIMER,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
UNIT_SECOND,
|
||||
ICON_TIMER,
|
||||
DEVICE_CLASS_DURATION,
|
||||
)
|
||||
|
||||
uptime_ns = cg.esphome_ns.namespace("uptime")
|
19
esphome/components/uptime/text_sensor/__init__.py
Normal file
19
esphome/components/uptime/text_sensor/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import ENTITY_CATEGORY_DIAGNOSTIC, ICON_TIMER
|
||||
|
||||
uptime_ns = cg.esphome_ns.namespace("uptime")
|
||||
UptimeTextSensor = uptime_ns.class_(
|
||||
"UptimeTextSensor", text_sensor.TextSensor, cg.PollingComponent
|
||||
)
|
||||
CONFIG_SCHEMA = text_sensor.text_sensor_schema(
|
||||
UptimeTextSensor,
|
||||
icon=ICON_TIMER,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s"))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await text_sensor.new_text_sensor(config)
|
||||
await cg.register_component(var, config)
|
63
esphome/components/uptime/text_sensor/uptime_text_sensor.cpp
Normal file
63
esphome/components/uptime/text_sensor/uptime_text_sensor.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#include "uptime_text_sensor.h"
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uptime {
|
||||
|
||||
static const char *const TAG = "uptime.sensor";
|
||||
|
||||
void UptimeTextSensor::setup() {
|
||||
this->last_ms_ = millis();
|
||||
if (this->last_ms_ < 60 * 1000)
|
||||
this->last_ms_ = 0;
|
||||
this->update();
|
||||
}
|
||||
|
||||
void UptimeTextSensor::update() {
|
||||
auto now = millis();
|
||||
// get whole seconds since last update. Note that even if the millis count has overflowed between updates,
|
||||
// the difference will still be correct due to the way twos-complement arithmetic works.
|
||||
uint32_t delta = now - this->last_ms_;
|
||||
this->last_ms_ = now - delta % 1000; // save remainder for next update
|
||||
delta /= 1000;
|
||||
this->uptime_ += delta;
|
||||
auto uptime = this->uptime_;
|
||||
unsigned interval = this->get_update_interval() / 1000;
|
||||
std::string buffer{};
|
||||
// display from the largest unit that corresponds to the update interval, drop larger units that are zero.
|
||||
while (true) { // enable use of break for early exit
|
||||
unsigned remainder = uptime % 60;
|
||||
uptime /= 60;
|
||||
if (interval < 30) {
|
||||
buffer.insert(0, str_sprintf("%us", remainder));
|
||||
if (uptime == 0)
|
||||
break;
|
||||
}
|
||||
remainder = uptime % 60;
|
||||
uptime /= 60;
|
||||
if (interval < 1800) {
|
||||
buffer.insert(0, str_sprintf("%um", remainder));
|
||||
if (uptime == 0)
|
||||
break;
|
||||
}
|
||||
remainder = uptime % 24;
|
||||
uptime /= 24;
|
||||
if (interval < 12 * 3600) {
|
||||
buffer.insert(0, str_sprintf("%uh", remainder));
|
||||
if (uptime == 0)
|
||||
break;
|
||||
}
|
||||
buffer.insert(0, str_sprintf("%ud", (unsigned) uptime));
|
||||
break;
|
||||
}
|
||||
this->publish_state(buffer);
|
||||
}
|
||||
|
||||
float UptimeTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
void UptimeTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Uptime Text Sensor", this); }
|
||||
|
||||
} // namespace uptime
|
||||
} // namespace esphome
|
25
esphome/components/uptime/text_sensor/uptime_text_sensor.h
Normal file
25
esphome/components/uptime/text_sensor/uptime_text_sensor.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uptime {
|
||||
|
||||
class UptimeTextSensor : public text_sensor::TextSensor, public PollingComponent {
|
||||
public:
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
void setup() override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
uint32_t uptime_{0}; // uptime in seconds, will overflow after 136 years
|
||||
uint32_t last_ms_{0};
|
||||
};
|
||||
|
||||
} // namespace uptime
|
||||
} // namespace esphome
|
@ -72,7 +72,8 @@ void WaveshareEPaper2P13InV3::write_buffer_(uint8_t cmd, int top, int bottom) {
|
||||
this->set_window_(top, bottom);
|
||||
this->command(cmd);
|
||||
this->start_data_();
|
||||
auto width_bytes = this->get_width_internal() / 8;
|
||||
|
||||
auto width_bytes = this->get_width_controller() / 8;
|
||||
this->write_array(this->buffer_ + top * width_bytes, (bottom - top) * width_bytes);
|
||||
this->end_data_();
|
||||
}
|
||||
@ -162,7 +163,8 @@ void WaveshareEPaper2P13InV3::display() {
|
||||
}
|
||||
}
|
||||
|
||||
int WaveshareEPaper2P13InV3::get_width_internal() { return 128; }
|
||||
int WaveshareEPaper2P13InV3::get_width_controller() { return 128; }
|
||||
int WaveshareEPaper2P13InV3::get_width_internal() { return 122; }
|
||||
|
||||
int WaveshareEPaper2P13InV3::get_height_internal() { return 250; }
|
||||
|
||||
|
@ -811,6 +811,7 @@ class WaveshareEPaper2P13InV3 : public WaveshareEPaper {
|
||||
void initialize() override;
|
||||
|
||||
protected:
|
||||
int get_width_controller() override;
|
||||
int get_width_internal() override;
|
||||
int get_height_internal() override;
|
||||
uint32_t idle_timeout_() override;
|
||||
|
@ -455,8 +455,9 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
|
||||
} else if (match.method == "toggle") {
|
||||
this->schedule_([obj]() { obj->toggle().perform(); });
|
||||
request->send(200);
|
||||
} else if (match.method == "turn_on") {
|
||||
auto call = obj->turn_on();
|
||||
} else if (match.method == "turn_on" || match.method == "turn_off") {
|
||||
auto call = match.method == "turn_on" ? obj->turn_on() : obj->turn_off();
|
||||
|
||||
if (request->hasParam("speed_level")) {
|
||||
auto speed_level = request->getParam("speed_level")->value();
|
||||
auto val = parse_number<int>(speed_level.c_str());
|
||||
@ -486,9 +487,6 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
|
||||
}
|
||||
this->schedule_([call]() mutable { call.perform(); });
|
||||
request->send(200);
|
||||
} else if (match.method == "turn_off") {
|
||||
this->schedule_([obj]() { obj->turn_off().perform(); });
|
||||
request->send(200);
|
||||
} else {
|
||||
request->send(404);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2025.1.0-dev"
|
||||
__version__ = "2025.2.0-dev"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||
@ -490,6 +490,7 @@ CONF_MEMORY_BLOCKS = "memory_blocks"
|
||||
CONF_MESSAGE = "message"
|
||||
CONF_METHANE = "methane"
|
||||
CONF_METHOD = "method"
|
||||
CONF_MIC_GAIN = "mic_gain"
|
||||
CONF_MICROPHONE = "microphone"
|
||||
CONF_MIN_BRIGHTNESS = "min_brightness"
|
||||
CONF_MIN_COOLING_OFF_TIME = "min_cooling_off_time"
|
||||
|
@ -1,21 +1,16 @@
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ARDUINO_VERSION,
|
||||
CONF_AREA,
|
||||
CONF_BOARD,
|
||||
CONF_BOARD_FLASH_MODE,
|
||||
CONF_BUILD_PATH,
|
||||
CONF_COMMENT,
|
||||
CONF_COMPILE_PROCESS_LIMIT,
|
||||
CONF_ESPHOME,
|
||||
CONF_FRAMEWORK,
|
||||
CONF_FRIENDLY_NAME,
|
||||
CONF_INCLUDES,
|
||||
CONF_LIBRARIES,
|
||||
@ -30,12 +25,9 @@ from esphome.const import (
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
CONF_PRIORITY,
|
||||
CONF_PROJECT,
|
||||
CONF_SOURCE,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE,
|
||||
CONF_VERSION,
|
||||
KEY_CORE,
|
||||
PLATFORM_ESP8266,
|
||||
TARGET_PLATFORMS,
|
||||
__version__ as ESPHOME_VERSION,
|
||||
)
|
||||
@ -44,7 +36,6 @@ from esphome.helpers import copy_file_if_changed, get_str_env, walk_files
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
BUILD_FLASH_MODES = ["qio", "qout", "dio", "dout"]
|
||||
StartupTrigger = cg.esphome_ns.class_(
|
||||
"StartupTrigger", cg.Component, automation.Trigger.template()
|
||||
)
|
||||
@ -58,8 +49,6 @@ ProjectUpdateTrigger = cg.esphome_ns.class_(
|
||||
"ProjectUpdateTrigger", cg.Component, automation.Trigger.template(cg.std_string)
|
||||
)
|
||||
|
||||
VERSION_REGEX = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$")
|
||||
|
||||
|
||||
VALID_INCLUDE_EXTS = {".h", ".hpp", ".tcc", ".ino", ".cpp", ".c"}
|
||||
|
||||
@ -111,7 +100,6 @@ else:
|
||||
_compile_process_limit_default = cv.UNDEFINED
|
||||
|
||||
|
||||
CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash"
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
@ -175,14 +163,9 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_NAME): cv.valid_name,
|
||||
cv.Optional(CONF_BUILD_PATH): cv.string,
|
||||
# Compat options, these were moved to target-platform specific sections
|
||||
# but we'll keep these around for a long time because every config would
|
||||
# be impacted
|
||||
cv.Optional(CONF_PLATFORM): cv.one_of(*TARGET_PLATFORMS, lower=True),
|
||||
cv.Optional(CONF_BOARD): cv.string_strict,
|
||||
cv.Optional(CONF_ESP8266_RESTORE_FROM_FLASH): cv.valid,
|
||||
cv.Optional(CONF_BOARD_FLASH_MODE): cv.valid,
|
||||
cv.Optional(CONF_ARDUINO_VERSION): cv.valid,
|
||||
cv.Optional(CONF_PLATFORM): cv.invalid(
|
||||
"Please remove the `platform` key from the [esphome] block and use the correct platform component. This style of configuration has now been removed."
|
||||
),
|
||||
cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All(
|
||||
cv.version_number, cv.validate_esphome_version
|
||||
),
|
||||
@ -204,62 +187,20 @@ def preload_core_config(config, result):
|
||||
conf[CONF_BUILD_PATH] = os.path.join(build_path, CORE.name)
|
||||
CORE.build_path = CORE.relative_internal_path(conf[CONF_BUILD_PATH])
|
||||
|
||||
has_oldstyle = CONF_PLATFORM in conf
|
||||
newstyle_found = [key for key in TARGET_PLATFORMS if key in config]
|
||||
oldstyle_opts = [
|
||||
CONF_ESP8266_RESTORE_FROM_FLASH,
|
||||
CONF_BOARD_FLASH_MODE,
|
||||
CONF_ARDUINO_VERSION,
|
||||
CONF_BOARD,
|
||||
]
|
||||
target_platforms = [key for key in TARGET_PLATFORMS if key in config]
|
||||
|
||||
if not has_oldstyle and not newstyle_found:
|
||||
if not target_platforms:
|
||||
raise cv.Invalid(
|
||||
"Platform missing. You must include one of the available platform keys: "
|
||||
+ ", ".join(TARGET_PLATFORMS),
|
||||
[CONF_ESPHOME],
|
||||
)
|
||||
if has_oldstyle and newstyle_found:
|
||||
if len(target_platforms) > 1:
|
||||
raise cv.Invalid(
|
||||
f"Please remove the `platform` key from the [esphome] block. You're already using the new style with the [{conf[CONF_PLATFORM]}] block",
|
||||
[CONF_ESPHOME, CONF_PLATFORM],
|
||||
f"Found multiple target platform blocks: {', '.join(target_platforms)}. Only one is allowed.",
|
||||
[target_platforms[0]],
|
||||
)
|
||||
if len(newstyle_found) > 1:
|
||||
raise cv.Invalid(
|
||||
f"Found multiple target platform blocks: {', '.join(newstyle_found)}. Only one is allowed.",
|
||||
[newstyle_found[0]],
|
||||
)
|
||||
if newstyle_found:
|
||||
# Convert to newstyle
|
||||
for key in oldstyle_opts:
|
||||
if key in conf:
|
||||
raise cv.Invalid(
|
||||
f"Please move {key} to the [{newstyle_found[0]}] block.",
|
||||
[CONF_ESPHOME, key],
|
||||
)
|
||||
|
||||
if has_oldstyle:
|
||||
plat = conf.pop(CONF_PLATFORM)
|
||||
plat_conf = {}
|
||||
if CONF_ESP8266_RESTORE_FROM_FLASH in conf:
|
||||
plat_conf["restore_from_flash"] = conf.pop(CONF_ESP8266_RESTORE_FROM_FLASH)
|
||||
if CONF_BOARD_FLASH_MODE in conf:
|
||||
plat_conf[CONF_BOARD_FLASH_MODE] = conf.pop(CONF_BOARD_FLASH_MODE)
|
||||
if CONF_ARDUINO_VERSION in conf:
|
||||
plat_conf[CONF_FRAMEWORK] = {}
|
||||
if plat != PLATFORM_ESP8266:
|
||||
plat_conf[CONF_FRAMEWORK][CONF_TYPE] = "arduino"
|
||||
|
||||
try:
|
||||
if conf[CONF_ARDUINO_VERSION] not in ("recommended", "latest", "dev"):
|
||||
cv.Version.parse(conf[CONF_ARDUINO_VERSION])
|
||||
plat_conf[CONF_FRAMEWORK][CONF_VERSION] = conf.pop(CONF_ARDUINO_VERSION)
|
||||
except ValueError:
|
||||
plat_conf[CONF_FRAMEWORK][CONF_SOURCE] = conf.pop(CONF_ARDUINO_VERSION)
|
||||
if CONF_BOARD in conf:
|
||||
plat_conf[CONF_BOARD] = conf.pop(CONF_BOARD)
|
||||
# Insert generated target platform config to main config
|
||||
config[plat] = plat_conf
|
||||
config[CONF_ESPHOME] = conf
|
||||
|
||||
|
||||
|
@ -120,7 +120,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(4, 4, 2)
|
||||
#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(5, 1, 5)
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP32_VARIANT_ESP32S2)
|
||||
|
@ -74,7 +74,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
#define esph_log_vv(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_VERY_VERBOSE, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_VERY_VERBOSE, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
#else
|
||||
@ -83,7 +83,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
#define esph_log_v(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_VERBOSE, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_VERBOSE, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_VERBOSE
|
||||
#else
|
||||
@ -92,9 +92,9 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||
#define esph_log_d(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_DEBUG, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_DEBUG, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
#define esph_log_config(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_CONFIG, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_CONFIG, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_DEBUG
|
||||
#define ESPHOME_LOG_HAS_CONFIG
|
||||
@ -105,7 +105,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_INFO
|
||||
#define esph_log_i(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_INFO
|
||||
#else
|
||||
@ -114,7 +114,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_WARN
|
||||
#define esph_log_w(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_WARN, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_WARN, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_WARN
|
||||
#else
|
||||
@ -123,7 +123,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
#define esph_log_e(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_ERROR, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_ERROR, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_ERROR
|
||||
#else
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user